1. מבוא
העדכון האחרון: 19 באוקטובר 2021
באמצעות התוסף WebView Flutter אפשר להוסיף ווידג'ט WebView לאפליקציית Flutter ל-Android או ל-iOS. ב-iOS, הווידג'ט WebView מגובה על ידי WKWebView, וב-Android, הווידג'ט WebView מגובה על ידי WebView. התוסף יכול להציג ווידג'טים של Flutter בתצוגת האינטרנט. לדוגמה, אפשר להציג תפריט נפתח מעל תצוגת האינטרנט.
מה תפַתחו
בשיעור הזה תלמדו איך ליצור אפליקציה לנייד שלב אחר שלב עם WebView באמצעות Flutter SDK. האפליקציה שלכם:
- הצגת תוכן אינטרנט ב
WebView
- הצגת ווידג'טים של Flutter בערימה מעל
WebView
- תגובה לאירועים של התקדמות טעינת הדף
- שליטה ב-
WebView
דרךWebViewController
- חסימת אתרים באמצעות
NavigationDelegate
- הערכת ביטויי JavaScript
- טיפול בקריאות חוזרות (callback) מ-JavaScript באמצעות
JavascriptChannels
- הגדרה, הסרה, הוספה או הצגה של קובצי Cookie
- טעינה והצגה של HTML מנכסים, מקבצים או ממחרוזות שמכילות HTML
מה תלמדו?
ב-Codelab הזה תלמדו איך להשתמש בתוסף webview_flutter
במגוון דרכים, כולל:
- איך מגדירים את הפלאגין
webview_flutter
- איך להאזין לאירועים של התקדמות טעינת הדף
- איך שולטים בניווט בדף
- איך מנחים את
WebView
לנוע קדימה ואחורה בהיסטוריה שלו - איך מעריכים JavaScript, כולל שימוש בתוצאות שהוחזרו
- איך רושמים קריאות חוזרות (callbacks) כדי לקרוא לקוד Dart מ-JavaScript
- כיצד לנהל קובצי cookie
- איך לטעון ולהציג דפי HTML מנכסים, מקבצים או ממחרוזת שמכילה HTML
מה צריך?
- Android Studio 4.1 ואילך (לפיתוח ל-Android)
- Xcode 12 ואילך (לפיתוח ל-iOS)
- Flutter SDK
- עורך קוד, כמו Android Studio או Visual Studio Code.
2. הגדרת סביבת הפיתוח של Flutter
כדי להשלים את שיעור ה-Lab הזה, תצטרכו שני סוגי תוכנה: Flutter SDK ועורך.
אפשר להריץ את ה-codelab באמצעות כל אחד מהמכשירים הבאים:
- מכשיר Android או iOS פיזי שמחובר למחשב ומוגדר למצב פיתוח.
- הסימולטור של iOS (נדרשת התקנה של כלי Xcode).
- Android Emulator (נדרשת הגדרה ב-Android Studio).
3. תחילת העבודה
תחילת העבודה עם Flutter
יש מגוון דרכים ליצור פרויקט Flutter חדש, וגם Android Studio וגם Visual Studio Code מספקות כלים למשימה הזו. אפשר לפעול לפי ההוראות המקושרות כדי ליצור פרויקט, או להריץ את הפקודות הבאות בטרמינל של שורת הפקודה.
$ 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.
הוספה של פלאגין WebView Flutter כתלות
הדרך הכי טובה להוסיף יכולות לאפליקציית Flutter היא באמצעות חבילות Pub. ב-codelab הזה תוסיפו את תוסף webview_flutter
לפרויקט. מריצים את הפקודות הבאות במסוף.
$ cd webview_in_flutter $ flutter pub add webview_flutter Resolving dependencies... Downloading packages... collection 1.18.0 (1.19.0 available) leak_tracker 10.0.5 (10.0.7 available) leak_tracker_flutter_testing 3.0.5 (3.0.7 available) material_color_utilities 0.11.1 (0.12.0 available) + plugin_platform_interface 2.1.8 string_scanner 1.2.0 (1.3.0 available) test_api 0.7.2 (0.7.3 available) + webview_flutter 4.9.0 + webview_flutter_android 3.16.7 + webview_flutter_platform_interface 2.10.0 + webview_flutter_wkwebview 3.15.0 Changed 5 dependencies! 6 packages have newer versions incompatible with dependency constraints. Try `flutter pub outdated` for more information.
אם תבדקו את הקובץ pubspec.yaml, תראו שיש בו עכשיו שורה בקטע dependencies של הפלאגין webview_flutter
.
הגדרת minSDK ב-Android
כדי להשתמש בפלאגין webview_flutter
ב-Android, צריך להגדיר את minSDK
לערך 20
. משנים את הקובץ android/app/build.gradle
באופן הבא:
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. הוספת הווידג'ט WebView לאפליקציית Flutter
בשלב הזה מוסיפים WebView
לבקשה. רכיבי WebView הם תצוגות מובנות שמתארחות באפליקציה, ולכם כמפתחי אפליקציות יש אפשרות לבחור איך לארח את התצוגות המובנות האלה באפליקציה. ב-Android אפשר לבחור בין תצוגות וירטואליות, שהן ברירת המחדל ב-Android, לבין קומפוזיציה היברידית. עם זאת, ב-iOS תמיד נעשה שימוש בהרכבה היברידית.
לדיון מעמיק על ההבדלים בין תצוגות וירטואליות לבין קומפוזיציה היברידית, אפשר לקרוא את התיעוד בנושא אירוח תצוגות מקוריות של Android ו-iOS באפליקציית Flutter באמצעות תצוגות פלטפורמה .
הצגת Webview במסך
מחליפים את התוכן של lib/main.dart
בתוכן הבא:
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,
),
);
}
}
אם מריצים את הפקודה הזו ב-iOS או ב-Android, יוצג WebView כחלון דפדפן ללא שוליים במכשיר, כלומר הדפדפן יוצג במסך מלא ללא שוליים או גבולות. כשגוללים, אפשר לראות חלקים בדף שנראים קצת מוזרים. הסיבה לכך היא ש-JavaScript מושבת, ונדרשת הפעלה של JavaScript כדי לעבד את flutter.dev בצורה תקינה.
הפעלת האפליקציה
מריצים את האפליקציה ב-Flutter ב-iOS או ב-Android כדי לראות Webview שבו מוצג האתר flutter.dev. אפשר גם להריץ את האפליקציה באמולטור Android או בסימולטור iOS. אתם יכולים להחליף את כתובת ה-URL הראשונית של WebView, למשל בכתובת של האתר שלכם.
$ flutter run
בהנחה שהסימולטור או האמולטור המתאימים פועלים, או שמכשיר פיזי מחובר, אחרי הידור ופריסה של האפליקציה במכשיר, אמור להופיע משהו כמו הדוגמה הבאה:
5. האזנה לאירועים של טעינת דף
ווידג'ט WebView
מספק כמה אירועים של התקדמות טעינת הדף, שהאפליקציה יכולה להאזין להם. במהלך מחזור הטעינה של הדף WebView
מופעלים שלושה אירועים שונים של טעינת הדף: onPageStarted
, onProgress
ו-onPageFinished
. בשלב הזה מטמיעים אינדיקטור לטעינת הדף. בנוסף, זה יראה שאתם יכולים להציג תוכן Flutter מעל אזור התוכן של WebView
.
הוספת אירועים של טעינת דף לאפליקציה
יוצרים קובץ מקור חדש ב-lib/src/web_view_stack.dart
וממלאים אותו בתוכן הבא:
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,
),
],
);
}
}
הקוד הזה עוטף את הווידג'ט WebView
בתג Stack
, ומוסיף שכבת-על של WebView
עם LinearProgressIndicator
באופן מותנה, כשהאחוז של טעינת הדף נמוך מ-100%. מכיוון שמדובר במצב התוכנית שמשתנה עם הזמן, שמרתם את המצב הזה במחלקה State
שמשויכת ל-StatefulWidget
.
כדי להשתמש בווידג'ט החדש WebViewStack
, צריך לשנות את lib/main.dart
באופן הבא:
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(),
);
}
}
כשמפעילים את האפליקציה, בהתאם לתנאי הרשת ולשאלה אם הדפדפן שמר במטמון את הדף שאליו עוברים, מוצג אינדיקטור לטעינת הדף שמוצג על גבי אזור התוכן WebView
.
6. עבודה עם WebViewController
גישה ל-WebViewController מהווידג'ט WebView
הווידג'ט WebView
מאפשר שליטה פרוגרמטית באמצעות WebViewController
. הבקר הזה זמין אחרי בניית הווידג'ט WebView
באמצעות קריאה חוזרת. הזמינות האסינכרונית של בקר זה הופכת אותו למועמד מצוין לשימוש במחלקה האסינכרונית Completer<T>
של Dart.
כך מעדכנים את lib/src/web_view_stack.dart
:
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,
),
],
);
}
}
הווידג'ט WebViewStack
משתמש עכשיו בבקר שנוצר בווידג'ט שמסביב. כך אפשר לשתף את בקר WebViewWidget
עם חלקים אחרים באפליקציה.
יצירת פקדי ניווט
הוספת WebView
עובדת היא דבר אחד, אבל היכולת לנווט קדימה ואחורה בהיסטוריית הדפים ולטעון מחדש את הדף תהיה תוספת שימושית. למזלנו, באמצעות WebViewController
אפשר להוסיף את הפונקציונליות הזו לאפליקציה.
יוצרים קובץ מקור חדש ב-lib/src/navigation_controls.dart
וממלאים אותו בתוכן הבא:
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();
},
),
],
);
}
}
הווידג'ט הזה משתמש ב-WebViewController
ששותף איתו בזמן הבנייה כדי לאפשר למשתמש לשלוט ב-WebView
באמצעות סדרה של IconButton
.
הוספת אמצעי ניווט לסרגל האפליקציות
עכשיו, כשיש לך את WebViewStack
המעודכן ואת NavigationControls
החדש, הגיע הזמן לשלב את הכול בWebViewApp
מעודכן. כאן אנחנו בונים את ה-WebViewController
המשותף. המיקום של WebViewApp
קרוב לחלק העליון של עץ הווידג'טים באפליקציה הזו, ולכן כדאי ליצור אותו ברמה הזו.
מעדכנים את הקובץ lib/main.dart
באופן הבא:
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
);
}
}
כשמפעילים את האפליקציה, אמור להיפתח דף אינטרנט עם אמצעי בקרה:
7. מעקב אחרי הניווט באמצעות NavigationDelegate
WebView
מספק לאפליקציה שלך NavigationDelegate,
שמאפשר לאפליקציה לעקוב אחרי הניווט בדף של הווידג'ט WebView
ולשלוט בו. כשמתחילים בניווט באמצעות WebView,
, למשל כשמשתמש לוחץ על קישור, מתבצעת קריאה ל-NavigationDelegate
. אפשר להשתמש בקריאה החוזרת NavigationDelegate
כדי לקבוע אם WebView
ימשיך בניווט.
רישום של NavigationDelegate מותאם אישית
בשלב הזה, תרשמו NavigationDelegate
callback כדי לחסום את הניווט אל YouTube.com. שימו לב שההטמעה הפשוטה הזו חוסמת גם תוכן מוטבע של YouTube שמופיע בדפים שונים של תיעוד Flutter API.
מעדכנים את lib/src/web_view_stack.dart
באופן הבא:
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,
),
],
);
}
}
בשלב הבא תוסיפו פריט לתפריט כדי להפעיל בדיקה של NavigationDelegate
באמצעות המחלקה WebViewController
. הקוראים מוזמנים להרחיב את הלוגיקה של הקריאה החוזרת כדי לחסום רק ניווט בדף מלא אל YouTube.com, ועדיין לאפשר את התוכן של YouTube בתוך השורה במסמכי ה-API.
8. הוספת כפתור תפריט לסרגל האפליקציות
בשלבים הבאים ניצור לחצן תפריט בווידג'ט AppBar
שמשמש להערכת JavaScript, להפעלת ערוצי JavaScript ולניהול קובצי Cookie. בקיצור, תפריט שימושי מאוד.
יוצרים קובץ מקור חדש ב-lib/src/menu.dart
וממלאים אותו בתוכן הבא:
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'),
),
],
);
}
}
כשמשתמש בוחר באפשרות בתפריט Navigate to YouTube, מופעלת השיטה loadRequest
של WebViewController
. הניווט הזה ייחסם על ידי הקריאה החוזרת navigationDelegate
שיצרתם בשלב הקודם.
כדי להוסיף את התפריט למסך של WebViewApp
, משנים את lib/main.dart
באופן הבא:
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),
);
}
}
מריצים את האפליקציה ומקישים על האפשרות Navigate to YouTube (ניווט אל YouTube) בתפריט. אמורה להופיע הודעה קצרה שמודיעה שבקר הניווט חסם את הניווט אל YouTube.
9. הערכה של JavaScript
WebViewController
יכול להעריך ביטויי JavaScript בהקשר של הדף הנוכחי. יש שתי דרכים שונות להערכת JavaScript: לקוד JavaScript שלא מחזיר ערך, משתמשים ב-runJavaScript
, ולקוד JavaScript שמחזיר ערך, משתמשים ב-runJavaScriptReturningResult
.
כדי להפעיל את JavaScript, צריך להגדיר את WebViewController
עם המאפיין javaScriptMode
שמוגדר לערך JavascriptMode.unrestricted
. כברירת מחדל, javascriptMode
מוגדר כ-JavascriptMode.disabled
.
מעדכנים את הכיתה _WebViewStackState
על ידי הוספת ההגדרה javascriptMode
באופן הבא:
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,
),
],
);
}
}
עכשיו, כש-WebViewWidget
יכול להריץ JavaScript, אפשר להוסיף לתפריט אפשרות להשתמש בשיטה runJavaScriptReturningResult
.
באמצעות Editor או פעולות במקלדת, ממירים את המחלקה Menu למחלקה StatefulWidget. משנים את lib/src/menu.dart
כך שיתאים לערכים הבאים:
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.
],
);
}
}
כשמקישים על אפשרות התפריט 'הצגת מחרוזת User-Agent', התוצאה של הפעלת הביטוי JavaScript navigator.userAgent
מוצגת ב-Snackbar
. כשמריצים את האפליקציה, יכול להיות שתשימו לב שהדף Flutter.dev נראה שונה. זאת התוצאה של ההרצה עם JavaScript מופעל.
10. עבודה עם ערוצי JavaScript
ערוצי JavaScript מאפשרים לאפליקציה לרשום פונקציות callback לטיפול באירועים בהקשר JavaScript של WebViewWidget
, שאפשר להפעיל כדי להעביר ערכים בחזרה לקוד Dart של האפליקציה. בשלב הזה רושמים ערוץ SnackBar
שיופעל עם התוצאה של XMLHttpRequest
.
מעדכנים את הכיתה WebViewStack
באופן הבא:
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,
),
],
);
}
}
לכל ערוץ JavaScript ב-Set
, אובייקט ערוץ זמין בהקשר של JavaScript כמאפיין חלון ששמו זהה לשם של ערוץ ה-JavaScript name
. כדי להשתמש בשיטה הזו בהקשר של JavaScript, צריך לקרוא ל-postMessage
בערוץ JavaScript כדי לשלוח הודעה שמועברת ל-JavascriptChannel
בשם של onMessageReceived
handler של קריאה חוזרת (callback).
כדי להשתמש בערוץ JavaScript שהוספתם קודם, מוסיפים עוד פריט לתפריט שמבצע XMLHttpRequest
בהקשר של JavaScript ומעביר את התוצאות בחזרה באמצעות ערוץ JavaScript SnackBar
.
עכשיו, אחרי שWebViewWidget
יודע על ערוצי JavaScript שלנו,
, נוסיף דוגמה כדי להרחיב את האפליקציה. כדי לעשות את זה, מוסיפים עוד PopupMenuItem
למחלקה Menu
ומוסיפים את הפונקציונליות הנוספת.
מעדכנים את _MenuOptions
באפשרות הנוספת בתפריט, מוסיפים את ערך הספירה javascriptChannel
ומוסיפים הטמעה למחלקה Menu
באופן הבא:
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 הזה מופעל כשהמשתמש בוחר באפשרות התפריט JavaScript Channel Example.
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();
הקוד הזה שולח בקשת GET
ל-API של כתובת IP ציבורית, ומחזיר את כתובת ה-IP של המכשיר. התוצאה הזו מוצגת ב-SnackBar
על ידי הפעלת postMessage
ב-SnackBar
JavascriptChannel
.
11. ניהול קובצי Cookie
האפליקציה יכולה לנהל קובצי Cookie ב-WebView
באמצעות המחלקה CookieManager
. בשלב הזה, תציגו רשימה של קובצי Cookie, תנקו את רשימת קובצי ה-Cookie, תמחקו קובצי Cookie ותגדירו קובצי Cookie חדשים. מוסיפים רשומות ל-_MenuOptions
לכל אחד מתרחישי השימוש בקובצי Cookie באופן הבא:
lib/src/menu.dart
enum _MenuOptions {
navigationDelegate,
userAgent,
javascriptChannel,
// Add from here ...
listCookies,
clearCookies,
addCookie,
setCookie,
removeCookie,
// ... to here.
}
שאר השינויים בשלב הזה מתמקדים במחלקה Menu
, כולל ההמרה של המחלקה Menu
ממצב חסר מצב למצב עם מצב. השינוי הזה חשוב כי Menu
צריך להיות הבעלים של CookieManager
, ומצב שניתן לשינוי בווידג'טים חסרי מצב הוא שילוב בעייתי.
מוסיפים את CookieManager למחלקת State שמתקבלת באופן הבא:
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) {
// ...
הקוד שנוסף קודם בכיתה Menu
יופיע בכיתה _MenuState
, יחד עם CookieManager
שנוסף עכשיו. בסדרת הקטעים הבאה, תוסיפו ל-_MenuState
פונקציות עזר שיופעלו על ידי פריטי התפריט שעדיין לא נוספו.
קבלת רשימה של כל קובצי ה-Cookie
תשתמשו ב-JavaScript כדי לקבל רשימה של כל קובצי ה-Cookie. כדי לעשות את זה, מוסיפים שיטת עזר לסוף המחלקה _MenuState
, שנקראת _onListCookies
. באמצעות ה-method runJavaScriptReturningResult
, שיטת העזר שלכם מפעילה את document.cookie
בהקשר של JavaScript, ומחזירה רשימה של כל קובצי ה-Cookie.
מוסיפים את הטקסט הבא לכיתה _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.'),
),
);
}
ניקוי כל קובצי ה-Cookie
כדי למחוק את כל קובצי ה-Cookie ב-WebView, משתמשים בשיטה clearCookies
של המחלקה CookieManager
. השיטה מחזירה Future<bool>
שמוביל ל-true
אם CookieManager
ניקה את קובצי ה-Cookie, ול-false
אם לא היו קובצי Cookie לניקוי.
מוסיפים את הטקסט הבא לכיתה _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),
),
);
}
הוספת קובץ Cookie
אפשר להוסיף קובץ Cookie באמצעות הפעלה של JavaScript. ממשק ה-API שמשמש להוספת קובץ Cookie למסמך JavaScript מתועד בפירוט ב-MDN.
מוסיפים את הטקסט הבא לכיתה _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.'),
),
);
}
הגדרה של קובץ Cookie באמצעות CookieManager
אפשר גם להגדיר קובצי Cookie באמצעות CookieManager באופן הבא.
מוסיפים את הטקסט הבא לכיתה _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.'),
),
);
}
הסרת קובץ Cookie
הסרה של קובץ Cookie כוללת הוספה של קובץ Cookie עם תאריך תפוגה שמוגדר בעבר.
מוסיפים את הטקסט הבא לכיתה _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.'),
),
);
}
הוספת אפשרויות לתפריט של CookieManager
כל מה שנותר הוא להוסיף את אפשרויות התפריט ולחבר אותן לשיטות העזר שהוספתם. מעדכנים את הכיתה _MenuState
באופן הבא:
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.
],
);
}
שימוש ב-CookieManager
כדי להשתמש בכל הפונקציונליות שהוספתם לאפליקציה, נסו את השלבים הבאים:
- בוחרים באפשרות List cookies (רשימת קובצי Cookie). אמורים להופיע בו קובצי ה-Cookie של Google Analytics שהוגדרו על ידי flutter.dev.
- בוחרים באפשרות מחיקת קובצי Cookie. הוא צריך לדווח שקובצי ה-Cookie אכן נוקו.
- בוחרים שוב באפשרות מחיקת קובצי Cookie. הדוח צריך לציין שלא היו קובצי Cookie לניקוי.
- בוחרים באפשרות List cookies (רשימת קובצי Cookie). הכלי אמור לדווח שאין קובצי Cookie.
- לוחצים על הוספת קובץ Cookie. היא צריכה לדווח על קובץ ה-Cookie כקובץ שנוסף.
- בוחרים באפשרות הגדרת קובץ Cookie. הוא אמור לדווח שקובץ ה-Cookie הוגדר.
- בוחרים באפשרות List cookies (רשימת קובצי Cookie), ואז בוחרים באפשרות Remove cookie (הסרת קובץ Cookie).
12. טעינת נכסי Flutter, קבצים ומחרוזות HTML ב-WebView
האפליקציה יכולה לטעון קובצי HTML בשיטות שונות ולהציג אותם ב-WebView. בשלב הזה תטענו נכס Flutter שצוין בקובץ pubspec.yaml
, תטענו קובץ שנמצא בנתיב שצוין ותטענו דף באמצעות מחרוזת HTML.
אם רוצים לטעון קובץ שנמצא בנתיב שצוין, צריך להוסיף את path_provider
אל pubspec.yaml
. זהו פלאגין של Flutter למציאת מיקומים נפוצים במערכת הקבצים.
מריצים את הפקודה הבאה בשורת הפקודה:
$ flutter pub add path_provider
כדי לטעון את הנכס, צריך לציין את הנתיב לנכס ב-pubspec.yaml
. ב-pubspec.yaml
מוסיפים את השורות הבאות:
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.
כדי להוסיף את הנכסים לפרויקט, מבצעים את השלבים הבאים:
- יוצרים ספרייה חדשה בשם
assets
בתיקיית הבסיס של הפרויקט. - יוצרים ספרייה חדשה בשם
www
בתיקייהassets
. - יוצרים ספרייה חדשה בשם
styles
בתיקייהwww
. - יוצרים קובץ חדש בשם
index.html
בתיקייהwww
. - יוצרים קובץ חדש בשם
style.css
בתיקייהstyles
.
מעתיקים את הקוד הבא ומדביקים אותו בקובץ index.html
:
assets/www/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>
כדי להגדיר את הסגנון של כותרת ה-HTML, משתמשים בשורות הבאות בקובץ style.css:
assets/www/styles/style.css
h1 {
color: blue;
}
עכשיו, אחרי שהגדרתם את הנכסים והם מוכנים לשימוש, אתם יכולים להטמיע את השיטות שנדרשות לטעינה ולהצגה של נכסי Flutter, קבצים או מחרוזות HTML.
טעינת נכס Flutter
כדי לטעון את הנכס שיצרתם, כל מה שצריך לעשות הוא להפעיל את השיטה loadFlutterAsset
באמצעות WebViewController
ולציין כפרמטר את הנתיב לנכס. מוסיפים את השיטה הבאה בסוף הקוד:
lib/src/menu.dart
Future<void> _onLoadFlutterAssetExample(
WebViewController controller, BuildContext context) async {
await controller.loadFlutterAsset('assets/www/index.html');
}
טעינת קובץ מקומי
כדי לטעון קובץ במכשיר, אפשר להוסיף שיטה שתשתמש בשיטה loadFile
, שוב באמצעות WebViewController
, שמקבלת String
שמכיל את הנתיב לקובץ.
קודם צריך ליצור קובץ שמכיל את קוד ה-HTML. כדי לעשות את זה, מוסיפים את קוד ה-HTML כמחרוזת בראש הקוד בקובץ menu.dart
, ממש מתחת לייבוא.
lib/src/menu.dart
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.
כדי ליצור File
ולכתוב את מחרוזת ה-HTML בקובץ, מוסיפים שתי שיטות. הפונקציה _onLoadLocalFileExample
תטען את הקובץ על ידי ציון הנתיב כמחרוזת שמוחזרת על ידי השיטה _prepareLocalFile()
. מוסיפים את השיטות הבאות לקוד:
lib/src/menu.dart
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;
}
טעינת מחרוזת HTML
הצגת דף באמצעות מחרוזת HTML היא פשוטה למדי. ל-WebViewController
יש שיטה שנקראת loadHtmlString
, שאפשר להשתמש בה כדי להעביר את מחרוזת ה-HTML כארגומנט. הדף ב-HTML שצוין יוצג ב-WebView
. מוסיפים את השיטה הבאה לקוד:
lib/src/menu.dart
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.
הוספת הפריטים לתפריט
אחרי שהנכסים מוגדרים ומוכנים לשימוש, והשיטות עם כל הפונקציונליות נוצרות, אפשר לעדכן את התפריט. מוסיפים את הערכים הבאים לספירה _MenuOptions
:
lib/src/menu.dart
enum _MenuOptions {
navigationDelegate,
userAgent,
javascriptChannel,
listCookies,
clearCookies,
addCookie,
setCookie,
removeCookie,
// Add from here ...
loadFlutterAsset,
loadLocalFile,
loadHtmlString,
// ... to here.
}
אחרי שמעדכנים את ה-enum, אפשר להוסיף את האפשרויות בתפריט ולקשר אותן לשיטות העזר שהוספתם. מעדכנים את הכיתה _MenuState
באופן הבא:
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.
],
);
}
בדיקת הנכסים, הקובץ ומחרוזת ה-HTML
כדי לבדוק אם הקוד שהטמעתם פועל, אתם יכולים להריץ את הקוד במכשיר שלכם וללחוץ על אחת מהאפשרויות החדשות שנוספו לתפריט. שימו לב איך התג _onLoadFlutterAssetExample
משתמש בתג style.css
שהוספנו כדי לשנות את הכותרת של קובץ ה-HTML לצבע כחול.
13. הכול מוכן!
חדשות טובות!!! סיימתם את ה-Codelab. אפשר למצוא את הקוד המלא של ה-Codelab הזה במאגר ה-Codelab.
כדי לקבל מידע נוסף, אפשר לנסות את הסדנאות האחרות ללימוד Flutter.