webview_flutter_ext 0.3.25 webview_flutter_ext: ^0.3.25 copied to clipboard
A Flutter plugin that provides a WebView widget on Android and iOS.
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MaterialApp(home: WebViewExample()));
const String kNavigationExamplePage = '''
<!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 WebViewExample extends StatefulWidget {
@override
_WebViewExampleState createState() => _WebViewExampleState();
}
String text="""
data:text/html;charset=utf-8,%3C!DOCTYPE%20html%3E%0A%3Chtml%3E%0A%3Cstyle%3E%0A%20%20%20%20*%20%7B%0A%20%20%20%20%20%20%20%20padding:%200;%0A%20%20%20%20%20%20%20%20margin:%200;%0A%20%20%20%20%7D%0A%20%20%20%20body%7B%0A%20%20%20%20%20%20%20%20background-color:%20%23000000;%0A%20%20%20%20%7D%0A%20%20%20%20.container%20%7B%0A%20%20%20%20%20%20%20%20position:%20absolute;%0A%20%20%20%20%20%20%20%20width:%20100%25;%0A%20%20%20%20%20%20%20%20height:%200;%0A%20%20%20%20%20%20%20%20top:%200;%0A%20%20%20%20%20%20%20%20left:%200;%0A%20%20%20%20%20%20%20%20right:0;%0A%20%20%20%20%20%20%20%20bottom:0;%0A%20%20%20%20%20%20%20%20background-color:%20%23000000;%0A%20%20%20%20%20%20%20%20padding-bottom:%2056.25%25;%0A%20%20%20%20%20%20%20%20margin:auto;%0A%20%20%20%20%7D%0A%0A%20%20%20%20.video%20%7B%0A%20%20%20%20%20%20%20%20position:%20absolute;%0A%20%20%20%20%20%20%20%20width:%20100%25;%0A%20%20%20%20%20%20%20%20height:%20100%25;%0A%20%20%20%20%7D%0A%3C/style%3E%0A%0A%3Cbody%3E%0A%3Cdiv%20class=%22container%22%3E%0A%20%20%20%20%3Ciframe%20class=%22video%22%20id=%22player%22%20src=%22https://www.youtube.com/embed/E0J3S2KDJOA?enablejsapi=1&controls=1&rel=0&autoplay=1%22%20frameborder=%220%22%0A%20%20%20%20%20%20%20%20%20%20%20%20allowfullscreen%3E%0A%20%20%20%20%3C/iframe%3E%0A%3C/div%3E%0A%3Cscript%20type=%22text/javascript%22%20src=%22https://www.youtube.com/iframe_api%22%3E%3C/script%3E%0A%3Cscript%3E%0A%20%20%20%20%20%20%20%20var%20player;%0A%20%20%20%20%20%20%20%20function%20onYouTubeIframeAPIReady()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20player%20=%20new%20YT.Player('player',%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20events:%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'onReady':%20onPlayerReady,%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'onStateChange':%20onPlayerStateChange%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D);%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20function%20onPlayerReady(event)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20handler.postMessage(%22ready%22)%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20function%20onPlayerStateChange(event)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(event.data%20==%20YT.PlayerState.PLAYING)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20handler.postMessage(%22start%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7Delse%20if(event.data%20==%20YT.PlayerState.ENDED)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20handler.postMessage(%22end%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7Delse%20if(event.data%20==%20YT.PlayerState.PAUSED)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20handler.postMessage(%22pause%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20function%20playVideo()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20player.playVideo();%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20function%20playPause()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20player.pauseVideo();%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20function%20stopVideo()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20player.stopVideo();%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20function%20setProgress(seconds)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20player.seekTo(seconds,true)%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20function%20getProgress()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20player.getCurrentTime();%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%3C/script%3E%0A%3C/body%3E%0A%3C/html%3E
""";
class _WebViewExampleState extends State<WebViewExample> {
final Completer<WebViewController> _controller =
Completer<WebViewController>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView example'),
// This drop down menu demonstrates that Flutter widgets can be shown over the web view.
actions: <Widget>[
NavigationControls(_controller.future),
SampleMenu(_controller.future),
],
),
// 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: 'about:blank',
initialMediaPlaybackPolicy:AutoMediaPlaybackPolicy.always_allow ,
supportVideoFullScreen: true,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
webViewController.loadUrl(text);
},
debuggingEnabled: false,
onScrollToBottom: (){
print('================>Scroll to bottom');
},
onScrollToTop: (){
print('================>Scroll to top');
},
scrollCallback: (dy){
print('================>Scroll to $dy');
},
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
// ignore: prefer_collection_literals
javascriptChannels: <JavascriptChannel>[
_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');
},
gestureNavigationEnabled: true,
);
}),
floatingActionButton: favoriteButton(),
);
}
JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toaster',
onMessageReceived: (JavascriptMessage message) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(message.message)),
);
});
}
Widget favoriteButton() {
return FutureBuilder<WebViewController>(
future: _controller.future,
builder: (BuildContext context,
AsyncSnapshot<WebViewController> controller) {
if (controller.hasData) {
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),
);
}
return Container();
});
}
}
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>(
onSelected: (MenuOptions value) {
switch (value) {
case MenuOptions.showUserAgent:
_onShowUserAgent(controller.data, context);
break;
case MenuOptions.listCookies:
_onListCookies(controller.data, context);
break;
case MenuOptions.clearCookies:
_onClearCookies(context);
break;
case MenuOptions.addToCache:
_onAddToCache(controller.data, context);
break;
case MenuOptions.listCache:
_onListCache(controller.data, context);
break;
case MenuOptions.clearCache:
_onClearCache(controller.data, context);
break;
case MenuOptions.navigationDelegate:
_onNavigationDelegateExample(controller.data, context);
break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
PopupMenuItem<MenuOptions>(
value: MenuOptions.showUserAgent,
child: const Text('Show user agent'),
enabled: controller.hasData,
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCookies,
child: Text('List cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCookies,
child: Text('Clear cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.addToCache,
child: Text('Add to cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCache,
child: Text('List cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCache,
child: Text('Clear cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.navigationDelegate,
child: Text('Navigation Delegate example'),
),
],
);
},
);
}
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.
await controller.evaluateJavascript(
'Toaster.postMessage("User Agent: " + navigator.userAgent);');
}
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),
],
),
));
}
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.'),
));
}
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))');
}
void _onClearCache(WebViewController controller, BuildContext context) async {
await controller.clearCache();
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text("Cache cleared."),
));
}
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),
));
}
void _onNavigationDelegateExample(
WebViewController controller, BuildContext context) async {
final String contentBase64 =
base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
await controller.loadUrl('data:text/html;base64,$contentBase64');
}
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(),
);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture)
: assert(_webViewControllerFuture != null);
final Future<WebViewController> _webViewControllerFuture;
@override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
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()) {
await controller.goBack();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("No back history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: !webViewReady
? null
: () async {
if (await controller.canGoForward()) {
await controller.goForward();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(
content: Text("No forward history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.replay),
onPressed: !webViewReady
? null
: () {
controller.reload();
},
),
],
);
},
);
}
}