flutter_webview_plugin.dart 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import 'dart:async';
  2. import 'package:flutter/services.dart';
  3. import 'package:flutter/material.dart';
  4. const _kChannel = 'flutter_webview_plugin';
  5. const _kEvent = 'flutter_webview_plugin_event';
  6. // TODO: more genral state for iOS/android
  7. enum WebViewState { startLoad, finishLoad }
  8. // copy from UIWebView.h
  9. enum _WebViewNavigateType {
  10. TypeLinkClicked,
  11. TypeFormSubmitted,
  12. TypeBackForward,
  13. TypeReload,
  14. TypeFormResubmitted,
  15. TypeOther
  16. }
  17. /// Singleton Class that communicate with a fullscreen Webview Instance
  18. /// Have to be instanciate after `runApp` called.
  19. class FlutterWebViewPlugin {
  20. final MethodChannel _channel;
  21. /// iOS WebView: Implemented
  22. /// Android WebView: not implemented
  23. final EventChannel _event;
  24. Stream<String> _stateChanged;
  25. Stream<String> get stateChanged {
  26. assert(_WebViewNavigateType.TypeLinkClicked.index == 0);
  27. assert(_WebViewNavigateType.TypeOther.index == 5);
  28. if (_stateChanged == null) {
  29. _stateChanged = _event.receiveBroadcastStream();
  30. _stateChanged.listen((var result) {
  31. // the list like: [state, url, navtype]
  32. if (result is List && result.length == 3) {
  33. if (_WebViewNavigateType.TypeBackForward.index == result[2]) {
  34. _onBackPressed.add(Null);
  35. } else if (_WebViewNavigateType.TypeOther.index == result[2] ||
  36. _WebViewNavigateType.TypeLinkClicked.index == result[2] ||
  37. _WebViewNavigateType.TypeFormSubmitted.index == result[2]) {
  38. // TODO: find out better way
  39. _onUrlChanged.add(result[1]);
  40. }
  41. } else if (result is String) {
  42. if (result == "destroy") {
  43. _onDestroy.add(Null);
  44. }
  45. }
  46. });
  47. }
  48. return _stateChanged;
  49. }
  50. final StreamController<Null> _onDestroy = new StreamController.broadcast();
  51. final StreamController<Null> _onBackPressed =
  52. new StreamController.broadcast();
  53. final StreamController<String> _onUrlChanged =
  54. new StreamController.broadcast();
  55. FlutterWebViewPlugin()
  56. : _channel = const MethodChannel(_kChannel),
  57. _event = const EventChannel(_kEvent) {
  58. _channel.setMethodCallHandler(_handleMessages);
  59. }
  60. Future<Null> _handleMessages(MethodCall call) async {
  61. switch (call.method) {
  62. case "onDestroy":
  63. _onDestroy.add(null);
  64. break;
  65. case "onBackPressed":
  66. _onBackPressed.add(null);
  67. break;
  68. case "onUrlChanged":
  69. _onUrlChanged.add(call.arguments["url"]);
  70. break;
  71. }
  72. }
  73. //////////////////////
  74. /// Listening the OnDestroy LifeCycle Event for Android
  75. ///
  76. Stream<Null> get onDestroy => _onDestroy.stream;
  77. /// Listening the onBackPressed Event for Android
  78. ///
  79. Stream<Null> get onBackPressed => _onBackPressed.stream;
  80. /// Start the Webview with [url]
  81. /// - [withJavascript] enable Javascript or not for the Webview
  82. /// iOS WebView: Not implemented yet
  83. /// android: Implemented.
  84. /// - [clearCache] clear the cache of the Webview
  85. /// iOS WebView: Not implemented yet
  86. /// iOS WkWebView: TODO: later
  87. /// android: Implemented
  88. /// - [clearCookies] clear all cookies of the Webview
  89. /// iOS WebView: Not implemented yet
  90. /// iOS WkWebView: will implement later
  91. /// android: Implemented
  92. /// - [hidden] not show
  93. /// iOS WebView: not shown(addSubView) in ViewController
  94. /// android: Not implemented yet.
  95. /// [fullScreen]: show in full screen mode, default true
  96. /// iOS WebView: without rect, show in full screen mode
  97. /// android: Implemented
  98. /// [rect]: show in rect(not full screen)
  99. /// iOS WebView: worked
  100. /// android: Not implemented yet
  101. /// [enableAppScheme]: false will enable all schemes, true only for httt/https/about
  102. /// iOS WebView: worked
  103. /// android: Not implemented yet
  104. /// [userAgent]: set the User-Agent of WebView
  105. /// iOS WebView: worked
  106. /// android: Not implemented yet
  107. Future<Null> launch(String url,
  108. {bool withJavascript: true,
  109. bool clearCache: false,
  110. bool clearCookies: false,
  111. bool hidden: false,
  112. bool fullScreen: true,
  113. bool enableAppScheme: true,
  114. Rect rect: null,
  115. String userAgent: null}) async {
  116. Map<String, dynamic> args = {
  117. "url": url,
  118. "withJavascript": withJavascript,
  119. "clearCache": clearCache,
  120. "hidden": hidden,
  121. "clearCookies": clearCookies,
  122. "fullScreen": fullScreen,
  123. "enableAppScheme": enableAppScheme,
  124. "userAgent": userAgent
  125. };
  126. if (!fullScreen) assert(rect != null);
  127. if (rect != null) {
  128. args["rect"] = {
  129. "left": rect.left,
  130. "right": rect.right,
  131. "width": rect.width,
  132. "height": rect.height
  133. };
  134. }
  135. await _channel.invokeMethod('launch', args);
  136. }
  137. /// iOS WebView: worked
  138. /// android: Not implemented yet
  139. Future<String> evalJavascript(String code) {
  140. return _channel.invokeMethod('eval', {"code": code});
  141. }
  142. /// Close the Webview
  143. /// Will trigger the [onDestroy] event
  144. Future<Null> close() => _channel.invokeMethod("close");
  145. /// Listening url changed
  146. /// iOS WebView: worked
  147. /// android: worked
  148. Stream<String> get onUrlChanged => _onUrlChanged.stream;
  149. }