Flutter网页视图展示插件flutter_webview_plugin_ios_android的使用
Flutter网页视图展示插件flutter_webview_plugin_ios_android的使用
Flutter WebView 插件
关于
我是 Siddhant Parashar,我正在发布此软件包作为从 https://pub.dartlang.org/packages/flutter_webview_plugin 分叉出来的版本。
这个分叉解决了 Android 和 iOS 的两个主要问题:
- 在 Android 上,已经修复了旧嵌入问题。
- 在 iOS 上,webview 没有保留会话,这导致用户每次渲染 webview 时都会自动登出。这个问题在 iOS 上得到了重大修复。
赞助
请我喝杯咖啡吧! https://www.buymeacoffee.com/siddhant12p
关于插件
允许 Flutter 与原生 WebView 进行通信的插件。
注意:
WebView 不集成到小部件树中,它是一个位于 Flutter 视图之上的原生视图。 你将无法看到可能会与 webview 占用的屏幕区域重叠的 snackbars、对话框或其他 Flutter 小部件。
getSafeAcceptedType()
函数仅适用于最低 SDK 为 21。
eval()
函数仅支持 SDK 为 19 或更高版本以评估 JavaScript。
开始使用
有关如何开始使用 Flutter 的帮助,请参阅我们的在线文档 http://flutter.io/。
iOS
为了使插件正常工作,你需要向 ios/Runner/Info.plist
添加新的键:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
NSAllowsArbitraryLoadsInWebContent
用于 iOS 10+,而 NSAllowsArbitraryLoads
用于 iOS 9。
它是如何工作的
全屏启动 WebView 并使用 Flutter 导航
new MaterialApp(
routes: {
"/": (_) => new WebviewScaffold(
url: "https://www.google.com",
appBar: new AppBar(
title: new Text("Widget webview"),
),
),
},
);
可选参数 hidden
和 initialChild
可供使用,以便在页面加载期间显示其他内容。
如果设置 hidden
为 true,则会显示默认的 CircularProgressIndicator
。如果你另外指定了一个 Widget 作为 initialChild
,你可以显示任何你喜欢的内容直到页面加载完成。
例如,以下代码将显示一个带有文本 ‘waiting…’ 的红色屏幕。
return new MaterialApp(
title: 'Flutter WebView Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/': (_) => const MyHomePage(title: 'Flutter WebView Demo'),
'/widget': (_) => new WebviewScaffold(
url: selectedUrl,
appBar: new AppBar(
title: const Text('Widget webview'),
),
withZoom: true,
withLocalStorage: true,
hidden: true,
initialChild: Container(
color: Colors.redAccent,
child: const Center(
child: Text('Waiting.....'),
),
),
),
},
);
FlutterWebviewPlugin
提供一个与唯一 webview 相关联的单例实例,因此你可以在应用的任何地方控制 webview。
监听事件:
final flutterWebviewPlugin = new FlutterWebviewPlugin();
flutterWebviewPlugin.onUrlChanged.listen((String url) {
});
监听 WebView 中的滚动事件
final flutterWebviewPlugin = new FlutterWebviewPlugin();
flutterWebviewPlugin.onScrollYChanged.listen((double offsetY) { // 最新的垂直滚动偏移值
// 在这里比较垂直滚动变化与旧值
});
flutterWebviewPlugin.onScrollXChanged.listen((double offsetX) { // 最新的水平滚动偏移值
// 在这里比较水平滚动变化与旧值
});
注意:请注意,iOS 和 Android 之间的滚动距离略有不同。Android 的滚动值差异通常比 iOS 设备大。
隐藏 WebView
final flutterWebviewPlugin = new FlutterWebviewPlugin();
flutterWebviewPlugin.launch(url, hidden: true);
关闭已启动的 WebView
flutterWebviewPlugin.close();
在自定义矩形内显示 WebView
final flutterWebviewPlugin = new FlutterWebviewPlugin();
flutterWebviewPlugin.launch(url,
fullScreen: false,
rect: new Rect.fromLTWH(
0.0,
0.0,
MediaQuery.of(context).size.width,
300.0,
),
);
向 WebView 注入自定义代码
使用 flutterWebviewPlugin.evalJavaScript(String code)
。这个函数必须在页面加载完成后运行(即监听 onStateChanged
事件,当状态为 finishLoad
时)。
如果你有大量的 JavaScript 需要嵌入,可以使用资源文件。将资源文件添加到 pubspec.yaml
,然后像这样调用函数:
Future<String> loadJS(String name) async {
var givenJS = rootBundle.loadString('assets/$name.js');
return givenJS.then((String js) {
flutterWebViewPlugin.onStateChanged.listen((viewState) async {
if (viewState.type == WebViewState.finishLoad) {
flutterWebViewPlugin.evalJavascript(js);
}
});
});
}
访问文件系统中的本地文件
在 launch
函数或 Webview
架构中将 withLocalUrl
选项设置为 true 以启用对本地 URL 的支持。
注意,在 iOS 上,还需要将 localUrlScope
选项设置为目录路径。所有位于此文件夹(或子文件夹)内的文件都将被允许访问。如果省略,只有正在打开的本地文件将被允许访问,导致无法加载子资源。该选项在 Android 上被忽略。
忽略 SSL 错误
将 ignoreSSLErrors
选项设置为 true 以显示来自通常不受 WebView 信任的服务器(如自签名证书)的内容。
注意:
不要在生产环境中使用此功能。
注意,对于 iOS,你还需要向 ios/Runner/Info.plist
添加新的键:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
NSAllowsArbitraryLoadsInWebContent
用于 iOS 10+,而 NSAllowsArbitraryLoads
用于 iOS 9。
否则,你仍然无法显示来自具有不受信任证书的页面的内容。
你可以通过 https://self-signed.badssl.com/ 测试你的 SSL 证书忽略是否有效。
WebView 事件
Stream<Null>
onDestroyStream<String>
onUrlChangedStream<WebViewStateChanged>
onStateChangedStream<double>
onScrollXChangedStream<double>
onScrollYChangedStream<String>
onError
不要忘记释放 WebView
flutterWebviewPlugin.dispose()
WebView 函数
Future<Null> launch(String url, {
Map<String, String> headers: null,
Set<JavascriptChannel> javascriptChannels: null,
bool withJavascript: true,
bool clearCache: false,
bool clearCookies: false,
bool hidden: false,
bool enableAppScheme: true,
Rect rect: null,
String userAgent: null,
bool withZoom: false,
bool displayZoomControls: false,
bool withLocalStorage: true,
bool withLocalUrl: true,
String localUrlScope: null,
bool withOverviewMode: false,
bool scrollBar: true,
bool supportMultipleWindows: false,
bool appCacheEnabled: false,
bool allowFileURLs: false,
bool useWideViewPort: false,
String invalidUrlRegex: null,
bool geolocationEnabled: false,
bool debuggingEnabled: false,
bool ignoreSSLErrors: false,
});
Future<String> evalJavascript(String code);
Future<Map<String, dynamic>> getCookies();
Future<Null> cleanCookies();
Future<Null> resize(Rect rect);
Future<Null> show();
Future<Null> hide();
Future<Null> reloadUrl(String url);
Future<Null> close();
Future<Null> reload();
Future<Null> goBack();
Future<Null> goForward();
Future<Null> stopLoading();
Future<bool> canGoBack();
Future<bool> canGoForward();
示例代码
main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin_ios_android/flutter_webview_plugin_ios_android.dart';
const kAndroidUserAgent =
'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36';
String selectedUrl = 'https://flutter.io';
// ignore: prefer_collection_literals
final Set<JavascriptChannel> jsChannels = [
JavascriptChannel(
name: 'Print',
onMessageReceived: (message) {
print(message.message);
}),
].toSet();
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final flutterWebViewPlugin = FlutterWebviewPlugin();
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter WebView Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/': (_) => MyHomePage(title: 'Flutter WebView Demo'),
'/widget': (_) {
return WebviewScaffold(
url: selectedUrl,
javascriptChannels: jsChannels,
mediaPlaybackRequiresUserGesture: false,
appBar: AppBar(
title: const Text('Widget WebView'),
),
withZoom: true,
withLocalStorage: true,
hidden: true,
initialChild: Container(
color: Colors.redAccent,
child: const Center(
child: Text('Waiting.....'),
),
),
bottomNavigationBar: BottomAppBar(
child: Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
flutterWebViewPlugin.goBack();
},
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: () {
flutterWebViewPlugin.goForward();
},
),
IconButton(
icon: const Icon(Icons.autorenew),
onPressed: () {
flutterWebViewPlugin.reload();
},
),
],
),
),
);
},
},
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
[@override](/user/override)
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// WebView 插件实例
final flutterWebViewPlugin = FlutterWebviewPlugin();
// 销毁流订阅
late StreamSubscription _onDestroy;
// URL 改变流订阅
late StreamSubscription<String> _onUrlChanged;
// 状态改变流订阅
late StreamSubscription<WebViewStateChanged> _onStateChanged;
late StreamSubscription<WebViewHttpError> _onHttpError;
late StreamSubscription<double> _onProgressChanged;
late StreamSubscription<double> _onScrollYChanged;
late StreamSubscription<double> _onScrollXChanged;
final _urlCtrl = TextEditingController(text: selectedUrl);
final _codeCtrl = TextEditingController(text: 'window.navigator.userAgent');
final _scaffoldKey = GlobalKey<ScaffoldState>();
final _history = [];
[@override](/user/override)
void initState() {
super.initState();
flutterWebViewPlugin.close();
_urlCtrl.addListener(() {
selectedUrl = _urlCtrl.text;
});
// 添加销毁 WebView 的监听器,以便你可以执行某些操作。
_onDestroy = flutterWebViewPlugin.onDestroy.listen((_) {
if (mounted) {
// 执行一些动作,例如显示信息提示。
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: const Text('Webview Destroyed')));
}
});
// 添加 URL 改变的监听器
_onUrlChanged = flutterWebViewPlugin.onUrlChanged.listen((String url) {
if (mounted) {
setState(() {
_history.add('onUrlChanged: $url');
});
}
});
_onProgressChanged =
flutterWebViewPlugin.onProgressChanged.listen((double progress) {
if (mounted) {
setState(() {
_history.add('onProgressChanged: $progress');
});
}
});
_onScrollYChanged =
flutterWebViewPlugin.onScrollYChanged.listen((double y) {
if (mounted) {
setState(() {
_history.add('Scroll in Y Direction: $y');
});
}
});
_onScrollXChanged =
flutterWebViewPlugin.onScrollXChanged.listen((double x) {
if (mounted) {
setState(() {
_history.add('Scroll in X Direction: $x');
});
}
});
_onStateChanged =
flutterWebViewPlugin.onStateChanged.listen((WebViewStateChanged state) {
if (mounted) {
setState(() {
_history.add('onStateChanged: ${state.type} ${state.url}');
});
}
});
_onHttpError =
flutterWebViewPlugin.onHttpError.listen((WebViewHttpError error) {
if (mounted) {
setState(() {
_history.add('onHttpError: ${error.code} ${error.url}');
});
}
});
}
[@override](/user/override)
void dispose() {
// 取消所有监听器,同样也需要取消这个流。
_onDestroy.cancel();
_onUrlChanged.cancel();
_onStateChanged.cancel();
_onHttpError.cancel();
_onProgressChanged.cancel();
_onScrollXChanged.cancel();
_onScrollYChanged.cancel();
flutterWebViewPlugin.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(24.0),
child: TextField(controller: _urlCtrl),
),
ElevatedButton(
onPressed: () {
flutterWebViewPlugin.launch(
selectedUrl,
geolocationEnabled: true,
rect: Rect.fromLTWH(
0.0, 0.0, MediaQuery.of(context).size.width, 300.0),
userAgent: kAndroidUserAgent,
invalidUrlRegex:
r'^(https).+(twitter)', // 防止点击 flutter 网站上的 twitter 图标时重定向到 twitter
);
},
child: const Text('Open Webview (rect)'),
),
ElevatedButton(
onPressed: () {
flutterWebViewPlugin.launch(
selectedUrl,
hidden: true,
geolocationEnabled: true,
);
},
child: const Text('Open "hidden" Webview'),
),
ElevatedButton(
onPressed: () {
flutterWebViewPlugin.launch(selectedUrl,
geolocationEnabled: true,
);
},
child: const Text('Open Fullscreen Webview'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed('/widget');
},
child: const Text('Open widget webview'),
),
Container(
padding: const EdgeInsets.all(24.0),
child: TextField(controller: _codeCtrl),
),
ElevatedButton(
onPressed: () {
final future =
flutterWebViewPlugin.evalJavascript(_codeCtrl.text);
future.then((result) {
setState(() {
_history.add('eval: $result');
});
});
},
child: const Text('Eval some javascript'),
),
ElevatedButton(
onPressed: () {
final future = flutterWebViewPlugin
.evalJavascript('alert("Hello World");');
future.then((result) {
setState(() {
_history.add('eval: $result');
});
});
},
child: const Text('Eval javascript alert()'),
),
ElevatedButton(
onPressed: () {
setState(() {
_history.clear();
});
flutterWebViewPlugin.close();
},
child: const Text('Close'),
),
ElevatedButton(
onPressed: () {
flutterWebViewPlugin.getCookies().then((m) {
setState(() {
_history.add('cookies: $m');
});
});
},
child: const Text('Cookies'),
),
Text(_history.join('\n'))
],
),
),
);
}
}
更多关于Flutter网页视图展示插件flutter_webview_plugin_ios_android的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter网页视图展示插件flutter_webview_plugin_ios_android的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
flutter_webview_plugin_ios_android
是一个用于在 Flutter 应用中嵌入 WebView 的插件。它允许你在应用中展示网页内容,并且支持 iOS 和 Android 平台。虽然它可能不是官方推荐的 WebView 插件(官方推荐使用 webview_flutter
插件),但它仍然是一个可选的解决方案。
安装插件
首先,你需要在 pubspec.yaml
文件中添加 flutter_webview_plugin_ios_android
插件的依赖:
dependencies:
flutter:
sdk: flutter
flutter_webview_plugin_ios_android: ^0.0.1 # 请检查最新版本
然后,运行 flutter pub get
来安装插件。
基本使用
以下是一个简单的示例,展示如何在 Flutter 应用中使用 flutter_webview_plugin_ios_android
插件来展示一个网页:
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin_ios_android/flutter_webview_plugin_ios_android.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter WebView Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: WebViewExample(),
);
}
}
class WebViewExample extends StatelessWidget {
final flutterWebviewPlugin = FlutterWebviewPlugin();
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter WebView Example'),
),
body: WebviewScaffold(
url: 'https://www.example.com',
withZoom: true,
withLocalStorage: true,
hidden: true,
initialChild: Center(
child: CircularProgressIndicator(),
),
),
);
}
}