webview_scaffold.dart 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import 'dart:async';
  2. import 'package:flutter/foundation.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/rendering.dart';
  5. import 'base.dart';
  6. class WebviewScaffold extends StatefulWidget {
  7. const WebviewScaffold({
  8. Key key,
  9. this.appBar,
  10. @required this.url,
  11. this.headers,
  12. this.withJavascript,
  13. this.clearCache,
  14. this.clearCookies,
  15. this.enableAppScheme,
  16. this.userAgent,
  17. this.primary = true,
  18. this.persistentFooterButtons,
  19. this.bottomNavigationBar,
  20. this.withZoom,
  21. this.withLocalStorage,
  22. this.withLocalUrl,
  23. this.scrollBar,
  24. this.supportMultipleWindows,
  25. this.appCacheEnabled,
  26. this.hidden = false,
  27. this.initialChild,
  28. this.allowFileURLs,
  29. this.resizeToAvoidBottomInset = false,
  30. this.invalidUrlRegex,
  31. this.geolocationEnabled
  32. }) : super(key: key);
  33. final PreferredSizeWidget appBar;
  34. final String url;
  35. final Map<String, String> headers;
  36. final bool withJavascript;
  37. final bool clearCache;
  38. final bool clearCookies;
  39. final bool enableAppScheme;
  40. final String userAgent;
  41. final bool primary;
  42. final List<Widget> persistentFooterButtons;
  43. final Widget bottomNavigationBar;
  44. final bool withZoom;
  45. final bool withLocalStorage;
  46. final bool withLocalUrl;
  47. final bool scrollBar;
  48. final bool supportMultipleWindows;
  49. final bool appCacheEnabled;
  50. final bool hidden;
  51. final Widget initialChild;
  52. final bool allowFileURLs;
  53. final bool resizeToAvoidBottomInset;
  54. final String invalidUrlRegex;
  55. final bool geolocationEnabled;
  56. @override
  57. _WebviewScaffoldState createState() => _WebviewScaffoldState();
  58. }
  59. class _WebviewScaffoldState extends State<WebviewScaffold> {
  60. final webviewReference = FlutterWebviewPlugin();
  61. Rect _rect;
  62. Timer _resizeTimer;
  63. StreamSubscription<WebViewStateChanged> _onStateChanged;
  64. var _onBack;
  65. @override
  66. void initState() {
  67. super.initState();
  68. webviewReference.close();
  69. _onBack = webviewReference.onBack.listen((_) async {
  70. if (!mounted) return;
  71. if (await Navigator.maybePop(context)) {
  72. webviewReference.close();
  73. }
  74. });
  75. if (widget.hidden) {
  76. _onStateChanged =
  77. webviewReference.onStateChanged.listen((WebViewStateChanged state) {
  78. if (state.type == WebViewState.finishLoad) {
  79. webviewReference.show();
  80. }
  81. });
  82. }
  83. }
  84. @override
  85. void dispose() {
  86. super.dispose();
  87. _onBack?.cancel();
  88. _resizeTimer?.cancel();
  89. webviewReference.close();
  90. if (widget.hidden) {
  91. _onStateChanged.cancel();
  92. }
  93. webviewReference.dispose();
  94. }
  95. @override
  96. Widget build(BuildContext context) {
  97. return Scaffold(
  98. appBar: widget.appBar,
  99. resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
  100. persistentFooterButtons: widget.persistentFooterButtons,
  101. bottomNavigationBar: widget.bottomNavigationBar,
  102. body: _WebviewPlaceholder(
  103. onRectChanged: (Rect value) {
  104. if (_rect == null) {
  105. _rect = value;
  106. webviewReference.launch(
  107. widget.url,
  108. headers: widget.headers,
  109. withJavascript: widget.withJavascript,
  110. clearCache: widget.clearCache,
  111. clearCookies: widget.clearCookies,
  112. hidden: widget.hidden,
  113. enableAppScheme: widget.enableAppScheme,
  114. userAgent: widget.userAgent,
  115. rect: _rect,
  116. withZoom: widget.withZoom,
  117. withLocalStorage: widget.withLocalStorage,
  118. withLocalUrl: widget.withLocalUrl,
  119. scrollBar: widget.scrollBar,
  120. supportMultipleWindows: widget.supportMultipleWindows,
  121. appCacheEnabled: widget.appCacheEnabled,
  122. allowFileURLs: widget.allowFileURLs,
  123. invalidUrlRegex: widget.invalidUrlRegex,
  124. geolocationEnabled: widget.geolocationEnabled
  125. );
  126. } else {
  127. if (_rect != value) {
  128. _rect = value;
  129. _resizeTimer?.cancel();
  130. _resizeTimer = Timer(const Duration(milliseconds: 250), () {
  131. // avoid resizing to fast when build is called multiple time
  132. webviewReference.resize(_rect);
  133. });
  134. }
  135. }
  136. },
  137. child: widget.initialChild ?? const Center(child: const CircularProgressIndicator()),
  138. ),
  139. );
  140. }
  141. }
  142. class _WebviewPlaceholder extends SingleChildRenderObjectWidget {
  143. const _WebviewPlaceholder({
  144. Key key,
  145. @required this.onRectChanged,
  146. Widget child,
  147. }) : super(key: key, child: child);
  148. final ValueChanged<Rect> onRectChanged;
  149. @override
  150. RenderObject createRenderObject(BuildContext context) {
  151. return _WebviewPlaceholderRender(
  152. onRectChanged: onRectChanged,
  153. );
  154. }
  155. @override
  156. void updateRenderObject(BuildContext context, _WebviewPlaceholderRender renderObject) {
  157. renderObject..onRectChanged = onRectChanged;
  158. }
  159. }
  160. class _WebviewPlaceholderRender extends RenderProxyBox {
  161. _WebviewPlaceholderRender({
  162. RenderBox child,
  163. ValueChanged<Rect> onRectChanged,
  164. }) : _callback = onRectChanged,
  165. super(child);
  166. ValueChanged<Rect> _callback;
  167. Rect _rect;
  168. Rect get rect => _rect;
  169. set onRectChanged(ValueChanged<Rect> callback) {
  170. if (callback != _callback) {
  171. _callback = callback;
  172. notifyRect();
  173. }
  174. }
  175. void notifyRect() {
  176. if (_callback != null && _rect != null) {
  177. _callback(_rect);
  178. }
  179. }
  180. @override
  181. void paint(PaintingContext context, Offset offset) {
  182. super.paint(context, offset);
  183. final rect = offset & size;
  184. if (_rect != rect) {
  185. _rect = rect;
  186. notifyRect();
  187. }
  188. }
  189. }