flutter_webview_plugin.dart 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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. final EventChannel _event;
  22. Stream<String> _stateChanged;
  23. Stream<String> get stateChanged {
  24. assert(_WebViewNavigateType.TypeLinkClicked.index == 0);
  25. assert(_WebViewNavigateType.TypeOther.index == 5);
  26. if (_stateChanged == null) {
  27. _stateChanged = _event.receiveBroadcastStream();
  28. _stateChanged.listen((var result) {
  29. // the list like: [state, url, navtype]
  30. if (result is List && result.length == 3) {
  31. if (_WebViewNavigateType.TypeBackForward.index == result[2]) {
  32. _onBackPressed.add(Null);
  33. } else if (_WebViewNavigateType.TypeOther.index == result[2] ||
  34. _WebViewNavigateType.TypeLinkClicked.index == result[2] ||
  35. _WebViewNavigateType.TypeFormSubmitted.index == result[2]) {
  36. // TODO: find out better way
  37. _onUrlChanged.add(result[1]);
  38. }
  39. } else if (result is String) {
  40. if (result == "destroy") {
  41. _onDestroy.add(Null);
  42. }
  43. }
  44. });
  45. }
  46. return _stateChanged;
  47. }
  48. final StreamController<Null> _onDestroy = new StreamController.broadcast();
  49. final StreamController<Null> _onBackPressed =
  50. new StreamController.broadcast();
  51. final StreamController<String> _onUrlChanged =
  52. new StreamController.broadcast();
  53. FlutterWebViewPlugin()
  54. : _channel = const MethodChannel(_kChannel),
  55. _event = const EventChannel(_kEvent) {
  56. _channel.setMethodCallHandler(_handleMessages);
  57. }
  58. Future<Null> _handleMessages(MethodCall call) async {
  59. switch (call.method) {
  60. case "onDestroy":
  61. _onDestroy.add(null);
  62. break;
  63. case "onBackPressed":
  64. _onBackPressed.add(null);
  65. break;
  66. case "onUrlChanged":
  67. _onUrlChanged.add(call.arguments["url"]);
  68. break;
  69. }
  70. }
  71. //////////////////////
  72. /// Listening the OnDestroy LifeCycle Event for Android
  73. ///
  74. Stream<Null> get onDestroy => _onDestroy.stream;
  75. /// Listening the onBackPressed Event for Android
  76. ///
  77. Stream<Null> get onBackPressed => _onBackPressed.stream;
  78. /// Start the Webview with [url]
  79. /// - [withJavascript] enable Javascript or not for the Webview
  80. /// iOS WebView: Not implemented yet
  81. /// android: Implemented.
  82. /// - [clearCache] clear the cache of the Webview
  83. /// iOS WebView: Not implemented yet
  84. /// iOS WKWebView: will implement later
  85. /// android: Implemented
  86. /// - [clearCookies] clear all cookies of the Webview
  87. /// iOS WebView: Not implemented yet
  88. /// iOS WKWebView: will implement later
  89. /// android: Implemented
  90. /// - [hidden] not show
  91. /// iOS WebView: not shown(addSubView) in ViewController
  92. /// android: Implemented
  93. /// [fullScreen]: show in full screen mode, default true
  94. /// iOS WebView: without rect, show in full screen mode
  95. /// android: Not implemented yet
  96. /// [rect]: show in rect(not full screen)
  97. /// iOS WebView: worked
  98. /// android: Not implemented yet
  99. /// [enableAppScheme]: false will enable all schemes, true only for
  100. /// httt/https/about
  101. Future<Null> launch(String url,
  102. {bool withJavascript: true,
  103. bool clearCache: false,
  104. bool clearCookies: false,
  105. bool hidden: false,
  106. bool fullScreen: true,
  107. bool enableAppScheme: true,
  108. Rect rect: null}) async {
  109. Map<String, dynamic> args = {
  110. "url": url,
  111. "withJavascript": withJavascript,
  112. "clearCache": clearCache,
  113. "hidden": hidden,
  114. "clearCookies": clearCookies,
  115. "fullScreen": fullScreen,
  116. "enableAppScheme": enableAppScheme
  117. };
  118. if (!fullScreen) assert(rect != null);
  119. if (rect != null) {
  120. args["rect"] = {
  121. "left": rect.left,
  122. "right": rect.right,
  123. "width": rect.width,
  124. "height": rect.height
  125. };
  126. }
  127. await _channel.invokeMethod('launch', args);
  128. }
  129. Future<String> evalJavascript(String code) {
  130. return _channel.invokeMethod('eval', {"code": code});
  131. }
  132. /// Close the Webview
  133. /// Will trigger the [onDestroy] event
  134. Future<Null> close() => _channel.invokeMethod("close");
  135. /// Listening url changed
  136. ///
  137. Stream<String> get onUrlChanged => _onUrlChanged.stream;
  138. }