Browse Source

Merge pull request #1 from pedia/schema

Schema
Ken 8 years ago
parent
commit
d2fd59e2cf
3 changed files with 138 additions and 32 deletions
  1. 39 12
      example/lib/main.dart
  2. 36 13
      ios/Classes/FlutterWebviewPlugin.m
  3. 63 7
      lib/flutter_webview_plugin.dart

+ 39 - 12
example/lib/main.dart

@@ -32,7 +32,7 @@ class MyHomePage extends StatefulWidget {
 
 class _MyHomePageState extends State<MyHomePage> {
   // Instance of WebView plugin
-  final FlutterWebviewPlugin flutterWebviewPlugin = new FlutterWebviewPlugin();
+  final FlutterWebViewPlugin flutterWebviewPlugin = new FlutterWebViewPlugin();
 
   // On destroy stream
   StreamSubscription _onDestroy;
@@ -42,8 +42,12 @@ class _MyHomePageState extends State<MyHomePage> {
 
   StreamSubscription<String> _onStateChanged;
 
-  TextEditingController _ctrl =
-      new TextEditingController(text: "https://flutter.io");
+  TextEditingController _urlCtrl =
+      new TextEditingController(text: "http://github.com");
+
+  TextEditingController _codeCtrl =
+      new TextEditingController(text: "window.location.href");
+
   GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey();
 
   final _history = [];
@@ -52,10 +56,10 @@ class _MyHomePageState extends State<MyHomePage> {
   initState() {
     super.initState();
 
-    _onStateChanged = flutterWebviewPlugin.stateChanged.listen((String state) {
+    _onStateChanged = flutterWebviewPlugin.stateChanged.listen((dynamic state) {
       if (mounted) {
         setState(() {
-          _history.add(state);
+          _history.add("stateChanged: $state");
         });
       }
     });
@@ -73,7 +77,7 @@ class _MyHomePageState extends State<MyHomePage> {
     _onUrlChanged = flutterWebviewPlugin.onUrlChanged.listen((String url) {
       if (mounted) {
         setState(() {
-          _history.add(url);
+          _history.add("onUrlChanged: $url");
         });
       }
     });
@@ -100,30 +104,53 @@ class _MyHomePageState extends State<MyHomePage> {
         children: [
           new Container(
             padding: const EdgeInsets.all(24.0),
-            child: new TextField(controller: _ctrl),
+            child: new TextField(controller: _urlCtrl),
           ),
           new RaisedButton(
             onPressed: () {
-              flutterWebviewPlugin.launch(_ctrl.text,
+              flutterWebviewPlugin.launch(_urlCtrl.text,
                   fullScreen: false,
                   rect: new Rect.fromLTWH(
                       0.0, 0.0, MediaQuery.of(context).size.width, 300.0));
             },
-            child: new Text("Open Webview"),
+            child: new Text("Open Webview (rect)"),
           ),
           new RaisedButton(
             onPressed: () {
-              flutterWebviewPlugin.launch(_ctrl.text, hidden: true);
+              flutterWebviewPlugin.launch(_urlCtrl.text, hidden: true);
             },
             child: new Text("Open 'hidden' Webview"),
           ),
           new RaisedButton(
             onPressed: () {
-              flutterWebviewPlugin.launch(_ctrl.text, fullScreen: true);
+              flutterWebviewPlugin.launch(_urlCtrl.text, fullScreen: true);
             },
             child: new Text("Open Fullscreen Webview"),
           ),
-          new Text(_history.join(", "))
+          new Container(
+            padding: const EdgeInsets.all(24.0),
+            child: new TextField(controller: _codeCtrl),
+          ),
+          new RaisedButton(
+            onPressed: () {
+              Future<String> future =
+                  flutterWebviewPlugin.evalJavascript(_codeCtrl.text);
+              future.then((String result) {
+                setState(() {
+                  _history.add("eval: $result");
+                });
+              });
+            },
+            child: new Text("Eval some javascript"),
+          ),
+          new RaisedButton(
+            onPressed: () {
+              _history.clear();
+              flutterWebviewPlugin.close();
+            },
+            child: new Text("Close"),
+          ),
+          new Text(_history.join("\n"))
         ],
       ),
     );

+ 36 - 13
ios/Classes/FlutterWebviewPlugin.m

@@ -6,6 +6,7 @@ static NSString *const EVENT_CHANNEL_NAME = @"flutter_webview_plugin_event";
 // UIWebViewDelegate
 @interface FlutterWebviewPlugin() <UIWebViewDelegate, FlutterStreamHandler> {
     FlutterEventSink _eventSink;
+    BOOL _enableAppScheme;
 }
 @end
 
@@ -44,24 +45,20 @@ static NSString *const EVENT_CHANNEL_NAME = @"flutter_webview_plugin_event";
     } else if ([@"close" isEqualToString:call.method]) {
         [self closeWebView];
         result(nil);
+    } else if ([@"eval" isEqualToString:call.method]) {
+        result([self evalJavascript:call]);
     } else {
         result(FlutterMethodNotImplemented);
     }
 }
 
-- (void)launch:(FlutterMethodCall*)call {
-    NSString *url = call.arguments[@"url"];
-    
-    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
-    [self.webview loadRequest:request];
-}
-
 - (void)initWebView:(FlutterMethodCall*)call {
     // NSNumber *withJavascript = call.arguments[@"withJavascript"];
     NSNumber *clearCache = call.arguments[@"clearCache"];
     NSNumber *clearCookies = call.arguments[@"clearCookies"];
     NSNumber *hidden = call.arguments[@"hidden"];
     NSDictionary *rect = call.arguments[@"rect"];
+    _enableAppScheme = call.arguments[@"enableAppScheme"];
     
     //
     if ([clearCache boolValue]) {
@@ -92,32 +89,58 @@ static NSString *const EVENT_CHANNEL_NAME = @"flutter_webview_plugin_event";
     [self launch:call];
 }
 
+- (void)launch:(FlutterMethodCall*)call {
+    NSString *url = call.arguments[@"url"];
+    
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
+    [self.webview loadRequest:request];
+}
+
+- (NSString *)evalJavascript:(FlutterMethodCall*)call {
+    NSString *code = call.arguments[@"code"];
+    
+    NSString *result = [self.webview stringByEvaluatingJavaScriptFromString:code];
+    return result;
+}
+
 - (void)closeWebView {
     [self.webview stopLoading];
     [self.webview removeFromSuperview];
     self.webview.delegate = nil;
     self.webview = nil;
+    [self sendEvent:@"destroy"];
 }
 
 
 #pragma mark -- WebView Delegate
 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
-    [self sendStateEvent:[NSString stringWithFormat:@"shouldStart %@", request.URL]];
-    return YES;
+    NSArray *data = [NSArray arrayWithObjects:@"shouldStart",
+                     request.URL.absoluteString, [NSNumber numberWithInt:navigationType],
+                     nil];
+    [self sendEvent:data];
+    
+    if (_enableAppScheme)
+        return YES;
+
+    // disable some scheme
+    return [request.URL.scheme isEqualToString:@"http"] ||
+            [request.URL.scheme isEqualToString:@"https"] ||
+            [request.URL.scheme isEqualToString:@"about"];
 }
+
 -(void)webViewDidStartLoad:(UIWebView *)webView {
-    [self sendStateEvent:@"startLoad"];
+    [self sendEvent:@"startLoad"];
 }
 
 - (void)webViewDidFinishLoad:(UIWebView *)webView {
-    [self sendStateEvent:@"finishLoad"];
+    [self sendEvent:@"finishLoad"];
 }
 
 - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
     id data = [FlutterError errorWithCode:[NSString stringWithFormat:@"%ld", error.code]
                                   message:error.localizedDescription
                                   details:error.localizedFailureReason];
-    [self sendStateEvent:data];
+    [self sendEvent:data];
 }
 
 #pragma mark -- WkWebView Delegate
@@ -135,7 +158,7 @@ static NSString *const EVENT_CHANNEL_NAME = @"flutter_webview_plugin_event";
     return nil;
 }
 
-- (void)sendStateEvent:(id)data {
+- (void)sendEvent:(id)data {
     // data should be @"" or [FlutterError]
     if (!_eventSink)
         return;

+ 63 - 7
lib/flutter_webview_plugin.dart

@@ -6,17 +6,49 @@ import 'package:flutter/material.dart';
 const _kChannel = 'flutter_webview_plugin';
 const _kEvent = 'flutter_webview_plugin_event';
 
+// TODO: more genral state for iOS/android
+enum WebViewState { startLoad, finishLoad }
+
+// copy from UIWebView.h
+enum _WebViewNavigateType {
+  TypeLinkClicked,
+  TypeFormSubmitted,
+  TypeBackForward,
+  TypeReload,
+  TypeFormResubmitted,
+  TypeOther
+}
+
 /// Singleton Class that communicate with a fullscreen Webview Instance
 /// Have to be instanciate after `runApp` called.
-class FlutterWebviewPlugin {
+class FlutterWebViewPlugin {
   final MethodChannel _channel;
 
   final EventChannel _event;
   Stream<String> _stateChanged;
 
   Stream<String> get stateChanged {
+    assert(_WebViewNavigateType.TypeLinkClicked.index == 0);
+    assert(_WebViewNavigateType.TypeOther.index == 5);
     if (_stateChanged == null) {
       _stateChanged = _event.receiveBroadcastStream();
+      _stateChanged.listen((var result) {
+        // the list like: [state, url, navtype]
+        if (result is List && result.length == 3) {
+          if (_WebViewNavigateType.TypeBackForward.index == result[2]) {
+            _onBackPressed.add(Null);
+          } else if (_WebViewNavigateType.TypeOther.index == result[2] ||
+              _WebViewNavigateType.TypeLinkClicked.index == result[2] ||
+              _WebViewNavigateType.TypeFormSubmitted.index == result[2]) {
+            // TODO: find out better way
+            _onUrlChanged.add(result[1]);
+          }
+        } else if (result is String) {
+          if (result == "destroy") {
+            _onDestroy.add(Null);
+          }
+        }
+      });
     }
     return _stateChanged;
   }
@@ -28,14 +60,13 @@ class FlutterWebviewPlugin {
   final StreamController<String> _onUrlChanged =
       new StreamController.broadcast();
 
-  FlutterWebviewPlugin() : 
-    _channel = const MethodChannel(_kChannel),
-    _event = const EventChannel(_kEvent) {
+  FlutterWebViewPlugin()
+      : _channel = const MethodChannel(_kChannel),
+        _event = const EventChannel(_kEvent) {
     _channel.setMethodCallHandler(_handleMessages);
   }
 
   Future<Null> _handleMessages(MethodCall call) async {
-    print("_handleMessages $call");
     switch (call.method) {
       case "onDestroy":
         _onDestroy.add(null);
@@ -61,14 +92,34 @@ class FlutterWebviewPlugin {
 
   /// Start the Webview with [url]
   /// - [withJavascript] enable Javascript or not for the Webview
+  ///     iOS WebView: Not implemented yet
+  ///     android: Implemented.
   /// - [clearCache] clear the cache of the Webview
-  /// - clearCookies] clear all cookies of the Webview
+  ///     iOS WebView: Not implemented yet
+  ///     iOS WKWebView: will implement later
+  ///     android: Implemented
+  /// - [clearCookies] clear all cookies of the Webview
+  ///     iOS WebView: Not implemented yet
+  ///     iOS WKWebView: will implement later
+  ///     android: Implemented
+  /// - [hidden] not show
+  ///     iOS WebView: not shown(addSubView) in ViewController
+  ///     android: Implemented
+  ///   [fullScreen]: show in full screen mode, default true
+  ///     iOS WebView: without rect, show in full screen mode
+  ///     android: Not implemented yet
+  ///   [rect]: show in rect(not full screen)
+  ///     iOS WebView: worked
+  ///     android: Not implemented yet
+  ///   [enableAppScheme]: false will enable all schemes, true only for
+  /// httt/https/about
   Future<Null> launch(String url,
       {bool withJavascript: true,
       bool clearCache: false,
       bool clearCookies: false,
       bool hidden: false,
       bool fullScreen: true,
+      bool enableAppScheme: true,
       Rect rect: null}) async {
     Map<String, dynamic> args = {
       "url": url,
@@ -76,7 +127,8 @@ class FlutterWebviewPlugin {
       "clearCache": clearCache,
       "hidden": hidden,
       "clearCookies": clearCookies,
-      "fullScreen": fullScreen
+      "fullScreen": fullScreen,
+      "enableAppScheme": enableAppScheme
     };
     if (!fullScreen) assert(rect != null);
     if (rect != null) {
@@ -90,6 +142,10 @@ class FlutterWebviewPlugin {
     await _channel.invokeMethod('launch', args);
   }
 
+  Future<String> evalJavascript(String code) {
+    return _channel.invokeMethod('eval', {"code": code});
+  }
+
   /// Close the Webview
   /// Will trigger the [onDestroy] event
   Future<Null> close() => _channel.invokeMethod("close");