webview_scaffold.dart 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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.invalidUrlRegex,
  30. }) : super(key: key);
  31. final PreferredSizeWidget appBar;
  32. final String url;
  33. final Map<String, String> headers;
  34. final bool withJavascript;
  35. final bool clearCache;
  36. final bool clearCookies;
  37. final bool enableAppScheme;
  38. final String userAgent;
  39. final bool primary;
  40. final List<Widget> persistentFooterButtons;
  41. final Widget bottomNavigationBar;
  42. final bool withZoom;
  43. final bool withLocalStorage;
  44. final bool withLocalUrl;
  45. final bool scrollBar;
  46. final bool supportMultipleWindows;
  47. final bool appCacheEnabled;
  48. final bool hidden;
  49. final Widget initialChild;
  50. final bool allowFileURLs;
  51. final String invalidUrlRegex;
  52. @override
  53. _WebviewScaffoldState createState() => _WebviewScaffoldState();
  54. }
  55. class _WebviewScaffoldState extends State<WebviewScaffold> {
  56. final webviewReference = FlutterWebviewPlugin();
  57. Rect _rect;
  58. Timer _resizeTimer;
  59. StreamSubscription<WebViewStateChanged> _onStateChanged;
  60. @override
  61. void initState() {
  62. super.initState();
  63. webviewReference.close();
  64. if (widget.hidden) {
  65. _onStateChanged = webviewReference.onStateChanged.listen((WebViewStateChanged state) {
  66. if (state.type == WebViewState.finishLoad) {
  67. webviewReference.show();
  68. }
  69. });
  70. }
  71. }
  72. @override
  73. void dispose() {
  74. super.dispose();
  75. _resizeTimer?.cancel();
  76. webviewReference.close();
  77. if (widget.hidden) {
  78. _onStateChanged.cancel();
  79. }
  80. webviewReference.dispose();
  81. }
  82. @override
  83. Widget build(BuildContext context) {
  84. return Scaffold(
  85. appBar: widget.appBar,
  86. persistentFooterButtons: widget.persistentFooterButtons,
  87. bottomNavigationBar: widget.bottomNavigationBar,
  88. body: _WebviewPlaceholder(
  89. onRectChanged: (Rect value) {
  90. if (_rect == null) {
  91. _rect = value;
  92. webviewReference.launch(
  93. widget.url,
  94. headers: widget.headers,
  95. withJavascript: widget.withJavascript,
  96. clearCache: widget.clearCache,
  97. clearCookies: widget.clearCookies,
  98. hidden: widget.hidden,
  99. enableAppScheme: widget.enableAppScheme,
  100. userAgent: widget.userAgent,
  101. rect: _rect,
  102. withZoom: widget.withZoom,
  103. withLocalStorage: widget.withLocalStorage,
  104. withLocalUrl: widget.withLocalUrl,
  105. scrollBar: widget.scrollBar,
  106. supportMultipleWindows: widget.supportMultipleWindows,
  107. appCacheEnabled: widget.appCacheEnabled,
  108. allowFileURLs: widget.allowFileURLs,
  109. invalidUrlRegex: widget.invalidUrlRegex,
  110. );
  111. } else {
  112. if (_rect != value) {
  113. _rect = value;
  114. _resizeTimer?.cancel();
  115. _resizeTimer = Timer(const Duration(milliseconds: 250), () {
  116. // avoid resizing to fast when build is called multiple time
  117. webviewReference.resize(_rect);
  118. });
  119. }
  120. }
  121. },
  122. child: widget.initialChild ?? const Center(child: const CircularProgressIndicator()),
  123. ),
  124. );
  125. }
  126. }
  127. class _WebviewPlaceholder extends SingleChildRenderObjectWidget {
  128. const _WebviewPlaceholder({
  129. Key key,
  130. @required this.onRectChanged,
  131. Widget child,
  132. }) : super(key: key, child: child);
  133. final ValueChanged<Rect> onRectChanged;
  134. @override
  135. RenderObject createRenderObject(BuildContext context) {
  136. return _WebviewPlaceholderRender(
  137. onRectChanged: onRectChanged,
  138. );
  139. }
  140. @override
  141. void updateRenderObject(BuildContext context, _WebviewPlaceholderRender renderObject) {
  142. renderObject..onRectChanged = onRectChanged;
  143. }
  144. }
  145. class _WebviewPlaceholderRender extends RenderProxyBox {
  146. _WebviewPlaceholderRender({
  147. RenderBox child,
  148. ValueChanged<Rect> onRectChanged,
  149. }) : _callback = onRectChanged,
  150. super(child);
  151. ValueChanged<Rect> _callback;
  152. Rect _rect;
  153. Rect get rect => _rect;
  154. set onRectChanged(ValueChanged<Rect> callback) {
  155. if (callback != _callback) {
  156. _callback = callback;
  157. notifyRect();
  158. }
  159. }
  160. void notifyRect() {
  161. if (_callback != null && _rect != null) {
  162. _callback(_rect);
  163. }
  164. }
  165. @override
  166. void paint(PaintingContext context, Offset offset) {
  167. super.paint(context, offset);
  168. final rect = offset & size;
  169. if (_rect != rect) {
  170. _rect = rect;
  171. notifyRect();
  172. }
  173. }
  174. }