Welcome to another Webview tutorial,
In this tutorial we will see what are the other API’s available in the webview_flutter plugin. Read the part-1 of this tutorial to get an introduction on webview in flutter.
Watch Video Tutorial
We will be implementing the below functionalities in our webview.
- Show User Agent
- List Cookies
- Clear Cookies
- Add To Cache
- List Cache
- Clear Cache
- Navigation Delegate
First we will write the functions that implements all the above functionalities
Show User Agent
void _onShowUserAgent( WebViewController controller, BuildContext context) async { // Send a message with the user agent string to the Toaster JavaScript channel we registered // with the WebView. controller.evaluateJavascript( 'Toaster.postMessage("User Agent: " + navigator.userAgent);'); }
List Cookies
void _onListCookies( WebViewController controller, BuildContext context) async { final String cookies = await controller.evaluateJavascript('document.cookie'); Scaffold.of(context).showSnackBar(SnackBar( content: Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: <Widget>[ const Text('Cookies:'), _getCookieList(cookies), ], ), )); }
Clear Cookies
void _onClearCookies(BuildContext context) async { final bool hadCookies = await cookieManager.clearCookies(); String message = 'There were cookies. Now, they are gone!'; if (!hadCookies) { message = 'There are no cookies.'; } Scaffold.of(context).showSnackBar(SnackBar( content: Text(message), )); }
Add To Cache
void _onAddToCache(WebViewController controller, BuildContext context) async { await controller.evaluateJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";'); Scaffold.of(context).showSnackBar(const SnackBar( content: Text('Added a test entry to cache.'), )); }
List Cache
void _onListCache(WebViewController controller, BuildContext context) async { await controller.evaluateJavascript('caches.keys()' '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Toaster.postMessage(caches))'); }
Clear Cache
void _onClearCache(WebViewController controller, BuildContext context) async { await controller.clearCache(); Scaffold.of(context).showSnackBar(const SnackBar( content: Text("Cache cleared."), )); }
Navigation Delegate
Here we will listen for each webview navigation and we can block or modify the url based on the request.
void _onNavigationDelegateExample( WebViewController controller, BuildContext context) async { final String contentBase64 = base64Encode(const Utf8Encoder().convert(kNavigationExamplePage)); controller.loadUrl('data:text/html;base64,$contentBase64'); }
Get Cookies List
Widget _getCookieList(String cookies) { if (cookies == null || cookies == '""') { return Container(); } final List<String> cookieList = cookies.split(';'); final Iterable<Text> cookieWidgets = cookieList.map((String cookie) => Text(cookie)); return Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: cookieWidgets.toList(), ); } }
Navigation Delegate implementation
@override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('Webview Demo'), actions: <Widget>[ NavigationControls(_controller.future), ], ), body: Builder( builder: (BuildContext context) { return WebView( initialUrl: 'https://flutter.dev', javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController webViewController) { _controller.complete(webViewController); }, javascriptChannels: <JavascriptChannel>[ snackbarJavascriptChannel(context), ].toSet(), navigationDelegate: (NavigationRequest request) { return NavigationDecision.navigate; }, ); }, ), ...
Complete Code
import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'dart:async'; import 'dart:convert'; const String examplepage = ''' <!DOCTYPE html><html> <head><title>Navigation Delegate Example</title></head> <body> <p> The navigation delegate is set to block navigation to the youtube website. </p> <ul> <ul><a href="https://www.youtube.com/">https://www.youtube.com/</a></ul> <ul><a href="https://www.google.com/">https://www.google.com/</a></ul> </ul> </body> </html> '''; class WebviewDemo extends StatefulWidget { final String title; WebviewDemo({Key key, this.title}) : super(key: key); @override State<StatefulWidget> createState() { return _WebviewDemoState(); } } JavascriptChannel snackbarJavascriptChannel(BuildContext context) { return JavascriptChannel( name: 'SnackbarJSChannel', onMessageReceived: (JavascriptMessage message) { Scaffold.of(context).showSnackBar(SnackBar( content: Text(message.message), )); }, ); } class _WebviewDemoState extends State<WebviewDemo> { // final Completer<WebViewController> _controller = Completer<WebViewController>(); @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('Webview Demo'), actions: <Widget>[ NavigationControls(_controller.future), SampleMenu(_controller.future) ], ), body: Builder( builder: (BuildContext context) { return WebView( initialUrl: 'http://flutter.dev', javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController webViewController) { _controller.complete(webViewController); }, javascriptChannels: <JavascriptChannel>[ snackbarJavascriptChannel(context), ].toSet(), navigationDelegate: (NavigationRequest request) { if (request.url.startsWith("https://www.youtube.com")) { print("Blocking navigation"); return NavigationDecision.prevent; } return NavigationDecision.navigate; }, ); }, ), ); } } enum MenuOptions { showUserAgent, listCookies, clearCookies, addToCache, listCache, clearCache, navigationDelegate } class SampleMenu extends StatelessWidget { SampleMenu(this.controller); final Future<WebViewController> controller; final CookieManager cookieManager = CookieManager(); @override Widget build(BuildContext context) { return FutureBuilder<WebViewController>( future: controller, builder: (BuildContext context, AsyncSnapshot<WebViewController> controller) { return PopupMenuButton<MenuOptions>( itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[ PopupMenuItem( value: MenuOptions.showUserAgent, child: const Text('Show User Agent'), enabled: controller.hasData, ), PopupMenuItem( value: MenuOptions.listCookies, child: const Text('List Cookies'), ), PopupMenuItem( value: MenuOptions.clearCookies, child: const Text('Clear Cookies'), ), PopupMenuItem( value: MenuOptions.addToCache, child: const Text('Add to Cache'), ), PopupMenuItem( value: MenuOptions.listCache, child: const Text('List Cache'), ), PopupMenuItem( value: MenuOptions.clearCache, child: const Text('Clear Cache'), ), PopupMenuItem( value: MenuOptions.navigationDelegate, child: const Text('Navigation Delegate Demo'), ) ], onSelected: (MenuOptions value) { switch (value) { case MenuOptions.showUserAgent: showUserAgent(controller.data, context); break; case MenuOptions.listCookies: listCookies(controller.data, context); break; case MenuOptions.clearCookies: clearCookies(controller.data, context); break; case MenuOptions.addToCache: addToCache(controller.data, context); break; case MenuOptions.listCache: listCache(controller.data, context); break; case MenuOptions.clearCache: clearCache(controller.data, context); break; case MenuOptions.navigationDelegate: navigationDelegateDemo(controller.data, context); break; default: } }, ); }, ); } navigationDelegateDemo( WebViewController controller, BuildContext context) async { final String contentbase64 = base64Encode(const Utf8Encoder().convert(examplepage)); controller.loadUrl('data:text/html;base64,$contentbase64'); } addToCache(WebViewController controller, BuildContext context) async { await controller.evaluateJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";'); Scaffold.of(context).showSnackBar( const SnackBar( content: Text('Added a test entry to cache'), ), ); } void listCache(WebViewController controller, BuildContext context) async { await controller.evaluateJavascript( 'caches.keys().then((cacheKeys) => JSON.stringify({"cacheKeys": cacheKeys, "localStorage": localStorage })).then((caches) => SnackbarJSChannel.postMessage(caches))'); } void clearCache(WebViewController controller, BuildContext context) async { await controller.clearCache(); Scaffold.of(context).showSnackBar( const SnackBar( content: Text('Cache Cleared'), ), ); } listCookies(WebViewController controller, BuildContext context) async { final String cookies = await controller.evaluateJavascript('document.cookie'); Scaffold.of(context).showSnackBar( SnackBar( content: Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: <Widget>[const Text('Cookies:'), getCookies(cookies)], ), ), ); } Widget getCookies(String cookies) { if (null == cookies || cookies.isEmpty) { return Container(); } final List<String> cookieList = cookies.split(';'); final Iterable<Text> cookieWidgets = cookieList.map( (String cookie) => Text(cookie), ); return Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: cookieWidgets.toList(), ); } void clearCookies(WebViewController controller, BuildContext context) async { final bool hadCookies = await cookieManager.clearCookies(); String message = 'There are no cookies'; Scaffold.of(context).showSnackBar( SnackBar( content: Text(message), ), ); } showUserAgent(WebViewController controller, BuildContext context) { controller.evaluateJavascript( 'SnackbarJSChannel.postMessage("User Agent: " + navigator.userAgent);'); } } class NavigationControls extends StatelessWidget { const NavigationControls(this._webViewControllerFuture); final Future<WebViewController> _webViewControllerFuture; @override Widget build(BuildContext context) { return FutureBuilder( future: _webViewControllerFuture, builder: (BuildContext context, AsyncSnapshot<WebViewController> snapshot) { final bool webViewReady = snapshot.connectionState == ConnectionState.done; final WebViewController controller = snapshot.data; return Row( children: <Widget>[ IconButton( icon: const Icon(Icons.arrow_back_ios), onPressed: !webViewReady ? null : () async { if (await controller.canGoBack()) { controller.goBack(); } else { Scaffold.of(context).showSnackBar( const SnackBar( content: Text("No Back history Item"), ), ); } }, ), IconButton( icon: const Icon(Icons.arrow_forward_ios), onPressed: !webViewReady ? null : () async { if (await controller.canGoForward()) { controller.goForward(); } else { Scaffold.of(context).showSnackBar( const SnackBar( content: Text("No Forward history Item"), ), ); } }, ), IconButton( icon: const Icon(Icons.refresh), onPressed: !webViewReady ? null : () async { controller.reload(); }, ) ], ); }, ); } }