1. Giới thiệu
Lần cập nhật gần đây nhất: ngày 19 tháng 10 năm 2021
Với trình bổ trợ WebView Flutter, bạn có thể thêm một tiện ích WebView vào ứng dụng Flutter trên Android hoặc iOS của mình. Trên iOS, tiện ích WebView được WKWebView hỗ trợ, còn trên Android, tiện ích WebView được WebView hỗ trợ. Trình bổ trợ này có thể hiển thị các tiện ích Flutter trên chế độ xem web. Ví dụ: bạn có thể hiển thị trình đơn thả xuống trên chế độ xem web.
Sản phẩm bạn sẽ tạo ra
Trong lớp học lập trình này, bạn sẽ từng bước xây dựng một ứng dụng di động có WebView bằng Flutter SDK. Ứng dụng này sẽ:
- Hiển thị nội dung trên web trong
WebView
- Hiển thị các tiện ích Flutter xếp chồng trên
WebView
- Phản ứng với các sự kiện về tiến trình tải trang
- Điều khiển
WebView
thông quaWebViewController
- Chặn các trang web bằng
NavigationDelegate
- Đánh giá biểu thức JavaScript
- Xử lý lệnh gọi lại từ JavaScript bằng
JavascriptChannels
- Đặt, xoá, thêm hoặc hiển thị cookie
- Tải và hiển thị HTML từ các thành phần, tệp hoặc chuỗi chứa HTML
Kiến thức bạn sẽ học được
Trong lớp học lập trình này, bạn sẽ tìm hiểu cách sử dụng trình bổ trợ webview_flutter
theo nhiều cách, bao gồm:
- Cách định cấu hình trình bổ trợ
webview_flutter
- Cách theo dõi các sự kiện tiến trình tải trang
- Cách kiểm soát hoạt động điều hướng trang
- Cách ra lệnh cho
WebView
tiến và lùi trong nhật ký của nó - Cách đánh giá JavaScript, bao gồm cả việc sử dụng kết quả được trả về
- Cách đăng ký lệnh gọi lại để gọi mã Dart từ JavaScript
- Cách quản lý cookie
- Cách tải và hiển thị các trang HTML từ thành phần hoặc tệp hoặc Chuỗi chứa HTML
Bạn cần có
- Android Studio 4.1 trở lên (để phát triển Android)
- Xcode 12 trở lên (để phát triển iOS)
- SDK Flutter
- Một trình soạn thảo mã, chẳng hạn như Android Studio, Visual Studio Code hoặc Emac.
2. Thiết lập môi trường phát triển Flutter
Bạn cần có 2 phần mềm để hoàn thành phòng thí nghiệm này – Flutter SDK và trình chỉnh sửa.
Bạn có thể chạy lớp học lập trình bằng bất kỳ thiết bị nào sau đây:
- Một thiết bị Android hoặc iOS thực kết nối với máy tính của bạn và được đặt ở Chế độ nhà phát triển.
- Trình mô phỏng iOS (yêu cầu cài đặt công cụ Xcode).
- Trình mô phỏng Android (yêu cầu thiết lập trong Android Studio).
3. Bắt đầu
Làm quen với Flutter
Có nhiều cách để tạo một dự án Flutter mới, trong đó có cả Android Studio và Visual Studio Code đều cung cấp công cụ cho nhiệm vụ này. Làm theo quy trình được liên kết để tạo một dự án hoặc thực thi các lệnh sau trong một thiết bị đầu cuối dòng lệnh thuận tiện.
$ flutter create --platforms=android,ios webview_in_flutter Creating project webview_in_flutter... Resolving dependencies in `webview_in_flutter`... Downloading packages... Got dependencies in `webview_in_flutter`. Wrote 74 files. All done! You can find general documentation for Flutter at: https://docs.flutter.dev/ Detailed API documentation is available at: https://api.flutter.dev/ If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev In order to run your application, type: $ cd webview_in_flutter $ flutter run Your application code is in webview_in_flutter/lib/main.dart.
Thêm trình bổ trợ WebView cho Flutter làm phần phụ thuộc
Bạn có thể dễ dàng thêm chức năng bổ sung vào ứng dụng Flutter bằng cách sử dụng các gói Pub. Trong lớp học lập trình này, bạn sẽ thêm trình bổ trợ webview_flutter
vào dự án. Chạy các lệnh sau trong thiết bị đầu cuối.
$ cd webview_in_flutter $ flutter pub add webview_flutter Resolving dependencies... Downloading packages... leak_tracker 10.0.4 (10.0.5 available) leak_tracker_flutter_testing 3.0.3 (3.0.5 available) material_color_utilities 0.8.0 (0.11.1 available) meta 1.12.0 (1.14.0 available) + plugin_platform_interface 2.1.8 test_api 0.7.0 (0.7.1 available) + webview_flutter 4.7.0 + webview_flutter_android 3.16.0 + webview_flutter_platform_interface 2.10.0 + webview_flutter_wkwebview 3.13.0 Changed 5 dependencies! 5 packages have newer versions incompatible with dependency constraints. Try `flutter pub outdated` for more information.
Nếu kiểm tra pubspec.yaml, giờ đây bạn sẽ thấy dòng này trong mục phần phụ thuộc cho trình bổ trợ webview_flutter
.
Định cấu hình Android minSDK
Để sử dụng trình bổ trợ webview_flutter
trên Android, bạn cần đặt minSDK
thành 20
. Sửa đổi tệp android/app/build.gradle
như sau:
android/app/build.gradle
android {
//...
defaultConfig {
applicationId = "com.example.webview_in_flutter"
minSdk = 20 // Modify this line
targetSdk = flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
}
4. Thêm tiện ích WebView vào ứng dụng Flutter
Ở bước này, bạn sẽ thêm một WebView
vào ứng dụng của mình. WebView là khung hiển thị gốc được lưu trữ và trong vai trò nhà phát triển ứng dụng, bạn có thể chọn cách lưu trữ những khung hiển thị gốc này trong ứng dụng. Trên Android, bạn có thể lựa chọn giữa Màn hình ảo (hiện là chế độ mặc định cho Android) và thành phần Kết hợp. Tuy nhiên, iOS luôn sử dụng thành phần kết hợp.
Để thảo luận sâu hơn về sự khác biệt giữa Giao diện ảo và thành phần kết hợp, vui lòng đọc tài liệu về Lưu trữ chế độ xem gốc dành cho Android và iOS trong ứng dụng Flutter có Chế độ xem nền tảng.
Đặt Chế độ xem web trên màn hình
Thay thế nội dung của lib/main.dart
bằng:
lib/main.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
runApp(
MaterialApp(
theme: ThemeData(useMaterial3: true),
home: const WebViewApp(),
),
);
}
class WebViewApp extends StatefulWidget {
const WebViewApp({super.key});
@override
State<WebViewApp> createState() => _WebViewAppState();
}
class _WebViewAppState extends State<WebViewApp> {
late final WebViewController controller;
@override
void initState() {
super.initState();
controller = WebViewController()
..loadRequest(
Uri.parse('https://flutter.dev'),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
),
body: WebViewWidget(
controller: controller,
),
);
}
}
Chạy tính năng này trên iOS hoặc Android sẽ hiển thị WebView dưới dạng một cửa sổ trình duyệt tràn lề trên thiết bị của bạn. Điều này có nghĩa là trình duyệt được hiển thị trên thiết bị của bạn ở chế độ toàn màn hình mà không có bất kỳ hình thức đường viền hoặc lề nào. Khi cuộn, bạn sẽ thấy các phần của trang trông có vẻ hơi kỳ lạ. Điều này là do JavaScript hiện đang bị tắt và việc hiển thị flutter.dev yêu cầu JavaScript đúng cách.
Chạy ứng dụng
Chạy ứng dụng Flutter trên iOS hoặc Android để xem một Webview, nơi hiển thị trang web flutter.dev. Ngoài ra, bạn có thể chạy ứng dụng trong trình mô phỏng Android hoặc trình mô phỏng iOS. Bạn có thể thay thế URL WebView ban đầu bằng trang web của riêng bạn, ví dụ như URL đó.
$ flutter run
Giả sử bạn đã chạy trình mô phỏng hoặc trình mô phỏng thích hợp hay đi kèm một thiết bị thực, sau khi biên dịch và triển khai ứng dụng cho thiết bị của mình, bạn sẽ thấy như sau:
5. Theo dõi các sự kiện tải trang
Tiện ích WebView
cung cấp một số sự kiện tiến trình tải trang mà ứng dụng của bạn có thể theo dõi. Trong chu kỳ tải trang WebView
, có 3 sự kiện tải trang sẽ được kích hoạt: onPageStarted
, onProgress
và onPageFinished
. Ở bước này, bạn sẽ triển khai chỉ báo tải trang. Ngoài ra, việc này còn cho thấy rằng bạn có thể kết xuất nội dung trên Flutter trên vùng nội dung WebView
.
Thêm sự kiện tải trang vào ứng dụng
Tạo một tệp nguồn mới tại lib/src/web_view_stack.dart
và điền nội dung sau vào tệp đó:
lib/src/web_view_stack.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewStack extends StatefulWidget {
const WebViewStack({super.key});
@override
State<WebViewStack> createState() => _WebViewStackState();
}
class _WebViewStackState extends State<WebViewStack> {
var loadingPercentage = 0;
late final WebViewController controller;
@override
void initState() {
super.initState();
controller = WebViewController()
..setNavigationDelegate(NavigationDelegate(
onPageStarted: (url) {
setState(() {
loadingPercentage = 0;
});
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
});
},
onPageFinished: (url) {
setState(() {
loadingPercentage = 100;
});
},
))
..loadRequest(
Uri.parse('https://flutter.dev'),
);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
WebViewWidget(
controller: controller,
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
),
],
);
}
}
Mã này đã gói tiện ích WebView
trong một Stack
, phủ lên WebView
bằng một LinearProgressIndicator
theo điều kiện khi tỷ lệ phần trăm tải trang nhỏ hơn 100%. Do trạng thái này liên quan đến trạng thái chương trình thay đổi theo thời gian, nên bạn đã lưu trữ trạng thái này trong một lớp State
liên kết với StatefulWidget
.
Để sử dụng tiện ích WebViewStack
mới này, hãy sửa đổi lib/main.dart của bạn như sau:
lib/main.dart
import 'package:flutter/material.dart';
import 'src/web_view_stack.dart';
void main() {
runApp(
MaterialApp(
theme: ThemeData(useMaterial3: true),
home: const WebViewApp(),
),
);
}
class WebViewApp extends StatefulWidget {
const WebViewApp({super.key});
@override
State<WebViewApp> createState() => _WebViewAppState();
}
class _WebViewAppState extends State<WebViewApp> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
),
body: const WebViewStack(),
);
}
}
Khi bạn chạy ứng dụng, tuỳ thuộc vào điều kiện mạng và việc trình duyệt có lưu trang mà bạn đang chuyển đến vào bộ nhớ đệm hay không, bạn sẽ thấy chỉ báo tải trang phủ lên trên vùng nội dung WebView
.
6. Làm việc với WebViewController
Truy cập WebViewController từ Tiện ích WebView
Tiện ích WebView
cho phép kiểm soát có lập trình bằng WebViewController
. Bộ điều khiển này sẽ được cung cấp sau khi xây dựng tiện ích WebView
thông qua một lệnh gọi lại. Bản chất không đồng bộ của khả năng sử dụng của bộ điều khiển này khiến bộ điều khiển này trở thành lựa chọn hàng đầu cho lớp Completer<T>
không đồng bộ của Dart.
Cập nhật lib/src/web_view_stack.dart
như sau:
lib/src/web_view_stack.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewStack extends StatefulWidget {
const WebViewStack({required this.controller, super.key}); // MODIFY
final WebViewController controller; // ADD
@override
State<WebViewStack> createState() => _WebViewStackState();
}
class _WebViewStackState extends State<WebViewStack> {
var loadingPercentage = 0;
// REMOVE the controller that was here
@override
void initState() {
super.initState();
// Modify from here...
widget.controller.setNavigationDelegate(
NavigationDelegate(
onPageStarted: (url) {
setState(() {
loadingPercentage = 0;
});
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
});
},
onPageFinished: (url) {
setState(() {
loadingPercentage = 100;
});
},
),
);
// ...to here.
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
WebViewWidget(
controller: widget.controller, // MODIFY
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
),
],
);
}
}
Tiện ích WebViewStack
hiện sử dụng bộ điều khiển được tạo trong tiện ích xung quanh. Việc này sẽ cho phép chia sẻ bộ điều khiển WebViewWidget
với các phần khác của ứng dụng một cách dễ dàng.
Tạo các nút điều khiển điều hướng
Việc có WebView
hoạt động là một điều hữu ích, nhưng khả năng điều hướng ngược và tiến trong nhật ký trang và tải lại trang thì sẽ là một cách bổ sung hữu ích. Rất may, với WebViewController
, bạn có thể thêm chức năng này vào ứng dụng của mình.
Tạo một tệp nguồn mới tại lib/src/navigation_controls.dart
và điền vào tệp đó như sau:
lib/src/navigation_controls.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class NavigationControls extends StatelessWidget {
const NavigationControls({required this.controller, super.key});
final WebViewController controller;
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () async {
final messenger = ScaffoldMessenger.of(context);
if (await controller.canGoBack()) {
await controller.goBack();
} else {
messenger.showSnackBar(
const SnackBar(content: Text('No back history item')),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: () async {
final messenger = ScaffoldMessenger.of(context);
if (await controller.canGoForward()) {
await controller.goForward();
} else {
messenger.showSnackBar(
const SnackBar(content: Text('No forward history item')),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.replay),
onPressed: () {
controller.reload();
},
),
],
);
}
}
Tiện ích này sử dụng WebViewController
được chia sẻ với nó tại thời điểm xây dựng để cho phép người dùng điều khiển WebView
thông qua một loạt IconButton
.
Thêm các tuỳ chọn điều khiển điều hướng vào AppBar
Với WebViewStack
đã cập nhật và NavigationControls
mới được chế tạo trong tay, đã đến lúc bạn tổng hợp tất cả trong một WebViewApp
mới. Đây là nơi chúng ta tạo WebViewController
dùng chung. Với WebViewApp
ở gần đầu cây Tiện ích trong ứng dụng này, bạn nên tạo nó ở cấp này.
Cập nhật tệp lib/main.dart
như sau:
lib/main.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart'; // ADD
import 'src/navigation_controls.dart'; // ADD
import 'src/web_view_stack.dart';
void main() {
runApp(
MaterialApp(
theme: ThemeData(useMaterial3: true),
home: const WebViewApp(),
),
);
}
class WebViewApp extends StatefulWidget {
const WebViewApp({super.key});
@override
State<WebViewApp> createState() => _WebViewAppState();
}
class _WebViewAppState extends State<WebViewApp> {
// Add from here...
late final WebViewController controller;
@override
void initState() {
super.initState();
controller = WebViewController()
..loadRequest(
Uri.parse('https://flutter.dev'),
);
}
// ...to here.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
// Add from here...
actions: [
NavigationControls(controller: controller),
],
// ...to here.
),
body: WebViewStack(controller: controller), // MODIFY
);
}
}
Khi chạy ứng dụng, bạn sẽ thấy một trang web có các chế độ điều khiển:
7. Theo dõi hoạt động điều hướng bằng NavigationDelegate
WebView
cung cấp cho ứng dụng của bạn một NavigationDelegate,
cho phép ứng dụng đó theo dõi và kiểm soát việc điều hướng trang của tiện ích WebView
. Khi WebView,
khởi tạo một thao tác điều hướng, chẳng hạn như khi người dùng nhấp vào một đường liên kết, NavigationDelegate
sẽ được gọi. Bạn có thể sử dụng lệnh gọi lại NavigationDelegate
để kiểm soát xem WebView
có tiếp tục điều hướng hay không.
Đăng ký một NavigationDelegate tuỳ chỉnh
Trong bước này, bạn sẽ đăng ký một lệnh gọi lại NavigationDelegate
để chặn điều hướng đến YouTube.com. Lưu ý: Cách triển khai đơn giản này cũng chặn nội dung trên YouTube cùng dòng, vốn xuất hiện trên nhiều trang tài liệu về Flutter API.
Cập nhật lib/src/web_view_stack.dart
như sau:
lib/src/web_view_stack.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewStack extends StatefulWidget {
const WebViewStack({required this.controller, super.key});
final WebViewController controller;
@override
State<WebViewStack> createState() => _WebViewStackState();
}
class _WebViewStackState extends State<WebViewStack> {
var loadingPercentage = 0;
@override
void initState() {
super.initState();
widget.controller.setNavigationDelegate(
NavigationDelegate(
onPageStarted: (url) {
setState(() {
loadingPercentage = 0;
});
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
});
},
onPageFinished: (url) {
setState(() {
loadingPercentage = 100;
});
},
// Add from here...
onNavigationRequest: (navigation) {
final host = Uri.parse(navigation.url).host;
if (host.contains('youtube.com')) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Blocking navigation to $host',
),
),
);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
// ...to here.
),
);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
WebViewWidget(
controller: widget.controller,
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
),
],
);
}
}
Trong bước tiếp theo, bạn sẽ thêm một mục trong trình đơn để cho phép kiểm thử NavigationDelegate
bằng cách sử dụng lớp WebViewController
. Đây chỉ là một bài tập dành cho người đọc nhằm tăng cường logic của lệnh gọi lại để chỉ chặn điều hướng toàn trang đến YouTube.com mà vẫn cho phép nội dung YouTube cùng dòng trong tài liệu về API.
8. Thêm một nút trình đơn vào AppBar
Trong vài bước tiếp theo, bạn sẽ tạo một nút trình đơn trong tiện ích AppBar
dùng để đánh giá JavaScript, gọi các kênh JavaScript và quản lý cookie. Nói chung, một trình đơn thực sự hữu ích.
Tạo một tệp nguồn mới tại lib/src/menu.dart
và điền vào tệp đó như sau:
lib/src/menu.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
enum _MenuOptions {
navigationDelegate,
}
class Menu extends StatelessWidget {
const Menu({required this.controller, super.key});
final WebViewController controller;
@override
Widget build(BuildContext context) {
return PopupMenuButton<_MenuOptions>(
onSelected: (value) async {
switch (value) {
case _MenuOptions.navigationDelegate:
await controller.loadRequest(Uri.parse('https://youtube.com'));
}
},
itemBuilder: (context) => [
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.navigationDelegate,
child: Text('Navigate to YouTube'),
),
],
);
}
}
Khi người dùng chọn tuỳ chọn trình đơn Navigate to YouTube (Di chuyển đến YouTube), phương thức loadRequest
của WebViewController
sẽ được thực thi. Thao tác điều hướng này sẽ bị chặn bởi lệnh gọi lại navigationDelegate
mà bạn đã tạo ở bước trước.
Để thêm trình đơn vào màn hình của WebViewApp
, hãy sửa đổi lib/main.dart
như sau:
lib/main.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'src/menu.dart'; // ADD
import 'src/navigation_controls.dart';
import 'src/web_view_stack.dart';
void main() {
runApp(
MaterialApp(
theme: ThemeData(useMaterial3: true),
home: const WebViewApp(),
),
);
}
class WebViewApp extends StatefulWidget {
const WebViewApp({super.key});
@override
State<WebViewApp> createState() => _WebViewAppState();
}
class _WebViewAppState extends State<WebViewApp> {
late final WebViewController controller;
@override
void initState() {
super.initState();
controller = WebViewController()
..loadRequest(
Uri.parse('https://flutter.dev'),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
actions: [
NavigationControls(controller: controller),
Menu(controller: controller), // ADD
],
),
body: WebViewStack(controller: controller),
);
}
}
Chạy ứng dụng rồi nhấn vào mục trong trình đơn Navigate to YouTube (Di chuyển đến YouTube). Bạn sẽ được chào đón bằng SnackBar thông báo rằng bộ điều khiển điều hướng đã chặn khi điều hướng đến YouTube.
9. Đánh giá JavaScript
WebViewController
có thể đánh giá biểu thức JavaScript trong ngữ cảnh của trang hiện tại. Có hai cách để đánh giá JavaScript: đối với mã JavaScript không trả về giá trị, hãy sử dụng runJavaScript
và đối với mã JavaScript trả về giá trị, hãy sử dụng runJavaScriptReturningResult
.
Để bật JavaScript, bạn cần định cấu hình WebViewController
bằng thuộc tính javaScriptMode
được đặt thành JavascriptMode.unrestricted
. Theo mặc định, javascriptMode
được đặt thành JavascriptMode.disabled
.
Cập nhật lớp _WebViewStackState
bằng cách thêm chế độ cài đặt javascriptMode
như sau:
lib/src/web_view_stack.dart
class _WebViewStackState extends State<WebViewStack> {
var loadingPercentage = 0;
@override
void initState() {
super.initState();
widget.controller
..setNavigationDelegate( // Modify this line to use .. instead of .
NavigationDelegate(
onPageStarted: (url) {
setState(() {
loadingPercentage = 0;
});
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
});
},
onPageFinished: (url) {
setState(() {
loadingPercentage = 100;
});
},
onNavigationRequest: (navigation) {
final host = Uri.parse(navigation.url).host;
if (host.contains('youtube.com')) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Blocking navigation to $host',
),
),
);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
)
..setJavaScriptMode(JavaScriptMode.unrestricted); // Add this line
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
WebViewWidget(
controller: widget.controller,
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
),
],
);
}
}
Giờ đây, WebViewWidget
có thể thực thi JavaScript, bạn có thể thêm một tuỳ chọn vào trình đơn để sử dụng phương thức runJavaScriptReturningResult
.
Khi sử dụng Trình chỉnh sửa hoặc một số hoạt động của bàn phím, hãy chuyển đổi lớp Trình đơn thành một StatefulWidget. Sửa đổi lib/src/menu.dart
để phù hợp với mục sau:
lib/src/menu.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
enum _MenuOptions {
navigationDelegate,
userAgent, // Add this line
}
class Menu extends StatefulWidget { // Convert to StatefulWidget
const Menu({required this.controller, super.key});
final WebViewController controller;
@override // Add from here
State<Menu> createState() => _MenuState();
}
class _MenuState extends State<Menu> { // To here.
@override
Widget build(BuildContext context) {
return PopupMenuButton<_MenuOptions>(
onSelected: (value) async {
switch (value) {
case _MenuOptions.navigationDelegate: // Modify from here
await widget.controller
.loadRequest(Uri.parse('https://youtube.com'));
case _MenuOptions.userAgent:
final userAgent = await widget.controller
.runJavaScriptReturningResult('navigator.userAgent');
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('$userAgent'),
)); // To here.
}
},
itemBuilder: (context) => [
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.navigationDelegate,
child: Text('Navigate to YouTube'),
),
const PopupMenuItem<_MenuOptions>( // Add from here
value: _MenuOptions.userAgent,
child: Text('Show user-agent'),
), // To here.
],
);
}
}
Khi bạn nhấn vào liên kết "Hiển thị tác nhân người dùng" trình đơn, thì kết quả của việc thực thi biểu thức JavaScript navigator.userAgent
sẽ được hiển thị trong Snackbar
. Khi chạy ứng dụng, bạn có thể nhận thấy trang Flutter.dev trông có vẻ khác. Đây là kết quả của quá trình chạy khi bật JavaScript.
10. Làm việc với kênh JavaScript
Kênh JavaScript cho phép ứng dụng của bạn đăng ký trình xử lý gọi lại trong ngữ cảnh JavaScript của WebViewWidget
. Trình xử lý này có thể được gọi để truyền các giá trị về mã Dart của ứng dụng. Ở bước này, bạn sẽ đăng ký kênh SnackBar
. Kênh này sẽ được gọi bằng kết quả của XMLHttpRequest
.
Cập nhật lớp WebViewStack
như sau:
lib/src/web_view_stack.dart
class WebViewStack extends StatefulWidget {
const WebViewStack({required this.controller, super.key});
final WebViewController controller;
@override
State<WebViewStack> createState() => _WebViewStackState();
}
class _WebViewStackState extends State<WebViewStack> {
var loadingPercentage = 0;
@override
void initState() {
super.initState();
widget.controller
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (url) {
setState(() {
loadingPercentage = 0;
});
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
});
},
onPageFinished: (url) {
setState(() {
loadingPercentage = 100;
});
},
onNavigationRequest: (navigation) {
final host = Uri.parse(navigation.url).host;
if (host.contains('youtube.com')) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Blocking navigation to $host',
),
),
);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
)
// Modify from here...
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel(
'SnackBar',
onMessageReceived: (message) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message.message)));
},
);
// ...to here.
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
WebViewWidget(
controller: widget.controller,
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
),
],
);
}
}
Đối với mỗi Kênh JavaScript trong Set
, một đối tượng kênh sẽ được cung cấp trong ngữ cảnh JavaScript dưới dạng thuộc tính cửa sổ có tên giống với tên của Kênh JavaScript name
. Việc sử dụng thuộc tính này từ ngữ cảnh JavaScript bao gồm việc gọi postMessage
trên Kênh JavaScript để gửi thông báo được chuyển đến trình xử lý gọi lại onMessageReceived
của JavascriptChannel
đã được đặt tên.
Để sử dụng Kênh JavaScript đã thêm ở trên, hãy thêm một mục khác trong trình đơn thực thi XMLHttpRequest
trong ngữ cảnh JavaScript và trả về kết quả bằng Kênh JavaScript SnackBar
.
Giờ đây WebViewWidget
đã biết về Kênh JavaScript của chúng tôi,,
bạn sẽ thêm một ví dụ để mở rộng ứng dụng hơn nữa. Để thực hiện việc này, hãy thêm một PopupMenuItem
bổ sung vào lớp Menu
và thêm chức năng bổ sung.
Cập nhật _MenuOptions
bằng tuỳ chọn trình đơn bổ sung bằng cách thêm giá trị liệt kê javascriptChannel
và thêm một phương thức triển khai cho lớp Menu
như sau:
lib/src/menu.dart
enum _MenuOptions {
navigationDelegate,
userAgent,
javascriptChannel, // Add this option
}
class Menu extends StatefulWidget {
const Menu({required this.controller, super.key});
final WebViewController controller;
@override
State<Menu> createState() => _MenuState();
}
class _MenuState extends State<Menu> {
@override
Widget build(BuildContext context) {
return PopupMenuButton<_MenuOptions>(
onSelected: (value) async {
switch (value) {
case _MenuOptions.navigationDelegate:
await widget.controller
.loadRequest(Uri.parse('https://youtube.com'));
case _MenuOptions.userAgent:
final userAgent = await widget.controller
.runJavaScriptReturningResult('navigator.userAgent');
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('$userAgent'),
));
case _MenuOptions.javascriptChannel: // Add from here
await widget.controller.runJavaScript('''
var req = new XMLHttpRequest();
req.open('GET', "https://api.ipify.org/?format=json");
req.onload = function() {
if (req.status == 200) {
let response = JSON.parse(req.responseText);
SnackBar.postMessage("IP Address: " + response.ip);
} else {
SnackBar.postMessage("Error: " + req.status);
}
}
req.send();'''); // To here.
}
},
itemBuilder: (context) => [
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.navigationDelegate,
child: Text('Navigate to YouTube'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.userAgent,
child: Text('Show user-agent'),
),
const PopupMenuItem<_MenuOptions>( // Add from here
value: _MenuOptions.javascriptChannel,
child: Text('Lookup IP Address'),
), // To here.
],
);
}
}
JavaScript này được thực thi khi người dùng chọn tùy chọn trình đơn Ví dụ về kênh JavaScript.
var req = new XMLHttpRequest();
req.open('GET', "https://api.ipify.org/?format=json");
req.onload = function() {
if (req.status == 200) {
SnackBar.postMessage(req.responseText);
} else {
SnackBar.postMessage("Error: " + req.status);
}
}
req.send();
Mã này sẽ gửi yêu cầu GET
đến một API Địa chỉ IP công khai và trả về địa chỉ IP của thiết bị. Kết quả này được hiển thị trong SnackBar
bằng cách gọi postMessage
trên SnackBar
JavascriptChannel
.
11. Quản lý cookie
Ứng dụng của bạn có thể quản lý cookie trong WebView
bằng cách sử dụng lớp CookieManager
. Trong bước này, bạn sẽ hiển thị danh sách cookie, xoá danh sách cookie, xoá cookie và đặt cookie mới. Thêm các mục nhập vào _MenuOptions
cho từng trường hợp sử dụng cookie như sau:
lib/src/menu.dart
enum _MenuOptions {
navigationDelegate,
userAgent,
javascriptChannel,
// Add from here ...
listCookies,
clearCookies,
addCookie,
setCookie,
removeCookie,
// ... to here.
}
Những thay đổi còn lại trong bước này tập trung vào lớp Menu
, bao gồm cả việc chuyển đổi lớp Menu
từ không có trạng thái sang có trạng thái. Thay đổi này rất quan trọng vì Menu
cần sở hữu CookieManager
và trạng thái có thể thay đổi trong các tiện ích không có trạng thái là một cách kết hợp không hợp lệ.
Thêm CookieManager vào lớp Trạng thái thu được như sau:
lib/src/menu.dart
class Menu extends StatefulWidget {
const Menu({required this.controller, super.key});
final WebViewController controller;
@override
State<Menu> createState() => _MenuState();
}
class _MenuState extends State<Menu> {
final cookieManager = WebViewCookieManager(); // Add this line
@override
Widget build(BuildContext context) {
// ...
Lớp _MenuState
sẽ chứa mã đã thêm trước đó vào lớp Menu
, cùng với CookieManager
mới được thêm vào. Trong loạt phần tiếp theo, bạn sẽ thêm các chức năng trợ giúp vào _MenuState
. Các chức năng này sẽ được gọi bằng các mục chưa được thêm trong trình đơn.
Nhận danh sách tất cả cookie
Bạn sẽ sử dụng JavaScript để lấy danh sách tất cả cookie. Để làm được việc này, hãy thêm một phương thức trợ giúp vào cuối lớp _MenuState
, có tên là _onListCookies
. Khi sử dụng phương thức runJavaScriptReturningResult
, phương thức trợ giúp của bạn sẽ thực thi document.cookie
trong ngữ cảnh JavaScript và trả về danh sách tất cả cookie.
Thêm nội dung sau đây vào lớp _MenuState
:
lib/src/menu.dart
Future<void> _onListCookies(WebViewController controller) async {
final String cookies = await controller
.runJavaScriptReturningResult('document.cookie') as String;
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(cookies.isNotEmpty ? cookies : 'There are no cookies.'),
),
);
}
Xoá tất cả cookie
Để xoá tất cả cookie trong WebView, hãy sử dụng phương thức clearCookies
của lớp CookieManager
. Phương thức này trả về một Future<bool>
phân giải thành true
nếu CookieManager
xoá cookie và false
nếu không có cookie nào để xoá.
Thêm nội dung sau đây vào lớp _MenuState
:
lib/src/menu.dart
Future<void> _onClearCookies() async {
final hadCookies = await cookieManager.clearCookies();
String message = 'There were cookies. Now, they are gone!';
if (!hadCookies) {
message = 'There were no cookies to clear.';
}
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
),
);
}
Thêm cookie
Có thể thêm cookie bằng cách gọi JavaScript. API dùng để thêm Cookie vào tài liệu JavaScript được ghi lại chi tiết trên MDN.
Thêm nội dung sau đây vào lớp _MenuState
:
lib/src/menu.dart
Future<void> _onAddCookie(WebViewController controller) async {
await controller.runJavaScript('''var date = new Date();
date.setTime(date.getTime()+(30*24*60*60*1000));
document.cookie = "FirstName=John; expires=" + date.toGMTString();''');
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Custom cookie added.'),
),
);
}
Đặt cookie bằng CookieManager
Bạn cũng có thể đặt cookie bằng cách sử dụng CookieManager như sau.
Thêm nội dung sau đây vào lớp _MenuState
:
lib/src/menu.dart
Future<void> _onSetCookie(WebViewController controller) async {
await cookieManager.setCookie(
const WebViewCookie(name: 'foo', value: 'bar', domain: 'flutter.dev'),
);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Custom cookie is set.'),
),
);
}
Xoá cookie
Việc xoá cookie bao gồm cả việc thêm một cookie, có ngày hết hạn được đặt trong quá khứ.
Thêm nội dung sau đây vào lớp _MenuState
:
lib/src/menu.dart
Future<void> _onRemoveCookie(WebViewController controller) async {
await controller.runJavaScript(
'document.cookie="FirstName=John; expires=Thu, 01 Jan 1970 00:00:00 UTC" ');
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Custom cookie removed.'),
),
);
}
Thêm các mục trong Trình đơn CookieManager
Tất cả việc còn lại là thêm các tuỳ chọn trong trình đơn và truyền chúng vào các phương thức trợ giúp bạn vừa thêm. Cập nhật lớp _MenuState
như sau:
lib/src/menu.dart
class _MenuState extends State<Menu> {
final cookieManager = WebViewCookieManager();
@override
Widget build(BuildContext context) {
return PopupMenuButton<_MenuOptions>(
onSelected: (value) async {
switch (value) {
case _MenuOptions.navigationDelegate:
await widget.controller
.loadRequest(Uri.parse('https://youtube.com'));
case _MenuOptions.userAgent:
final userAgent = await widget.controller
.runJavaScriptReturningResult('navigator.userAgent');
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('$userAgent'),
));
case _MenuOptions.javascriptChannel:
await widget.controller.runJavaScript('''
var req = new XMLHttpRequest();
req.open('GET', "https://api.ipify.org/?format=json");
req.onload = function() {
if (req.status == 200) {
let response = JSON.parse(req.responseText);
SnackBar.postMessage("IP Address: " + response.ip);
} else {
SnackBar.postMessage("Error: " + req.status);
}
}
req.send();''');
case _MenuOptions.clearCookies: // Add from here
await _onClearCookies();
case _MenuOptions.listCookies:
await _onListCookies(widget.controller);
case _MenuOptions.addCookie:
await _onAddCookie(widget.controller);
case _MenuOptions.setCookie:
await _onSetCookie(widget.controller);
case _MenuOptions.removeCookie:
await _onRemoveCookie(widget.controller); // To here.
}
},
itemBuilder: (context) => [
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.navigationDelegate,
child: Text('Navigate to YouTube'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.userAgent,
child: Text('Show user-agent'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.javascriptChannel,
child: Text('Lookup IP Address'),
),
const PopupMenuItem<_MenuOptions>( // Add from here
value: _MenuOptions.clearCookies,
child: Text('Clear cookies'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.listCookies,
child: Text('List cookies'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.addCookie,
child: Text('Add cookie'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.setCookie,
child: Text('Set cookie'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.removeCookie,
child: Text('Remove cookie'),
), // To here.
],
);
}
Thực hành CookieManager
Để sử dụng tất cả chức năng bạn vừa thêm vào ứng dụng, hãy thử các bước sau:
- Chọn Liệt kê cookie. Trang này sẽ liệt kê các cookie Google Analytics do flutter.dev thiết lập.
- Chọn Xoá cookie. Hệ thống sẽ báo cáo rằng cookie thực sự đã bị xoá.
- Chọn Xoá cookie một lần nữa. Sẽ báo cáo rằng không có cookie nào để xoá.
- Chọn Liệt kê cookie. Hộp thoại sẽ báo cáo rằng không có cookie.
- Chọn Thêm cookie. Thao tác này sẽ báo cáo cookie là đã được thêm.
- Chọn Đặt cookie. Cookie sẽ báo cáo là đã đặt.
- Chọn Liệt kê cookie và sau đó khi phát triển cuối cùng, chọn Remove cookie (Xoá cookie).
12. Tải các thành phần, tệp và chuỗi HTML của Flutter trong WebView
Ứng dụng của bạn có thể tải tệp HTML bằng nhiều phương thức và hiển thị những tệp đó trong WebView. Ở bước này, bạn sẽ tải một thành phần Flutter được chỉ định trong tệp pubspec.yaml
, sau đó tải một tệp nằm tại đường dẫn được chỉ định và tải một trang bằng một Chuỗi HTML.
Nếu muốn tải một tệp nằm ở một đường dẫn cụ thể, bạn cần thêm path_provider
vào pubspec.yaml
. Đây là một trình bổ trợ Flutter dùng để tìm các vị trí thường dùng trên hệ thống tệp.
Trên dòng lệnh, hãy chạy lệnh sau:
$ flutter pub add path_provider
Để tải thành phần, chúng ta cần chỉ định đường dẫn đến thành phần đó trong pubspec.yaml
. Trong pubspec.yaml
, hãy thêm các dòng sau:
pubspec.yaml
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# Add from here
assets:
- assets/www/index.html
- assets/www/styles/style.css
# ... to here.
Để thêm các thành phần vào dự án, hãy làm theo các bước sau:
- Tạo một Directory (Thư mục) mới có tên
assets
trong thư mục gốc của dự án. - Tạo một Directory (Thư mục) mới có tên
www
trong thư mụcassets
. - Tạo một Directory (Thư mục) mới có tên
styles
trong thư mụcwww
. - Tạo một Tệp mới có tên là
index.html
trong thư mụcwww
. - Tạo một Tệp mới có tên là
style.css
trong thư mụcstyles
.
Sao chép và dán mã nguồn sau đây vào tệp index.html
:
<!DOCTYPE html>
<!-- Copyright 2013 The Flutter Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<html lang="en">
<head>
<title>Load file or HTML string example</title>
<link rel="stylesheet" href="styles/style.css" />
</head>
<body>
<h1>Local demo page</h1>
<p>
This is an example page used to demonstrate how to load a local file or HTML
string using the <a href="https://pub.dev/packages/webview_flutter">Flutter
webview</a> plugin.
</p>
</body>
</html>
Đối với style.css, hãy sử dụng vài dòng sau để thiết lập kiểu tiêu đề HTML:
h1 {
color: blue;
}
Giờ đây, các thành phần đã được thiết lập và sẵn sàng để sử dụng, bạn có thể triển khai các phương thức cần thiết để tải và hiển thị các thành phần, tệp hoặc chuỗi HTML của Flutter.
Tải thành phần Flutter
Để tải thành phần bạn vừa tạo, bạn chỉ cần gọi phương thức loadFlutterAsset
bằng cách sử dụng WebViewController
và cung cấp cho tham số đường dẫn đến thành phần đó. Thêm phương thức sau vào cuối mã:
lib/src/menu.dart
Future<void> _onLoadFlutterAssetExample(
WebViewController controller, BuildContext context) async {
await controller.loadFlutterAsset('assets/www/index.html');
}
Tải tệp trên máy
Để tải một tệp trên thiết bị, bạn có thể thêm một phương thức sẽ sử dụng phương thức loadFile
, một lần nữa bằng cách sử dụng WebViewController
để lấy String
chứa đường dẫn đến tệp.
Trước tiên, bạn cần tạo một tệp có chứa mã HTML. Bạn chỉ cần thực hiện việc này bằng cách thêm mã HTML dưới dạng Chuỗi ở đầu mã trong tệp menu.dart
ngay bên dưới mục nhập.
import 'dart:io'; // Add this line,
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart'; // And this one.
import 'package:webview_flutter/webview_flutter.dart';
// Add from here ...
const String kExamplePage = '''
<!DOCTYPE html>
<html lang="en">
<head>
<title>Load file or HTML string example</title>
</head>
<body>
<h1>Local demo page</h1>
<p>
This is an example page used to demonstrate how to load a local file or HTML
string using the <a href="https://pub.dev/packages/webview_flutter">Flutter
webview</a> plugin.
</p>
</body>
</html>
''';
// ... to here.
Để tạo một File
và ghi Chuỗi HTML vào tệp, bạn sẽ thêm 2 phương thức. _onLoadLocalFileExample
sẽ tải tệp bằng cách cung cấp đường dẫn dưới dạng Chuỗi được phương thức _prepareLocalFile()
trả về. Thêm các phương thức sau vào mã của bạn:
Future<void> _onLoadLocalFileExample(
WebViewController controller, BuildContext context) async {
final String pathToIndex = await _prepareLocalFile();
await controller.loadFile(pathToIndex);
}
static Future<String> _prepareLocalFile() async {
final String tmpDir = (await getTemporaryDirectory()).path;
final File indexFile = File('$tmpDir/www/index.html');
await Directory('$tmpDir/www').create(recursive: true);
await indexFile.writeAsString(kExamplePage);
return indexFile.path;
}
Tải chuỗi HTML
Để hiển thị một trang bằng cách cung cấp chuỗi HTML thì khá đơn giản. WebViewController
có một phương thức mà bạn có thể dùng là loadHtmlString
, trong đó bạn có thể cung cấp Chuỗi HTML làm đối số. Sau đó, WebView
sẽ hiển thị trang HTML được cung cấp. Thêm phương thức sau vào mã của bạn:
Future<void> _onLoadFlutterAssetExample(
WebViewController controller, BuildContext context) async {
await controller.loadFlutterAsset('assets/www/index.html');
}
Future<void> _onLoadLocalFileExample(
WebViewController controller, BuildContext context) async {
final String pathToIndex = await _prepareLocalFile();
await controller.loadFile(pathToIndex);
}
static Future<String> _prepareLocalFile() async {
final String tmpDir = (await getTemporaryDirectory()).path;
final File indexFile = File('$tmpDir/www/index.html');
await Directory('$tmpDir/www').create(recursive: true);
await indexFile.writeAsString(kExamplePage);
return indexFile.path;
}
// Add here ...
Future<void> _onLoadHtmlStringExample(
WebViewController controller, BuildContext context) async {
await controller.loadHtmlString(kExamplePage);
}
// ... to here.
Thêm các món trong thực đơn
Giờ đây, các thành phần đã được thiết lập và sẵn sàng để sử dụng, cũng như các phương thức có tất cả chức năng đã được tạo, bạn có thể cập nhật trình đơn. Thêm các mục sau vào enum _MenuOptions
:
lib/src/menu.dart
enum _MenuOptions {
navigationDelegate,
userAgent,
javascriptChannel,
listCookies,
clearCookies,
addCookie,
setCookie,
removeCookie,
// Add from here ...
loadFlutterAsset,
loadLocalFile,
loadHtmlString,
// ... to here.
}
Giờ đây, khi enum đã được cập nhật, bạn có thể thêm các tuỳ chọn trong trình đơn và chuyển các tuỳ chọn đó vào các phương thức trợ giúp mà bạn vừa thêm. Cập nhật lớp _MenuState
như sau:
lib/src/menu.dart
class _MenuState extends State<Menu> {
final cookieManager = WebViewCookieManager();
@override
Widget build(BuildContext context) {
return PopupMenuButton<_MenuOptions>(
onSelected: (value) async {
switch (value) {
case _MenuOptions.navigationDelegate:
await widget.controller
.loadRequest(Uri.parse('https://youtube.com'));
case _MenuOptions.userAgent:
final userAgent = await widget.controller
.runJavaScriptReturningResult('navigator.userAgent');
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('$userAgent'),
));
case _MenuOptions.javascriptChannel:
await widget.controller.runJavaScript('''
var req = new XMLHttpRequest();
req.open('GET', "https://api.ipify.org/?format=json");
req.onload = function() {
if (req.status == 200) {
let response = JSON.parse(req.responseText);
SnackBar.postMessage("IP Address: " + response.ip);
} else {
SnackBar.postMessage("Error: " + req.status);
}
}
req.send();''');
case _MenuOptions.clearCookies:
await _onClearCookies();
case _MenuOptions.listCookies:
await _onListCookies(widget.controller);
case _MenuOptions.addCookie:
await _onAddCookie(widget.controller);
case _MenuOptions.setCookie:
await _onSetCookie(widget.controller);
case _MenuOptions.removeCookie:
await _onRemoveCookie(widget.controller);
case _MenuOptions.loadFlutterAsset: // Add from here
if (!mounted) return;
await _onLoadFlutterAssetExample(widget.controller, context);
case _MenuOptions.loadLocalFile:
if (!mounted) return;
await _onLoadLocalFileExample(widget.controller, context);
case _MenuOptions.loadHtmlString:
if (!mounted) return;
await _onLoadHtmlStringExample(widget.controller, context);
// To here.
}
},
itemBuilder: (context) => [
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.navigationDelegate,
child: Text('Navigate to YouTube'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.userAgent,
child: Text('Show user-agent'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.javascriptChannel,
child: Text('Lookup IP Address'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.clearCookies,
child: Text('Clear cookies'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.listCookies,
child: Text('List cookies'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.addCookie,
child: Text('Add cookie'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.setCookie,
child: Text('Set cookie'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.removeCookie,
child: Text('Remove cookie'),
),
const PopupMenuItem<_MenuOptions>( // Add from here
value: _MenuOptions.loadFlutterAsset,
child: Text('Load Flutter Asset'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.loadHtmlString,
child: Text('Load HTML string'),
),
const PopupMenuItem<_MenuOptions>(
value: _MenuOptions.loadLocalFile,
child: Text('Load local file'),
), // To here.
],
);
}
Thử nghiệm thành phần, tệp và chuỗi HTML
Để kiểm tra xem mã có hoạt động mà bạn vừa triển khai hay không, bạn có thể chạy mã trên thiết bị rồi nhấp vào một trong các mục trình đơn mới được thêm vào. Hãy lưu ý cách _onLoadFlutterAssetExample
sử dụng style.css
mà chúng ta đã thêm để thay đổi tiêu đề của tệp HTML thành màu xanh dương.
13. Đã xong!
Xin chúc mừng!!! Bạn đã hoàn tất lớp học lập trình. Bạn có thể tìm thấy mã đã hoàn tất cho lớp học lập trình này trong kho lưu trữ lớp học lập trình.
Để tìm hiểu thêm, hãy thử tham gia các lớp học lập trình khác về Flutter.