Explorar o código

patch Android and update README

Lejard Hadrien %!s(int64=8) %!d(string=hai) anos
pai
achega
3ed35f0572

+ 3 - 3
CHANGELOG.md

@@ -1,14 +1,14 @@
 # 0.0.10
 
-- iOS: add Delegate, same as the event of android.
-
 - iOS && Android:
-
     - eval javascript
     - user agent setting
     - state change event
     - embed in rectangle(not fullscreen)
     - hidden webview
+    
+- Android
+    - adding Activity in manifest is no mandatory when not using fullScreen
 
 # 0.0.9
 

+ 51 - 35
README.md

@@ -2,57 +2,73 @@
 
 Plugin that allow Flutter to communicate with a native WebView.
 
-***For Android, it will launch a new Activity inside the App with the Webview inside. Does not allow to integrate a Webview inside a Flutter Widget***
-
-***For IOS, it will launch a new UIViewController inside the App with the UIWebView inside. Does not allow to integrate a Webview inside a Flutter Widget***
-
- - [x] Android
- - [x] IOS
-
 ## Getting Started
 
 For help getting started with Flutter, view our online [documentation](http://flutter.io/).
 
 ### How it works
 
-#### Launch WebView with variable url
+#### Launch WebView Fullscreen (default)
+
+On Android, add the Activity to you `AndroidManifest.xml`:
+
+```xml
+<activity android:name="com.flutter_webview_plugin.WebviewActivity" android:parentActivityName=".MainActivity"/>
+```
+
+***For Android, it will launch a new Activity inside the App with the Webview inside. Does not allow to integrate a Webview inside a Flutter Widget***
+
+***For IOS, it will launch a new UIViewController inside the App with the UIWebView inside. Does not allow to integrate a Webview inside a Flutter Widget***
+
 
 ```dart
-void launchWebView(String url) sync {
-  var flutterWebviewPlugin = new FlutterWebviewPlugin();  
-  
-  flutterWebviewPlugin.launch(url);  
-  
-  // Wait in this async function until destroy of WebView.
-  await flutterWebviewPlugin.onDestroy.first;
-}
+final flutterWebviewPlugin = new FlutterWebviewPlugin();  
+
+flutterWebviewPlugin.launch(url);  
 ```
 
-### Close launched WebView
+#### Close launched WebView
 
 ```dart
-void launchWebViewAndCloseAfterWhile(String url) {
-  var flutterWebviewPlugin = new FlutterWebviewPlugin();  
-  
-  flutterWebviewPlugin.launch(url);  
-  
-  // After 10 seconds.
-  new Timer(const Duration(seconds: 10), () {
-    // Close WebView.
-    // This will also emit the onDestroy event.
-    flutterWebviewPlugin.close();
-  });
-}
+final flutterWebviewPlugin = new FlutterWebviewPlugin();  
+
+flutterWebviewPlugin.launch(url);  
+
+....
+
+// Close WebView.
+// This will also emit the onDestroy event.
+flutterWebviewPlugin.close();
 ```
 
-### Android
+#### Hidden webView
 
-Add the Activity to you `AndroidManifest.xml`:
+```dart
+final flutterWebviewPlugin = new FlutterWebviewPlugin();  
 
-```xml
-<activity android:name="com.flutter_webview_plugin.WebviewActivity" android:parentActivityName=".MainActivity"/>
+flutterWebviewPlugin.launch(url, hidden: true);
+```
+
+#### Webview inside custom Rectangle
+
+```dart
+final flutterWebviewPlugin = new FlutterWebviewPlugin();  
+
+flutterWebviewPlugin.launch(url,
+                  fullScreen: false,
+                  rect: new Rect.fromLTWH(
+                      0.0, 
+                      0.0, 
+                      MediaQuery.of(context).size.width, 
+                      300.0));
 ```
 
-### iOS
+### Webview Events
+
+- `Stream<Null>` onDestroy
+- `Stream<String>` onUrlChanged
+- `Stream<Null>` onBackPressed
+- `Stream<WebViewStateChanged>` onStateChanged
 
-No extra configuration is needed.
+***Don't forget to dispose webview***
+`flutterWebviewPlugin.dispose()`

+ 40 - 0
android/src/main/java/com/flutter_webview_plugin/BrowserClient.java

@@ -0,0 +1,40 @@
+package com.flutter_webview_plugin;
+
+import android.graphics.Bitmap;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by lejard_h on 20/12/2017.
+ */
+
+public class BrowserClient extends WebViewClient {
+    public BrowserClient() {
+        super();
+    }
+
+    @Override
+    public void onPageStarted(WebView view, String url, Bitmap favicon) {
+        super.onPageStarted(view, url, favicon);
+        Map<String, Object> data = new HashMap<>();
+        data.put("url", url);
+        data.put("type", "startLoad");
+        FlutterWebviewPlugin.channel.invokeMethod("onState", data);
+    }
+
+    @Override
+    public void onPageFinished(WebView view, String url) {
+        super.onPageFinished(view, url);
+        Map<String, Object> data = new HashMap<>();
+        data.put("url", url);
+
+        FlutterWebviewPlugin.channel.invokeMethod("onUrlChanged", data);
+
+        data.put("type", "finishLoad");
+        FlutterWebviewPlugin.channel.invokeMethod("onState", data);
+
+    }
+}

+ 71 - 177
android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java

@@ -3,17 +3,9 @@ package com.flutter_webview_plugin;
 
 import android.app.Activity;
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.os.Build;
-import android.view.ViewGroup;
-import android.view.View;
-import android.webkit.CookieManager;
-import android.webkit.ValueCallback;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
+import android.content.Intent;
 import android.widget.FrameLayout;
 
-import java.util.HashMap;
 import java.util.Map;
 
 import io.flutter.plugin.common.MethodCall;
@@ -25,189 +17,91 @@ import io.flutter.plugin.common.PluginRegistry;
  * FlutterWebviewPlugin
  */
 public class FlutterWebviewPlugin implements MethodCallHandler {
-  private Activity activity;
-  private WebView webView;
-  public static MethodChannel channel;
-  private static final String CHANNEL_NAME = "flutter_webview_plugin";
+    private final int WEBVIEW_ACTIVITY_CODE = 1;
 
-  public static void registerWith(PluginRegistry.Registrar registrar) {
-    channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME);
-    FlutterWebviewPlugin instance = new FlutterWebviewPlugin((Activity)registrar.activity());
-    channel.setMethodCallHandler(instance);
-  }
+    private Activity activity;
+    private WebviewManager webViewManager;
+    public static MethodChannel channel;
+    private static final String CHANNEL_NAME = "flutter_webview_plugin";
 
-  private FlutterWebviewPlugin(Activity activity) {
-    this.activity = activity;
-  }
-
-  @Override
-  public void onMethodCall(MethodCall call, MethodChannel.Result result) {
-    switch (call.method) {
-      case "launch":
-        openUrl(call, result);
-        break;
-      case "close":
-        close(call, result);
-        break;
-      case "eval":
-        eval(call, result);
-        break;
-      default:
-        result.notImplemented();
-        break;
+    public static void registerWith(PluginRegistry.Registrar registrar) {
+        channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME);
+        FlutterWebviewPlugin instance = new FlutterWebviewPlugin((Activity) registrar.activity());
+        channel.setMethodCallHandler(instance);
     }
-  }
-
-  private void clearCookies() {
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-      CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
-        @Override
-        public void onReceiveValue(Boolean aBoolean) {
 
-        }
-      });
-    } else {
-      CookieManager.getInstance().removeAllCookie();
+    private FlutterWebviewPlugin(Activity activity) {
+        this.activity = activity;
     }
-  }
-
-  private void clearCache() {
-    webView.clearCache(true);
-    webView.clearFormData();
-  }
-
-  private WebViewClient setWebViewClient() {
-    WebViewClient webViewClient = new BrowserClient();
-    webView.setWebViewClient(webViewClient);
-    return webViewClient;
-  }
 
-  private void eval(String code, final MethodChannel.Result result) {
-    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
-      webView.evaluateJavascript(code, new ValueCallback<String>() {
-        @Override
-        public void onReceiveValue(String value) {
-          result.success(value);
+    @Override
+    public void onMethodCall(MethodCall call, MethodChannel.Result result) {
+        switch (call.method) {
+            case "launch":
+                openUrl(call, result);
+                break;
+            case "close":
+                close(call, result);
+                break;
+            case "eval":
+                eval(call, result);
+                break;
+            default:
+                result.notImplemented();
+                break;
         }
-      });
-    } else {
-      webView.loadUrl(code);
-    }
-  }
-
-  // @Override
-  protected void onDestroy() {
-    FlutterWebviewPlugin.channel.invokeMethod("onDestroy", null);
-  }
-
-  // @Override
-  public void onBackPressed() {
-    if(webView.canGoBack()){
-      webView.goBack();
-      return;
-    }
-    FlutterWebviewPlugin.channel.invokeMethod("onBackPressed", null);
-  }
-
-  private static int dp2px(Context context, float dp) {
-    final float scale = context.getResources().getDisplayMetrics().density;
-    return (int) (dp * scale +0.5f);
-  }
-
-  private void openUrl(MethodCall call, MethodChannel.Result result) {
-    if (webView == null) {
-      webView = new WebView(activity);
-
-      Map<String, Number> rc = call.argument("rect");
-      if (rc != null) {
-        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
-                dp2px(activity, rc.get("width").intValue()), dp2px(activity, rc.get("height").intValue()));
-        params.setMargins(dp2px(activity, rc.get("left").intValue()), dp2px(activity, rc.get("top").intValue()),
-                0, 0);
-        activity.addContentView(webView, params);
-      }
-      else if (!(boolean) call.argument("hidden")) {
-        activity.setContentView(webView);
-      }
-
-      setWebViewClient();
-    }
-
-    webView.getSettings().setJavaScriptEnabled((boolean) call.argument("withJavascript"));
-
-    if ((boolean) call.argument("clearCache")) {
-      clearCache();
     }
 
-    if ((boolean) call.argument("hidden")) {
-      webView.setVisibility(View.INVISIBLE);
-    }
-
-    if ((boolean) call.argument("clearCookies")) {
-      clearCookies();
-    }
-
-    String userAgent = call.argument("userAgent");
-    if (userAgent != null) {
-      webView.getSettings().setUserAgentString(userAgent);
-    }
-
-    String url = (String) call.argument("url");
-    webView.loadUrl(url);
-    result.success(null);
-  }
-
-  private void close(MethodCall call, MethodChannel.Result result) {
-    if (View.VISIBLE == webView.getVisibility()) {
-      ViewGroup vg = (ViewGroup) (webView.getParent());
-      vg.removeView(webView);
-    }
-    webView = null;
-    result.success(null);
-
-    FlutterWebviewPlugin.channel.invokeMethod("onDestroy", null);
-  }
-
-  private void eval(MethodCall call, final MethodChannel.Result result) {
-    String code = call.argument("code");
-
-    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
-      webView.evaluateJavascript(code, new ValueCallback<String>() {
-        @Override
-        public void onReceiveValue(String value) {
-          result.success(value);
+    private void openUrl(MethodCall call, MethodChannel.Result result) {
+        if ((boolean) call.argument("fullScreen") && !(boolean) call.argument("hidden")) {
+            Intent intent = new Intent(activity, WebviewActivity.class);
+            intent.putExtra(WebviewActivity.URL_KEY, (String) call.argument("url"));
+            intent.putExtra(WebviewActivity.WITH_JAVASCRIPT_KEY, (boolean) call.argument("withJavascript"));
+            intent.putExtra(WebviewActivity.CLEAR_CACHE_KEY, (boolean) call.argument("clearCache"));
+            intent.putExtra(WebviewActivity.CLEAR_COOKIES_KEY, (boolean) call.argument("clearCookies"));
+            activity.startActivityForResult(intent, WEBVIEW_ACTIVITY_CODE);
+        } else {
+            if (webViewManager == null) {
+                webViewManager = new WebviewManager(activity);
+            }
+
+            Map<String, Number> rc = call.argument("rect");
+            if (rc != null) {
+                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+                        dp2px(activity, rc.get("width").intValue()), dp2px(activity, rc.get("height").intValue()));
+                params.setMargins(dp2px(activity, rc.get("left").intValue()), dp2px(activity, rc.get("top").intValue()),
+                        0, 0);
+                activity.addContentView(webViewManager.webView, params);
+            } else if (!(boolean) call.argument("hidden")) {
+                activity.setContentView(webViewManager.webView);
+            }
+
+            webViewManager.openUrl((boolean) call.argument("withJavascript"),
+                    (boolean) call.argument("clearCache"),
+                    (boolean) call.argument("hidden"),
+                    (boolean) call.argument("clearCookies"),
+                    (String) call.argument("userAgent"),
+                    (String) call.argument("url")
+            );
         }
-      });
-    } else {
-      // TODO:
-      webView.loadUrl(code);
+        result.success(null);
     }
-  }
 
-
-  private class BrowserClient extends WebViewClient {
-    private BrowserClient() {
-      super();
+    private void close(MethodCall call, MethodChannel.Result result) {
+        if (webViewManager != null) {
+            webViewManager.close(call, result);
+            webViewManager = null;
+        }
     }
 
-    @Override
-    public void onPageStarted(WebView view, String url, Bitmap favicon) {
-      super.onPageStarted(view, url, favicon);
-      Map<String, Object> data = new HashMap<>();
-      data.put("url", url);
-      FlutterWebviewPlugin.channel.invokeMethod("onUrlChanged", data);
-
-      data.put("type", "startLoad");
-      FlutterWebviewPlugin.channel.invokeMethod("onState", data);
+    private void eval(MethodCall call, final MethodChannel.Result result) {
+        if (webViewManager != null) {
+            webViewManager.eval(call, result);
+        }
     }
 
-    @Override
-    public void onPageFinished(WebView view, String url) {
-      super.onPageFinished(view, url);
-      Map<String, Object> data = new HashMap<>();
-      data.put("url", url);
-      data.put("type", "finishLoad");
-      FlutterWebviewPlugin.channel.invokeMethod("onState", data);
+    private static int dp2px(Context context, float dp) {
+        final float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (dp * scale + 0.5f);
     }
-  }
 }

+ 25 - 67
android/src/main/java/com/flutter_webview_plugin/WebviewActivity.java

@@ -1,29 +1,24 @@
 package com.flutter_webview_plugin;
 
 import android.app.Activity;
-import android.graphics.Bitmap;
-import android.os.Build;
 import android.os.Bundle;
-import android.webkit.CookieManager;
-import android.webkit.ValueCallback;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
 
-import java.util.HashMap;
-import java.util.Map;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
 
 /**
  * Created by lejard_h on 23/04/2017.
  */
 
-public class WebviewActivity extends Activity {
+public class WebviewActivity extends Activity implements MethodChannel.MethodCallHandler {
 
     static public final String URL_KEY = "URL";
     static public final String CLEAR_CACHE_KEY = "CLEAR_CACHE";
     static public final String CLEAR_COOKIES_KEY = "CLEAR_COOKIES";
     static public final String WITH_JAVASCRIPT_KEY = "WITH_JAVASCRIPT";
+    static public final String USER_AGENT_KEY = "USER_AGENT";
 
-    private WebView webView;
+    static WebviewManager webViewManager;
 
     public WebviewActivity() {
     }
@@ -32,49 +27,15 @@ public class WebviewActivity extends Activity {
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        webView = initWebview();
-        setContentView(webView);
-        clearCookies();
-        clearCache();
-        setWebViewClient();
-        loadUrl();
-    }
-
-    protected WebView initWebview() {
-        return new WebView(this);
-    }
-
-    protected void clearCookies() {
-        if (getIntent().getBooleanExtra(CLEAR_COOKIES_KEY, false)) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-                CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
-                    @Override
-                    public void onReceiveValue(Boolean aBoolean) {
-
-                    }
-                });
-            } else {
-                CookieManager.getInstance().removeAllCookie();
-            }
-        }
-    }
-
-    protected void clearCache() {
-        if (getIntent().getBooleanExtra(CLEAR_CACHE_KEY, false)) {
-            webView.clearCache(true);
-            webView.clearFormData();
-        }
-    }
-
-    protected WebViewClient setWebViewClient() {
-        WebViewClient webViewClient = new BrowserClient();
-        webView.setWebViewClient(webViewClient);
-        return webViewClient;
-    }
-
-    protected void loadUrl() {
-        webView.getSettings().setJavaScriptEnabled(getIntent().getBooleanExtra(WITH_JAVASCRIPT_KEY, true));
-        webView.loadUrl(getIntent().getStringExtra(URL_KEY));
+        webViewManager = new WebviewManager(this);
+        setContentView(webViewManager.webView);
+        webViewManager.openUrl(getIntent().getBooleanExtra(WITH_JAVASCRIPT_KEY, true),
+                getIntent().getBooleanExtra(CLEAR_CACHE_KEY, false),
+                false,
+                getIntent().getBooleanExtra(CLEAR_COOKIES_KEY, false),
+                getIntent().getStringExtra(USER_AGENT_KEY),
+                getIntent().getStringExtra(URL_KEY)
+                );
     }
 
     @Override
@@ -85,26 +46,23 @@ public class WebviewActivity extends Activity {
 
     @Override
     public void onBackPressed() {
-        if(webView.canGoBack()){
-            webView.goBack();
+        if(webViewManager.webView.canGoBack()){
+            webViewManager.webView.goBack();
             return;
         }
         FlutterWebviewPlugin.channel.invokeMethod("onBackPressed", null);
         super.onBackPressed();
     }
 
-
-    private class BrowserClient extends WebViewClient {
-        private BrowserClient() {
-            super();
-        }
-
-        @Override
-        public void onPageStarted(WebView view, String url, Bitmap favicon) {
-            super.onPageStarted(view, url, favicon);
-            Map<String, Object> data = new HashMap<>();
-            data.put("url", url);
-            FlutterWebviewPlugin.channel.invokeMethod("onUrlChanged", data);
+    @Override
+    public void onMethodCall(MethodCall call, MethodChannel.Result result) {
+        switch (call.method) {
+            case "eval":
+                webViewManager.eval(call, result);
+                break;
+            default:
+                result.notImplemented();
+                break;
         }
     }
 }

+ 100 - 0
android/src/main/java/com/flutter_webview_plugin/WebviewManager.java

@@ -0,0 +1,100 @@
+package com.flutter_webview_plugin;
+
+import android.app.Activity;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.CookieManager;
+import android.webkit.ValueCallback;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * Created by lejard_h on 20/12/2017.
+ */
+
+class WebviewManager {
+
+    Activity activity;
+    WebView webView;
+
+    WebviewManager(Activity activity) {
+        this.activity = activity;
+        this.webView = new WebView(activity);
+        WebViewClient webViewClient = new BrowserClient();
+        webView.setWebViewClient(webViewClient);
+    }
+
+
+
+    private void clearCookies() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
+                @Override
+                public void onReceiveValue(Boolean aBoolean) {
+
+                }
+            });
+        } else {
+            CookieManager.getInstance().removeAllCookie();
+        }
+    }
+
+    private void clearCache() {
+        webView.clearCache(true);
+        webView.clearFormData();
+    }
+
+    void openUrl(boolean withJavascript, boolean clearCache, boolean hidden, boolean clearCookies, String userAgent, String url) {
+        webView.getSettings().setJavaScriptEnabled(withJavascript);
+
+        if (clearCache) {
+            clearCache();
+        }
+
+        if (hidden) {
+            webView.setVisibility(View.INVISIBLE);
+        }
+
+        if (clearCookies) {
+            clearCookies();
+        }
+
+        if (userAgent != null) {
+            webView.getSettings().setUserAgentString(userAgent);
+        }
+
+        webView.loadUrl(url);
+    }
+
+    void close(MethodCall call, MethodChannel.Result result) {
+        if (View.VISIBLE == webView.getVisibility()) {
+            ViewGroup vg = (ViewGroup) (webView.getParent());
+            vg.removeView(webView);
+        }
+        webView = null;
+        result.success(null);
+
+        FlutterWebviewPlugin.channel.invokeMethod("onDestroy", null);
+    }
+
+    void eval(MethodCall call, final MethodChannel.Result result) {
+        String code = call.argument("code");
+
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+            webView.evaluateJavascript(code, new ValueCallback<String>() {
+                @Override
+                public void onReceiveValue(String value) {
+                    result.success(value);
+                }
+            });
+        } else {
+            // TODO:
+            webView.loadUrl(code);
+        }
+    }
+
+}

+ 13 - 0
example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java

@@ -0,0 +1,13 @@
+package io.flutter.plugins;
+
+import io.flutter.plugin.common.PluginRegistry;
+import com.flutter_webview_plugin.FlutterWebviewPlugin;
+
+/**
+ * Generated file. Do not edit.
+ */
+public final class GeneratedPluginRegistrant {
+  public static void registerWith(PluginRegistry registry) {
+    FlutterWebviewPlugin.registerWith(registry.registrarFor("com.flutter_webview_plugin.FlutterWebviewPlugin"));
+  }
+}

+ 18 - 4
example/lib/main.dart

@@ -35,7 +35,7 @@ class MyHomePage extends StatefulWidget {
 
 class _MyHomePageState extends State<MyHomePage> {
   // Instance of WebView plugin
-  final FlutterWebViewPlugin flutterWebviewPlugin = new FlutterWebViewPlugin();
+  final flutterWebviewPlugin = new FlutterWebviewPlugin();
 
   // On destroy stream
   StreamSubscription _onDestroy;
@@ -43,8 +43,11 @@ class _MyHomePageState extends State<MyHomePage> {
   // On urlChanged stream
   StreamSubscription<String> _onUrlChanged;
 
+  // On urlChanged stream
+  StreamSubscription<WebViewStateChanged> _onStateChanged;
+
   TextEditingController _urlCtrl =
-      new TextEditingController(text: "http://github.com");
+      new TextEditingController(text: "https://github.com/dart-flitter/flutter_webview_plugin");
 
   TextEditingController _codeCtrl =
       new TextEditingController(text: "window.navigator.userAgent");
@@ -74,13 +77,24 @@ class _MyHomePageState extends State<MyHomePage> {
         });
       }
     });
+
+    _onStateChanged = flutterWebviewPlugin.onStateChanged.listen((WebViewStateChanged state) {
+      if (mounted) {
+        setState(() {
+          _history.add("onStateChanged: ${state.type} ${state.url}");
+        });
+      }
+    });
   }
 
   @override
   void dispose() {
     // Every listener should be canceled, the same should be done with this stream.
-    _onDestroy?.cancel();
-    _onUrlChanged?.cancel();
+    _onDestroy.cancel();
+    _onUrlChanged.cancel();
+    _onStateChanged.cancel();
+
+    flutterWebviewPlugin.dispose();
 
     super.dispose();
   }

+ 2 - 2
ios/Classes/FlutterWebviewPlugin.m

@@ -138,11 +138,11 @@ static NSString *const CHANNEL_NAME = @"flutter_webview_plugin";
 }
 
 -(void)webViewDidStartLoad:(UIWebView *)webView {
-    [channel invokeMethod:@"onState" arguments:@{@"type": @"startLoad"}];
+    [channel invokeMethod:@"onState" arguments:@{@"type": @"startLoad", @"url": webView.request.URL.absoluteString}];
 }
 
 - (void)webViewDidFinishLoad:(UIWebView *)webView {
-    [channel invokeMethod:@"onState" arguments:@{@"type": @"finishLoad"}];
+    [channel invokeMethod:@"onState" arguments:@{@"type": @"finishLoad", @"url": webView.request.URL.absoluteString}];
 }
 
 - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {

+ 57 - 43
lib/flutter_webview_plugin.dart

@@ -5,27 +5,27 @@ import 'package:flutter/material.dart';
 
 const _kChannel = 'flutter_webview_plugin';
 
-// TODO: more genral state for iOS/android
+// TODO: more general state for iOS/android
 enum WebViewState { shouldStart, startLoad, finishLoad }
 
-/// Singleton Class that communicate with a fullscreen Webview Instance
-/// Have to be instanciate after `runApp` called.
-class FlutterWebViewPlugin {
-  final MethodChannel _channel;
+// TODO: use an id by webview to be able to manage multiple webview
 
-  final StreamController<Null> _onDestroy = new StreamController.broadcast();
-  final StreamController<Null> _onBackPressed =
-      new StreamController.broadcast();
+/// Singleton Class that communicate with a Webview Instance
+/// Have to be instanciate after `runApp` called.
+class FlutterWebviewPlugin {
+  final _channel = const MethodChannel(_kChannel);
 
-  final StreamController<String> _onUrlChanged =
-      new StreamController.broadcast();
+  final _onDestroy = new StreamController<Null>.broadcast();
+  final _onBackPressed = new StreamController<Null>.broadcast();
+  final _onUrlChanged = new StreamController<String>.broadcast();
+  final _onStateChanged = new StreamController<WebViewStateChanged>.broadcast();
+  final _onError = new StreamController<String>.broadcast();
 
-  final StreamController<Null> _onStateChanged =
-      new StreamController.broadcast();
+  static FlutterWebviewPlugin _instance;
 
-  final StreamController<Null> _onError = new StreamController.broadcast();
+  factory FlutterWebviewPlugin() => _instance ??= new FlutterWebviewPlugin._();
 
-  FlutterWebViewPlugin() : _channel = const MethodChannel(_kChannel) {
+  FlutterWebviewPlugin._() {
     _channel.setMethodCallHandler(_handleMessages);
   }
 
@@ -41,7 +41,7 @@ class FlutterWebViewPlugin {
         _onUrlChanged.add(call.arguments["url"]);
         break;
       case "onState":
-        _onStateChanged.add(call.arguments);
+        _onStateChanged.add(new WebViewStateChanged.fromMap(call.arguments));
         break;
       case "onError":
         _onError.add(call.arguments);
@@ -50,13 +50,10 @@ class FlutterWebViewPlugin {
   }
 
   /// Listening the OnDestroy LifeCycle Event for Android
-  /// content is Map for url
   Stream<Null> get onDestroy => _onDestroy.stream;
 
   /// Listening url changed
-  /// iOS WebView: worked
-  /// android: worked
-  Stream<Null> get onUrlChanged => _onUrlChanged.stream;
+  Stream<String> get onUrlChanged => _onUrlChanged.stream;
 
   /// Listening the onBackPressed Event for Android
   /// content null
@@ -65,39 +62,23 @@ class FlutterWebViewPlugin {
   Stream<Null> get onBackPressed => _onBackPressed.stream;
 
   /// Listening the onState Event for iOS WebView and Android
-  /// content is Map for type: {shouldStart|startLoad|finishLoad}
+  /// content is Map for type: {shouldStart(iOS)|startLoad|finishLoad}
   /// more detail than other events
-  /// iOS WebView: worked
-  /// android: Not for now.
-  Stream<Null> get onStateChanged => _onStateChanged.stream;
+  Stream<WebViewStateChanged> get onStateChanged => _onStateChanged.stream;
 
   /// 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
   ///     iOS WebView: Not implemented yet
-  ///     iOS WkWebView: TODO: 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: Implemented
-  ///   [rect]: show in rect(not full screen)
-  ///     iOS WebView: worked
-  ///     android: Implemented
-  ///   [enableAppScheme]: false will enable all schemes, true only for httt/https/about
-  ///     iOS WebView: worked
+  /// - [fullScreen]: show in full screen mode, default true
+  /// - [rect]: show in rect(not full screen)
+  /// - [enableAppScheme]: false will enable all schemes, true only for httt/https/about
   ///     android: Not implemented yet
-  ///   [userAgent]: set the User-Agent of WebView
-  ///     iOS WebView: worked
-  ///     android: Implemented
+  /// - [userAgent]: set the User-Agent of WebView
   Future<Null> launch(String url,
       {bool withJavascript: true,
       bool clearCache: false,
@@ -129,8 +110,7 @@ class FlutterWebViewPlugin {
     await _channel.invokeMethod('launch', args);
   }
 
-  /// iOS WebView: worked
-  /// android: implemented
+  /// Execute Javascript inside webview
   Future<String> evalJavascript(String code) {
     return _channel.invokeMethod('eval', {"code": code});
   }
@@ -138,4 +118,38 @@ class FlutterWebViewPlugin {
   /// Close the Webview
   /// Will trigger the [onDestroy] event
   Future<Null> close() => _channel.invokeMethod("close");
+
+  /// Close all Streams
+  void dispose() {
+    _onDestroy.close();
+    _onBackPressed.close();
+    _onUrlChanged.close();
+    _onStateChanged.close();
+    _onError.close();
+    _instance = null;
+  }
+}
+
+class WebViewStateChanged {
+  final WebViewState type;
+  final String url;
+  final int navigationType;
+
+  WebViewStateChanged(this.type, this.url, this.navigationType);
+
+  factory WebViewStateChanged.fromMap(Map<String, dynamic> map) {
+    WebViewState t;
+    switch (map["type"]) {
+      case "shouldStart":
+        t = WebViewState.shouldStart;
+        break;
+      case "startLoad":
+        t = WebViewState.startLoad;
+        break;
+      case "finishLoad":
+        t = WebViewState.finishLoad;
+        break;
+    }
+    return new WebViewStateChanged(t, map["url"], map["navigationType"]);
+  }
 }