diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md index 5230a2b0f7c7..f62554b919b7 100644 --- a/packages/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.18+2 + +* Add support for onProgressChanged event. + ## 0.3.18+1 * Be explicit that keyboard is not ready for production in README.md. diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebChromeClient.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebChromeClient.java new file mode 100644 index 000000000000..d355cd7e2bfe --- /dev/null +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebChromeClient.java @@ -0,0 +1,20 @@ +package io.flutter.plugins.webviewflutter; + +import android.webkit.WebChromeClient; +import android.webkit.WebView; + +import io.flutter.plugin.common.MethodChannel; + +public class FlutterWebChromeClient extends WebChromeClient { + private MethodChannel channel; + + public FlutterWebChromeClient(MethodChannel channel) { + this.channel = channel; + } + + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + channel.invokeMethod("onProgressChanged", newProgress); + } +} diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index 83a7ed6340f8..fbbc868994d4 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -55,6 +55,8 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { flutterWebViewClient = new FlutterWebViewClient(methodChannel); applySettings((Map) params.get("settings")); + webView.setWebChromeClient(new FlutterWebChromeClient(methodChannel)); + if (params.containsKey(JS_CHANNEL_NAMES_FIELD)) { registerJavaScriptChannelNames((List) params.get(JS_CHANNEL_NAMES_FIELD)); } diff --git a/packages/webview_flutter/example/lib/main.dart b/packages/webview_flutter/example/lib/main.dart index 20520d1532a4..d96dddbf11d2 100644 --- a/packages/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/example/lib/main.dart @@ -34,7 +34,7 @@ class WebViewExample extends StatefulWidget { class _WebViewExampleState extends State { final Completer _controller = Completer(); - + double progress = 0.0; @override Widget build(BuildContext context) { return Scaffold( @@ -49,31 +49,49 @@ class _WebViewExampleState extends State { // We're using a Builder here so we have a context that is below the Scaffold // to allow calling Scaffold.of(context) so we can show a snackbar. body: Builder(builder: (BuildContext context) { - return WebView( - initialUrl: 'https://flutter.dev', - javascriptMode: JavascriptMode.unrestricted, - onWebViewCreated: (WebViewController webViewController) { - _controller.complete(webViewController); - }, - // TODO(iskakaushik): Remove this when collection literals makes it to stable. - // ignore: prefer_collection_literals - javascriptChannels: [ - _toasterJavascriptChannel(context), - ].toSet(), - navigationDelegate: (NavigationRequest request) { - if (request.url.startsWith('https://www.youtube.com/')) { - print('blocking navigation to $request}'); - return NavigationDecision.prevent; - } - print('allowing navigation to $request'); - return NavigationDecision.navigate; - }, - onPageStarted: (String url) { - print('Page started loading: $url'); - }, - onPageFinished: (String url) { - print('Page finished loading: $url'); - }, + return Stack( + children: [ + WebView( + initialUrl: 'https://flutter.dev', + javascriptMode: JavascriptMode.unrestricted, + onWebViewCreated: (WebViewController webViewController) { + _controller.complete(webViewController); + }, + // TODO(iskakaushik): Remove this when collection literals makes it to stable. + // ignore: prefer_collection_literals + javascriptChannels: [ + _toasterJavascriptChannel(context), + ].toSet(), + navigationDelegate: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + print('blocking navigation to $request}'); + return NavigationDecision.prevent; + } + print('allowing navigation to $request'); + return NavigationDecision.navigate; + }, + onPageStarted: (String url) { + print('Page started loading: $url'); + }, + onPageFinished: (String url) { + print('Page finished loading: $url'); + }, + onProgressChanged: (int p){ + setState(() { + progress = p/100.0; + }); + }, + ), + Offstage( + offstage: progress == 1, + child: SizedBox( + height: 1.5, + child: LinearProgressIndicator( + value: progress, + valueColor: const AlwaysStoppedAnimation(Colors.red), + ), + )) + ], ); }), floatingActionButton: favoriteButton(), diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index 60fa24052038..bdfa45cc19af 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -77,6 +77,8 @@ - (instancetype)initWithFrame:(CGRect)frame [weakSelf onMethodCall:call result:result]; }]; + [_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil]; + [self applySettings:settings]; // TODO(amirh): return an error if apply settings failed once it's possible to do so. // https://github.com/flutter/flutter/issues/36228 @@ -88,7 +90,20 @@ - (instancetype)initWithFrame:(CGRect)frame } return self; } +#pragma mark - listen estimatedProgress +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + + if ([keyPath isEqualToString:@"estimatedProgress"]) { + if (object == _webView) { + int progress =(int)(_webView.estimatedProgress*100); + [_channel invokeMethod:@"onProgressChanged" arguments: [NSNumber numberWithInt:progress]]; + } + } +} +- (void)dealloc { + [_webView removeObserver:self forKeyPath:@"estimatedProgress"]; +} - (UIView*)view { return _webView; } diff --git a/packages/webview_flutter/lib/platform_interface.dart b/packages/webview_flutter/lib/platform_interface.dart index a3af47a5c714..8a5939ecb560 100644 --- a/packages/webview_flutter/lib/platform_interface.dart +++ b/packages/webview_flutter/lib/platform_interface.dart @@ -28,6 +28,9 @@ abstract class WebViewPlatformCallbacksHandler { /// Invoked by [WebViewPlatformController] when a page has finished loading. void onPageFinished(String url); + + /// Invoked by [WebViewPlatformController] when a page is loading in progress. + void onProgressChanged(int progress); } /// Interface for talking to the webview's platform implementation. diff --git a/packages/webview_flutter/lib/src/webview_method_channel.dart b/packages/webview_flutter/lib/src/webview_method_channel.dart index ad5a81e98ef5..525134b51d12 100644 --- a/packages/webview_flutter/lib/src/webview_method_channel.dart +++ b/packages/webview_flutter/lib/src/webview_method_channel.dart @@ -43,6 +43,9 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { case 'onPageStarted': _platformCallbacksHandler.onPageStarted(call.arguments['url']); return null; + case 'onProgressChanged': + _platformCallbacksHandler.onProgressChanged(call.arguments); + return null; } throw MissingPluginException( '${call.method} was invoked but has no handler'); diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index a57e2e13bdba..ea2d0a9e4078 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -79,6 +79,9 @@ typedef void PageStartedCallback(String url); /// Signature for when a [WebView] has finished loading a page. typedef void PageFinishedCallback(String url); +/// Signature for when a [WebView] is loading in progress. +typedef void OnProgressChangedCallback(int progress); + /// Specifies possible restrictions on automatic media playback. /// /// This is typically used in [WebView.initialMediaPlaybackPolicy]. @@ -147,6 +150,7 @@ class WebView extends StatefulWidget { this.gestureRecognizers, this.onPageStarted, this.onPageFinished, + this.onProgressChanged, this.debuggingEnabled = false, this.userAgent, this.initialMediaPlaybackPolicy = @@ -276,6 +280,10 @@ class WebView extends StatefulWidget { /// [WebViewController.evaluateJavascript] can assume this. final PageFinishedCallback onPageFinished; + + /// Invoked when a page is loading in progress. + final OnProgressChangedCallback onProgressChanged; + /// Controls whether WebView debugging is enabled. /// /// Setting this to true enables [WebView debugging on Android](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/). @@ -473,6 +481,13 @@ class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler { } } + @override + void onProgressChanged(int progress) { + if (_widget.onProgressChanged != null) { + _widget.onProgressChanged(progress); + } + } + void _updateJavascriptChannelsFromSet(Set channels) { _javascriptChannels.clear(); if (channels == null) { diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml index 8983f05d2b41..41421dfbb580 100644 --- a/packages/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. -version: 0.3.18+1 +version: 0.3.18+2 homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter environment: