Flutter WebView插件webview_flutter_tizen的使用
Flutter WebView插件webview_flutter_tizen的使用
webview_flutter_tizen
Tizen 版本的 webview_flutter
插件,适用于 Tizen TV 设备。
WebView 组件在 Tizen 上基于 EFL WebKit (EWK)。
必需权限
要使用此插件,你需要在 tizen-manifest.xml
文件的 <manifest>
部分添加以下行:
<privileges>
<privilege>http://tizen.org/privilege/internet</privilege>
</privileges>
使用方法
该包不是 webview_flutter
的官方实现。因此,你必须同时包含 webview_flutter
和 webview_flutter_tizen
作为依赖项。
在 pubspec.yaml
文件中添加以下依赖项:
dependencies:
webview_flutter: ^4.4.2
webview_flutter_tizen: ^0.9.2
示例
下面是一个完整的示例,展示了如何在 Tizen TV 设备上使用 webview_flutter_tizen
。
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_tizen/webview_flutter_tizen.dart';
class WebViewExample extends StatefulWidget {
const WebViewExample({super.key});
@override
State<WebViewExample> createState() => _WebViewExampleState();
}
class _WebViewExampleState extends State<WebViewExample> {
final WebViewController _controller = WebViewController();
@override
void initState() {
super.initState();
// 设置 WebView 的配置
_controller
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
debugPrint('WebView is loading (progress : $progress%)');
},
onPageStarted: (String url) {
debugPrint('Page started loading: $url');
},
onPageFinished: (String url) {
debugPrint('Page finished loading: $url');
},
onWebResourceError: (WebResourceError error) {
debugPrint('''
Page resource error:
code: ${error.errorCode}
description: ${error.description}
errorType: ${error.errorType}
isForMainFrame: ${error.isForMainFrame}
''');
},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
debugPrint('blocking navigation to ${request.url}');
return NavigationDecision.prevent;
}
debugPrint('allowing navigation to ${request.url}');
return NavigationDecision.navigate;
},
onUrlChange: (UrlChange change) {
debugPrint('url change to ${change.url}');
},
),
)
..addJavaScriptChannel(
'Toaster',
onMessageReceived: (JavaScriptMessage message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message.message)),
);
},
);
// 加载初始页面
_controller.loadRequest(Uri.parse('https://flutter.dev'));
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.green,
appBar: AppBar(
title: const Text('Flutter WebView 示例'),
// 这个下拉菜单演示了 Flutter 小部件可以在 WebView 上方显示。
actions: <Widget>[
NavigationControls(webViewController: _controller),
SampleMenu(webViewController: _controller),
],
),
body: WebViewWidget(controller: _controller),
floatingActionButton: favoriteButton(),
);
}
Widget favoriteButton() {
return FloatingActionButton(
onPressed: () async {
final String? url = await _controller.currentUrl();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('收藏 $url')),
);
}
},
child: const Icon(Icons.favorite),
);
}
}
enum MenuOptions {
showUserAgent,
listCookies,
clearCookies,
addToCache,
listCache,
clearCache,
navigationDelegate,
doPostRequest,
loadLocalFile,
loadFlutterAsset,
loadHtmlString,
transparentBackground,
setCookie,
logExample,
}
class SampleMenu extends StatelessWidget {
SampleMenu({
super.key,
required this.webViewController,
});
final WebViewController webViewController;
late final WebViewCookieManager cookieManager = WebViewCookieManager();
@override
Widget build(BuildContext context) {
return PopupMenuButton<MenuOptions>(
key: const ValueKey<String>('ShowPopupMenu'),
onSelected: (MenuOptions value) {
switch (value) {
case MenuOptions.showUserAgent:
_onShowUserAgent();
case MenuOptions.listCookies:
_onListCookies(context);
case MenuOptions.clearCookies:
_onClearCookies(context);
case MenuOptions.addToCache:
_onAddToCache(context);
case MenuOptions.listCache:
_onListCache();
case MenuOptions.clearCache:
_onClearCache(context);
case MenuOptions.navigationDelegate:
_onNavigationDelegateExample();
case MenuOptions.doPostRequest:
_onDoPostRequest();
case MenuOptions.loadLocalFile:
_onLoadLocalFileExample();
case MenuOptions.loadFlutterAsset:
_onLoadFlutterAssetExample();
case MenuOptions.loadHtmlString:
_onLoadHtmlStringExample();
case MenuOptions.transparentBackground:
_onTransparentBackground();
case MenuOptions.setCookie:
_onSetCookie();
case MenuOptions.logExample:
_onLogExample();
}
},
itemBuilder: (BuildContext context) =>
<PopupMenuItem<MenuOptions>>[
const PopupMenuItem<MenuOptions>(
value: MenuOptions.showUserAgent,
child: Text('显示用户代理'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCookies,
child: Text('列出 Cookie'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCookies,
child: Text('清除 Cookie'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.addToCache,
child: Text('添加到缓存'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCache,
child: Text('列出缓存'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCache,
child: Text('清除缓存'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.navigationDelegate,
child: Text('导航委托示例'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.doPostRequest,
child: Text('POST 请求'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.loadHtmlString,
child: Text('加载 HTML 字符串'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.loadLocalFile,
child: Text('加载本地文件'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.loadFlutterAsset,
child: Text('加载 Flutter 资源'),
),
const PopupMenuItem<MenuOptions>(
key: ValueKey<String>('ShowTransparentBackgroundExample'),
value: MenuOptions.transparentBackground,
child: Text('透明背景示例'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.setCookie,
child: Text('设置 Cookie'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.logExample,
child: Text('日志示例'),
),
],
);
}
Future<void> _onShowUserAgent() {
// 发送一条消息到 JavaScript 通道以获取用户代理字符串
return webViewController.runJavaScript(
'Toaster.postMessage("User Agent: " + navigator.userAgent);',
);
}
Future<void> _onListCookies(BuildContext context) async {
final String cookies = await webViewController
.runJavaScriptReturningResult('document.cookie') as String;
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Cookie:'),
_getCookieList(cookies),
],
),
));
}
}
Future<void> _onAddToCache(BuildContext context) async {
await webViewController.runJavaScript(
'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";',
);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('已添加测试条目到缓存'),
));
}
}
Future<void> _onListCache() {
return webViewController.runJavaScript('caches.keys()'
// 忽略: 相邻字符串之间缺少空格
'.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
'.then((caches) => Toaster.postMessage(caches))');
}
Future<void> _onClearCache(BuildContext context) async {
await webViewController.clearCache();
// 此功能在 webview_flutter_tizen 中未实现。
// await webViewController.clearLocalStorage();
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('缓存已清除'),
));
}
}
Future<void> _onClearCookies(BuildContext context) async {
final bool hadCookies = await cookieManager.clearCookies();
String message = '有 Cookie。现在,它们已经清除了!';
if (!hadCookies) {
message = '没有 Cookie。';
}
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(message),
));
}
}
Future<void> _onNavigationDelegateExample() {
final String contentBase64 = base64Encode(
const Utf8Encoder().convert(kNavigationExamplePage),
);
return webViewController.loadRequest(
Uri.parse('data:text/html;base64,$contentBase64'),
);
}
Future<void> _onSetCookie() async {
await cookieManager.setCookie(
const WebViewCookie(
name: 'foo',
value: 'bar',
domain: 'httpbin.org',
path: '/anything',
),
);
await webViewController.loadRequest(Uri.parse(
'https://httpbin.org/anything',
));
}
Future<void> _onDoPostRequest() {
return webViewController.loadRequest(
Uri.parse('https://httpbin.org/post'),
method: LoadRequestMethod.post,
headers: {'foo': 'bar', 'Content-Type': 'text/plain'},
body: Uint8List.fromList('Test Body'.codeUnits),
);
}
Future<void> _onLoadLocalFileExample() async {
final String pathToIndex = await _prepareLocalFile();
await webViewController.loadFile(pathToIndex);
}
Future<void> _onLoadFlutterAssetExample() {
return webViewController.loadFlutterAsset('assets/www/index.html');
}
Future<void> _onLoadHtmlStringExample() {
return webViewController.loadHtmlString(kLocalExamplePage);
}
Future<void> _onTransparentBackground() {
return webViewController.loadHtmlString(kTransparentBackgroundPage);
}
Widget _getCookieList(String cookies) {
if (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(),
);
}
static Future<String> _prepareLocalFile() async {
final String tmpDir = (await getTemporaryDirectory()).path;
final File indexFile = File(
<String>{tmpDir, 'www', 'index.html'}.join(Platform.pathSeparator));
await indexFile.create(recursive: true);
await indexFile.writeAsString(kLocalExamplePage);
return indexFile.path;
}
Future<void> _onLogExample() {
webViewController
.setOnConsoleMessage((JavaScriptConsoleMessage consoleMessage) {
debugPrint(
'== JS == ${consoleMessage.level.name}: ${consoleMessage.message}');
});
return webViewController.loadHtmlString(kLogExamplePage);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls({super.key, required this.webViewController});
final WebViewController webViewController;
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () async {
if (await webViewController.canGoBack()) {
await webViewController.goBack();
} else {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('无后退历史项')),
);
}
}
},
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: () async {
if (await webViewController.canGoForward()) {
await webViewController.goForward();
} else {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('无前进历史项')),
);
}
}
},
),
IconButton(
icon: const Icon(Icons.replay),
onPressed: () => webViewController.reload(),
),
],
);
}
}
支持的设备
该插件仅支持运行 Tizen 5.5 或更高版本的 Tizen TV 设备。
注意事项
- 要播放 YouTube 视频,请将应用的背景颜色设置为透明。
--- a/packages/webview_flutter/example/lib/main.dart
+++ b/packages/webview_flutter/example/lib/main.dart
@override
Widget build(BuildContext context) {
return Scaffold(
- backgroundColor: Colors.green,
+ backgroundColor: Colors.transparent,
appBar: AppBar(
title: const Text('Flutter WebView 示例'),
- 在 Tizen 6.0 版本中,有些设备无法创建 WebView。在这种情况下,通过内部使用升级 Web 引擎 (UWE) 可以解决创建失败的问题。如果你在创建
WebViewWidget
之前将WebViewController.tizenEnginePolicy
扩展 API 设置为true
,WebView 会内部搜索另一个版本的引擎。但是,这个 API 可能随时被更改(或移除),并且不保证正式支持。
import 'package:webview_flutter_tizen/webview_flutter_tizen.dart';
WebViewController _controller;
_controller.tizenEnginePolicy = true;
更多关于Flutter WebView插件webview_flutter_tizen的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter WebView插件webview_flutter_tizen的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用webview_flutter_tizen
插件的示例代码案例。这个插件允许你在Tizen设备上嵌入WebView。请注意,webview_flutter_tizen
是一个特定于Tizen平台的插件,因此你需要确保你的开发环境已经设置好Tizen SDK。
首先,确保你已经在pubspec.yaml
文件中添加了webview_flutter_tizen
依赖:
dependencies:
flutter:
sdk: flutter
webview_flutter_tizen: ^x.y.z # 请替换为最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,在你的Flutter项目中创建一个新的页面或使用现有的页面来嵌入WebView。下面是一个完整的示例,展示如何在Flutter中使用webview_flutter_tizen
插件:
import 'package:flutter/material.dart';
import 'package:webview_flutter_tizen/webview_flutter_tizen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter WebView Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: WebViewDemo(),
);
}
}
class WebViewDemo extends StatefulWidget {
@override
_WebViewDemoState createState() => _WebViewDemoState();
}
class _WebViewDemoState extends State<WebViewDemo> {
late WebViewController _controller;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('WebView Demo'),
),
body: WebView(
initialUrl: 'https://flutter.dev', // 初始URL
javascriptMode: JavascriptMode.unrestricted, // 允许JavaScript
onWebViewCreated: (WebViewController webViewController) {
_controller = webViewController;
// 可以在这里执行其他WebView操作,如加载其他URL或执行JavaScript
// _controller.loadUrl('https://example.com');
// _controller.evaluateJavascript('document.title');
},
navigationDelegate: (NavigationRequest request) {
// 处理页面导航请求,如是否允许页面跳转
if (request.url.startsWith('https://flutter.dev')) {
return NavigationDecision.navigate;
}
return NavigationDecision.prevent;
},
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
// 示例:在WebView中执行JavaScript
String title = await _controller.evaluateJavascript('document.title');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Page title: $title')),
);
},
tooltip: 'Get Page Title',
child: Icon(Icons.info),
),
);
}
}
这个示例展示了如何:
- 在
pubspec.yaml
文件中添加webview_flutter_tizen
依赖。 - 创建一个Flutter应用,并在其中嵌入WebView。
- 设置初始URL,并允许JavaScript执行。
- 使用
onWebViewCreated
回调来获取WebViewController
实例,以便后续操作(如加载其他URL或执行JavaScript)。 - 使用
navigationDelegate
来处理页面导航请求,决定是否允许页面跳转。 - 添加一个浮动操作按钮,用于在WebView中执行JavaScript并获取页面标题。
请确保你的开发环境已经配置好Tizen SDK,并且你的设备或模拟器支持Tizen平台。如果你遇到任何特定于Tizen平台的问题,请查阅相关文档或社区资源以获取更多帮助。