webview_scaffold.dart 4.9 KB

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