-
-
Save herveGuigoz/09f5560a213f4b9b445c421307fe8a75 to your computer and use it in GitHub Desktop.
HTML Widgets for Flutter Web (IFrame and TextArea)- by Simon Lightfoot - 21/04/2020
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MIT License | |
// | |
// Copyright (c) 2020 Simon Lightfoot | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to deal | |
// in the Software without restriction, including without limitation the rights | |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in all | |
// copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
// SOFTWARE. | |
// | |
import 'dart:html' as html; | |
import 'dart:ui' as ui; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/gestures.dart' show OneSequenceGestureRecognizer; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/rendering.dart'; | |
import 'package:flutter/services.dart' | |
show PlatformViewController, SystemChannels; | |
final _widgetRef = <int, HtmlElementWidgetState>{}; | |
abstract class HtmlElementWidget extends StatefulWidget { | |
const HtmlElementWidget({ | |
Key key, | |
}) : super(key: key); | |
html.HtmlElement createHtmlElement(BuildContext context) { | |
throw UnimplementedError('createHtmlElement implementation required'); | |
} | |
@override | |
HtmlElementWidgetState createState() => HtmlElementWidgetState(); | |
} | |
class HtmlElementWidgetState<T extends HtmlElementWidget> extends State<T> { | |
static bool _registered = false; | |
@override | |
void initState() { | |
super.initState(); | |
if (!_registered) { | |
registerPlatformView(); | |
_registered = true; | |
} | |
} | |
html.HtmlElement createHtmlElement(BuildContext context) { | |
return widget.createHtmlElement(context); | |
} | |
void registerPlatformView() { | |
if (kIsWeb) { | |
// ignore: undefined_prefixed_name | |
ui.platformViewRegistry.registerViewFactory( | |
'HtmlElementWidget', | |
(int viewId) { | |
final state = _widgetRef[viewId]; | |
assert(state != null); | |
return state.createHtmlElement(context); | |
}, | |
); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
return PlatformViewLink( | |
viewType: 'HtmlElementWidget', | |
onCreatePlatformView: (PlatformViewCreationParams params) { | |
_widgetRef[params.id] = this; | |
final controller = _HtmlElementViewController(params); | |
controller._initialize().then((_) { | |
params.onPlatformViewCreated(params.id); | |
}); | |
return controller; | |
}, | |
surfaceFactory: (_, PlatformViewController controller) { | |
return PlatformViewSurface( | |
controller: controller, | |
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{}, | |
hitTestBehavior: PlatformViewHitTestBehavior.opaque, | |
); | |
}, | |
); | |
} | |
} | |
class _HtmlElementViewController extends PlatformViewController { | |
_HtmlElementViewController(this.params); | |
final PlatformViewCreationParams params; | |
@override | |
int get viewId => params.id; | |
bool _initialized = false; | |
Future<void> _initialize() async { | |
await SystemChannels.platform_views.invokeMethod<void>( | |
'create', | |
<String, dynamic>{'id': viewId, 'viewType': params.viewType}, | |
); | |
_initialized = true; | |
} | |
@override | |
Future<void> clearFocus() async { | |
// Currently this does nothing on Flutter Web. | |
// TODO(het): Implement this. See https://github.com/flutter/flutter/issues/39496 | |
} | |
@override | |
Future<void> dispatchPointerEvent(PointerEvent event) async { | |
// We do not dispatch pointer events to HTML views because they may contain | |
// cross-origin iframes, which only accept user-generated events. | |
} | |
@override | |
Future<void> dispose() async { | |
if (_initialized) { | |
// Asynchronously dispose this view. | |
SystemChannels.platform_views.invokeMethod<void>('dispose', viewId); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MIT License | |
// | |
// Copyright (c) 2020 Simon Lightfoot | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to deal | |
// in the Software without restriction, including without limitation the rights | |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in all | |
// copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
// SOFTWARE. | |
// | |
import 'dart:html'; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
import 'html_element_widget.dart'; | |
class IFrameWidget extends HtmlElementWidget { | |
const IFrameWidget({ | |
Key key, | |
this.width, | |
this.height, | |
this.src, | |
this.style, | |
this.allow, | |
this.allowFullscreen, | |
}) : super(key: key); | |
final int width; | |
final int height; | |
final String src; | |
final String style; | |
final String allow; | |
final bool allowFullscreen; | |
@override | |
HtmlElement createHtmlElement(BuildContext context) { | |
return IFrameElement() | |
..width = width.toString() | |
..height = height.toString() | |
..src = src | |
..style.cssText = style | |
..allow = allow | |
..allowFullscreen = allowFullscreen; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MIT License | |
// | |
// Copyright (c) 2020 Simon Lightfoot | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to deal | |
// in the Software without restriction, including without limitation the rights | |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in all | |
// copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
// SOFTWARE. | |
// | |
import 'package:flutter/gestures.dart' show PointerSignalEvent, PointerScrollEvent; | |
import 'dart:async'; | |
import 'dart:html' as html; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
import 'html_element_widget.dart'; | |
class TextAreaWidget extends HtmlElementWidget { | |
const TextAreaWidget({ | |
Key key, | |
this.initialValue = '', | |
this.wrap = 'soft', | |
this.style = 'border: 0; outline: none; resize: none;', | |
this.autofocus, | |
this.onChanged, | |
}) : super(key: key); | |
final String initialValue; | |
final String wrap; | |
final String style; | |
final bool autofocus; | |
final ValueChanged<String> onChanged; | |
@override | |
HtmlElementWidgetState<HtmlElementWidget> createState() { | |
return TextAreaWidgetState(); | |
} | |
} | |
class TextAreaWidgetState extends HtmlElementWidgetState<TextAreaWidget> { | |
html.TextAreaElement _element; | |
StreamSubscription<html.Event> _onInputSub; | |
@override | |
html.HtmlElement createHtmlElement(BuildContext context) => _element; | |
String get text => _element?.text; | |
set text(String text) => _element?.text = text; | |
@override | |
void initState() { | |
super.initState(); | |
_element = html.TextAreaElement() | |
..wrap = widget.wrap | |
..value = widget.initialValue | |
..autofocus = widget.autofocus | |
..style.cssText = widget.style; | |
_onInputSub = _element.onInput.listen(_onInput); | |
} | |
@override | |
void didUpdateWidget(TextAreaWidget oldWidget) { | |
super.didUpdateWidget(oldWidget); | |
_element | |
..wrap = widget.wrap | |
..style.cssText = widget.style; | |
} | |
void _onInput(html.Event event) => widget.onChanged?.call(_element.value); | |
void _onPointerSignal(PointerSignalEvent event) { | |
if(event is PointerScrollEvent) { | |
final delta = event.scrollDelta; | |
_element.scrollBy(delta.dx, delta.dy); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Listener( | |
onPointerSignal: _onPointerSignal, | |
child: super.build(context), | |
); | |
} | |
@override | |
void dispose() { | |
_onInputSub.cancel(); | |
_element = null; | |
super.dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment