From d41c9a049ada79d4755137dbaeaa5fc1756c48b6 Mon Sep 17 00:00:00 2001 From: Emily Fortuna Date: Tue, 18 Dec 2018 17:36:30 -0800 Subject: [PATCH 1/5] Add currentUrl accessor to WebView plugin. --- packages/webview_flutter/CHANGELOG.md | 5 ++ .../webviewflutter/FlutterWebView.java | 7 +++ .../example/ios/Flutter/Debug.xcconfig | 1 + .../example/ios/Flutter/Release.xcconfig | 1 + .../webview_flutter/example/lib/main.dart | 17 ++++++ .../ios/Classes/FlutterWebView.m | 8 +++ .../webview_flutter/lib/webview_flutter.dart | 8 +++ packages/webview_flutter/pubspec.yaml | 2 +- .../test/webview_flutter_test.dart | 60 +++++++++++++++---- 9 files changed, 95 insertions(+), 14 deletions(-) diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md index d0c61ab34541..39ea0b7048f2 100644 --- a/packages/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.1 + +* Added a `currentUrl` accessor for the WebView controller to look up what URL + is being displayed. + ## 0.1.0+1 * Fix null crash when initialUrl is unset on iOS. 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 d51254a62a60..1a8019c7dda1 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 @@ -53,6 +53,9 @@ public void onMethodCall(MethodCall methodCall, Result result) { case "goForward": goForward(methodCall, result); break; + case "currentUrl": + currentUrl(methodCall, result); + break; default: result.notImplemented(); } @@ -86,6 +89,10 @@ private void goForward(MethodCall methodCall, Result result) { result.success(null); } + private void currentUrl(MethodCall methodCall, Result result) { + result.success(webView.getUrl()); + } + @SuppressWarnings("unchecked") private void updateSettings(MethodCall methodCall, Result result) { applySettings((Map) methodCall.arguments); diff --git a/packages/webview_flutter/example/ios/Flutter/Debug.xcconfig b/packages/webview_flutter/example/ios/Flutter/Debug.xcconfig index 592ceee85b89..e8efba114687 100644 --- a/packages/webview_flutter/example/ios/Flutter/Debug.xcconfig +++ b/packages/webview_flutter/example/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/packages/webview_flutter/example/ios/Flutter/Release.xcconfig b/packages/webview_flutter/example/ios/Flutter/Release.xcconfig index 592ceee85b89..399e9340e6f6 100644 --- a/packages/webview_flutter/example/ios/Flutter/Release.xcconfig +++ b/packages/webview_flutter/example/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/packages/webview_flutter/example/lib/main.dart b/packages/webview_flutter/example/lib/main.dart index 9f40f7e84fd9..580ffd105d48 100644 --- a/packages/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/example/lib/main.dart @@ -30,8 +30,25 @@ class WebViewExample extends StatelessWidget { _controller.complete(webViewController); }, ), + floatingActionButton: favoriteButton(), ); } + + favoriteButton() { + return Builder(builder: (BuildContext context) { + return FloatingActionButton( + onPressed: () async { + if (_controller.isCompleted) { + var url = await (await _controller.future).currentUrl(); + Scaffold.of(context).showSnackBar( + SnackBar(content: Text("Favorited $url")), + ); + } + }, + child: Icon(Icons.favorite), + ); + }); + } } class SampleMenu extends StatelessWidget { diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index 9ea2d911bca6..71e2ba776bf4 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -32,6 +32,7 @@ @implementation FLTWebViewController { WKWebView* _webView; int64_t _viewId; FlutterMethodChannel* _channel; + NSString* _currentUrl; } - (instancetype)initWithFrame:(CGRect)frame @@ -74,6 +75,8 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self onGoBack:call result:result]; } else if ([[call method] isEqualToString:@"goForward"]) { [self onGoForward:call result:result]; + } else if ([[call method] isEqualToString:@"currentUrl"]) { + [self onCurrentUrl:call result:result]; } else { result(FlutterMethodNotImplemented); } @@ -115,6 +118,11 @@ - (void)onGoForward:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } +- (void)onCurrentUrl:(FlutterMethodCall*)call result:(FlutterResult)result { + _currentUrl = [[_webView URL] absoluteString]; + result(_currentUrl); +} + - (void)applySettings:(NSDictionary*)settings { for (NSString* key in settings) { if ([key isEqualToString:@"jsMode"]) { diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 8da971a03897..716637f889fe 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -205,6 +205,14 @@ class WebViewController { return _channel.invokeMethod('loadUrl', url); } + /// Accessor to the current URL that the WebView is displaying. + /// + /// If initialUrl was never specified, currentUrl returns `null`. + Future currentUrl() async { + String url = await _channel.invokeMethod('currentUrl'); + return url; + } + /// Checks whether there's a back history item. /// /// Note that this operation is asynchronous, and it is possible that the "canGoBack" state has diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml index f0e335468696..966e9ebff4e0 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.1.0+1 +version: 0.1.1 author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter diff --git a/packages/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/test/webview_flutter_test.dart index 1504b2d2d542..3a63911d8da3 100644 --- a/packages/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/test/webview_flutter_test.dart @@ -34,7 +34,7 @@ void main() { final FakePlatformWebView platformWebView = fakePlatformViewsController.lastCreatedView; - expect(platformWebView.currentUrl, 'https://youtube.com'); + expect(platformWebView.url, 'https://youtube.com'); }); testWidgets('JavaScript mode', (WidgetTester tester) async { @@ -73,7 +73,7 @@ void main() { controller.loadUrl('https://flutter.io'); - expect(platformWebView.currentUrl, 'https://flutter.io'); + expect(platformWebView.url, 'https://flutter.io'); }); testWidgets('Invald urls', (WidgetTester tester) async { @@ -92,14 +92,14 @@ void main() { fakePlatformViewsController.lastCreatedView; expect(() => controller.loadUrl(null), throwsA(anything)); - expect(platformWebView.currentUrl, isNull); + expect(platformWebView.url, isNull); expect(() => controller.loadUrl(''), throwsA(anything)); - expect(platformWebView.currentUrl, isNull); + expect(platformWebView.url, isNull); // Missing schema. expect(() => controller.loadUrl('flutter.io'), throwsA(anything)); - expect(platformWebView.currentUrl, isNull); + expect(platformWebView.url, isNull); }); testWidgets("Can't go back before loading a page", @@ -227,15 +227,15 @@ void main() { final FakePlatformWebView platformWebView = fakePlatformViewsController.lastCreatedView; - expect(platformWebView.currentUrl, 'https://youtube.com'); + expect(platformWebView.url, 'https://youtube.com'); controller.loadUrl('https://flutter.io'); - expect(platformWebView.currentUrl, 'https://flutter.io'); + expect(platformWebView.url, 'https://flutter.io'); controller.goBack(); - expect(platformWebView.currentUrl, 'https://youtube.com'); + expect(platformWebView.url, 'https://youtube.com'); }); testWidgets('Go forward', (WidgetTester tester) async { @@ -254,19 +254,50 @@ void main() { final FakePlatformWebView platformWebView = fakePlatformViewsController.lastCreatedView; - expect(platformWebView.currentUrl, 'https://youtube.com'); + expect(platformWebView.url, 'https://youtube.com'); controller.loadUrl('https://flutter.io'); - expect(platformWebView.currentUrl, 'https://flutter.io'); + expect(platformWebView.url, 'https://flutter.io'); controller.goBack(); - expect(platformWebView.currentUrl, 'https://youtube.com'); + expect(platformWebView.url, 'https://youtube.com'); controller.goForward(); - expect(platformWebView.currentUrl, 'https://flutter.io'); + expect(platformWebView.url, 'https://flutter.io'); + }); + + + testWidgets('Current URL', (WidgetTester tester) async { + WebViewController controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + + expect(controller, isNotNull); + + // Test a WebView without an explicitly set first URL. + // TODO(efortuna): expect null? empty string? specify in lower level code? + expect(await controller.currentUrl(), null); + + final FakePlatformWebView platformWebView = + fakePlatformViewsController.lastCreatedView; + + controller.loadUrl('https://youtube.com'); + expect(platformWebView.url, 'https://youtube.com'); + expect(await controller.currentUrl(), 'https://youtube.com'); + + controller.loadUrl('https://flutter.io'); + expect(await controller.currentUrl(), 'https://flutter.io'); + + controller.goBack(); + expect(await controller.currentUrl(), 'https://youtube.com'); }); } @@ -290,7 +321,7 @@ class FakePlatformWebView { List history = []; int currentPosition = -1; - String get currentUrl => history.isEmpty ? null : history[currentPosition]; + String get url => history.isEmpty ? null : history[currentPosition]; JavaScriptMode javaScriptMode; Future onMethodCall(MethodCall call) { @@ -321,6 +352,9 @@ class FakePlatformWebView { currentPosition = min(history.length - 1, currentPosition + 1); return Future.sync(() {}); break; + case 'currentUrl': + return Future.value(url); + break; } return Future.sync(() {}); } From 641801ea49fb9f4e3a3943b47ddf94ec52d22ac5 Mon Sep 17 00:00:00 2001 From: Emily Fortuna Date: Tue, 18 Dec 2018 17:38:45 -0800 Subject: [PATCH 2/5] remove unnecessary TODO --- packages/webview_flutter/test/webview_flutter_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/test/webview_flutter_test.dart index 3a63911d8da3..f4289b75e105 100644 --- a/packages/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/test/webview_flutter_test.dart @@ -283,7 +283,6 @@ void main() { expect(controller, isNotNull); // Test a WebView without an explicitly set first URL. - // TODO(efortuna): expect null? empty string? specify in lower level code? expect(await controller.currentUrl(), null); final FakePlatformWebView platformWebView = From 5926fada93a9dccf04475de9e584406efdc3d641 Mon Sep 17 00:00:00 2001 From: Emily Fortuna Date: Wed, 19 Dec 2018 12:25:15 -0800 Subject: [PATCH 3/5] Addressed Amir's comments --- .../webview_flutter/example/lib/main.dart | 15 +++-- .../webview_flutter/lib/webview_flutter.dart | 6 +- .../test/webview_flutter_test.dart | 63 ++++++++----------- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/packages/webview_flutter/example/lib/main.dart b/packages/webview_flutter/example/lib/main.dart index 580ffd105d48..2c67103e6023 100644 --- a/packages/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/example/lib/main.dart @@ -34,19 +34,22 @@ class WebViewExample extends StatelessWidget { ); } - favoriteButton() { - return Builder(builder: (BuildContext context) { + Widget favoriteButton() { + return FutureBuilder( + future: _controller.future, + builder: (BuildContext context, AsyncSnapshot controller) { + if (controller.hasData) { return FloatingActionButton( onPressed: () async { - if (_controller.isCompleted) { - var url = await (await _controller.future).currentUrl(); + final String url = await controller.data.currentUrl(); Scaffold.of(context).showSnackBar( SnackBar(content: Text("Favorited $url")), ); - } }, - child: Icon(Icons.favorite), + child: const Icon(Icons.favorite), ); + } + return Container(); }); } } diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 716637f889fe..c977a5f59cb0 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -207,7 +207,11 @@ class WebViewController { /// Accessor to the current URL that the WebView is displaying. /// - /// If initialUrl was never specified, currentUrl returns `null`. + /// If [WebView.initialUrl] was never specified, or if , currentUrl returns `null`. + /// Note that this operation is asynchronous, and it is possible that the + /// current URL changes again by the time this function returns (in other + /// words, by the time this future completes, the WebView may be displaying a + /// different URL). Future currentUrl() async { String url = await _channel.invokeMethod('currentUrl'); return url; diff --git a/packages/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/test/webview_flutter_test.dart index f4289b75e105..34732e0fce46 100644 --- a/packages/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/test/webview_flutter_test.dart @@ -27,14 +27,17 @@ void main() { }); testWidgets('Initial url', (WidgetTester tester) async { - await tester.pumpWidget(const WebView( - initialUrl: 'https://youtube.com', - )); - - final FakePlatformWebView platformWebView = - fakePlatformViewsController.lastCreatedView; + WebViewController controller; + await tester.pumpWidget( + WebView( + initialUrl: 'https://youtube.com', + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); - expect(platformWebView.url, 'https://youtube.com'); + expect(await controller.currentUrl(), 'https://youtube.com'); }); testWidgets('JavaScript mode', (WidgetTester tester) async { @@ -68,15 +71,12 @@ void main() { expect(controller, isNotNull); - final FakePlatformWebView platformWebView = - fakePlatformViewsController.lastCreatedView; - controller.loadUrl('https://flutter.io'); - expect(platformWebView.url, 'https://flutter.io'); + expect(await controller.currentUrl(), 'https://flutter.io'); }); - testWidgets('Invald urls', (WidgetTester tester) async { + testWidgets('Invalid urls', (WidgetTester tester) async { WebViewController controller; await tester.pumpWidget( WebView( @@ -88,18 +88,15 @@ void main() { expect(controller, isNotNull); - final FakePlatformWebView platformWebView = - fakePlatformViewsController.lastCreatedView; - expect(() => controller.loadUrl(null), throwsA(anything)); - expect(platformWebView.url, isNull); + expect(await controller.currentUrl(), isNull); expect(() => controller.loadUrl(''), throwsA(anything)); - expect(platformWebView.url, isNull); + expect(await controller.currentUrl(), isNull); // Missing schema. expect(() => controller.loadUrl('flutter.io'), throwsA(anything)); - expect(platformWebView.url, isNull); + expect(await controller.currentUrl(), isNull); }); testWidgets("Can't go back before loading a page", @@ -224,18 +221,15 @@ void main() { expect(controller, isNotNull); - final FakePlatformWebView platformWebView = - fakePlatformViewsController.lastCreatedView; - - expect(platformWebView.url, 'https://youtube.com'); + expect(await controller.currentUrl(), 'https://youtube.com'); controller.loadUrl('https://flutter.io'); - expect(platformWebView.url, 'https://flutter.io'); + expect(await controller.currentUrl(), 'https://flutter.io'); controller.goBack(); - expect(platformWebView.url, 'https://youtube.com'); + expect(await controller.currentUrl(), 'https://youtube.com'); }); testWidgets('Go forward', (WidgetTester tester) async { @@ -251,22 +245,19 @@ void main() { expect(controller, isNotNull); - final FakePlatformWebView platformWebView = - fakePlatformViewsController.lastCreatedView; - - expect(platformWebView.url, 'https://youtube.com'); + expect(await controller.currentUrl(), 'https://youtube.com'); controller.loadUrl('https://flutter.io'); - expect(platformWebView.url, 'https://flutter.io'); + expect(await controller.currentUrl(), 'https://flutter.io'); controller.goBack(); - expect(platformWebView.url, 'https://youtube.com'); + expect(await controller.currentUrl(), 'https://youtube.com'); controller.goForward(); - expect(platformWebView.url, 'https://flutter.io'); + expect(await controller.currentUrl(), 'https://flutter.io'); }); @@ -283,13 +274,9 @@ void main() { expect(controller, isNotNull); // Test a WebView without an explicitly set first URL. - expect(await controller.currentUrl(), null); - - final FakePlatformWebView platformWebView = - fakePlatformViewsController.lastCreatedView; + expect(await controller.currentUrl(), isNull); controller.loadUrl('https://youtube.com'); - expect(platformWebView.url, 'https://youtube.com'); expect(await controller.currentUrl(), 'https://youtube.com'); controller.loadUrl('https://flutter.io'); @@ -320,7 +307,7 @@ class FakePlatformWebView { List history = []; int currentPosition = -1; - String get url => history.isEmpty ? null : history[currentPosition]; + String get currentUrl => history.isEmpty ? null : history[currentPosition]; JavaScriptMode javaScriptMode; Future onMethodCall(MethodCall call) { @@ -352,7 +339,7 @@ class FakePlatformWebView { return Future.sync(() {}); break; case 'currentUrl': - return Future.value(url); + return Future.value(currentUrl); break; } return Future.sync(() {}); From 4176144c635d33b0528e0c9080d240d461624634 Mon Sep 17 00:00:00 2001 From: Emily Fortuna Date: Wed, 19 Dec 2018 12:29:56 -0800 Subject: [PATCH 4/5] addressed analyzer error --- .../webview_flutter/example/lib/main.dart | 21 ++++++++++--------- .../webview_flutter/lib/webview_flutter.dart | 2 +- .../test/webview_flutter_test.dart | 1 - 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/webview_flutter/example/lib/main.dart b/packages/webview_flutter/example/lib/main.dart index 2c67103e6023..3da5d2db6caf 100644 --- a/packages/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/example/lib/main.dart @@ -37,20 +37,21 @@ class WebViewExample extends StatelessWidget { Widget favoriteButton() { return FutureBuilder( future: _controller.future, - builder: (BuildContext context, AsyncSnapshot controller) { + builder: (BuildContext context, + AsyncSnapshot controller) { if (controller.hasData) { - return FloatingActionButton( - onPressed: () async { - final String url = await controller.data.currentUrl(); - Scaffold.of(context).showSnackBar( - SnackBar(content: Text("Favorited $url")), + return FloatingActionButton( + onPressed: () async { + final String url = await controller.data.currentUrl(); + Scaffold.of(context).showSnackBar( + SnackBar(content: Text("Favorited $url")), + ); + }, + child: const Icon(Icons.favorite), ); - }, - child: const Icon(Icons.favorite), - ); } return Container(); - }); + }); } } diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index c977a5f59cb0..3ce395e63118 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -213,7 +213,7 @@ class WebViewController { /// words, by the time this future completes, the WebView may be displaying a /// different URL). Future currentUrl() async { - String url = await _channel.invokeMethod('currentUrl'); + final String url = await _channel.invokeMethod('currentUrl'); return url; } diff --git a/packages/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/test/webview_flutter_test.dart index 34732e0fce46..d3cdc5e398e6 100644 --- a/packages/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/test/webview_flutter_test.dart @@ -260,7 +260,6 @@ void main() { expect(await controller.currentUrl(), 'https://flutter.io'); }); - testWidgets('Current URL', (WidgetTester tester) async { WebViewController controller; await tester.pumpWidget( From 95604c0e0adbb2c3fd562a0a22f6307eb12414f8 Mon Sep 17 00:00:00 2001 From: Emily Fortuna Date: Wed, 19 Dec 2018 13:11:31 -0800 Subject: [PATCH 5/5] Fix documentation nit --- packages/webview_flutter/lib/webview_flutter.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 3ce395e63118..a2ba394eb423 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -207,7 +207,7 @@ class WebViewController { /// Accessor to the current URL that the WebView is displaying. /// - /// If [WebView.initialUrl] was never specified, or if , currentUrl returns `null`. + /// If [WebView.initialUrl] was never specified, returns `null`. /// Note that this operation is asynchronous, and it is possible that the /// current URL changes again by the time this function returns (in other /// words, by the time this future completes, the WebView may be displaying a