1. מבוא
עדכון אחרון: 19.10.2021
באמצעות הפלאגין של WebView Flutter, אפשר להוסיף ווידג'ט WebView לאפליקציית Flutter ל-Android או ל-iOS. ב-iOS, הווידג'ט WebView מגובה על ידי WKWebView, ואילו ב-Android, הווידג'ט של WebView מגובה על ידי WebView. הפלאגין יכול לעבד ווידג'טים של Flutter בתצוגת האינטרנט. לדוגמה, אפשר להציג תפריט נפתח מעל תצוגת אינטרנט.
מה תפַתחו
ב-Codelab הזה תבנה אפליקציה לנייד, שלב אחרי שלב, עם WebView באמצעות Flutter SDK. האפליקציה שלך:
- הצגת תוכן מהאינטרנט ב-
WebView
- הצגת ווידג'טים של Flutter מעל ל-
WebView
- תגובה לאירועי התקדמות של טעינת דף
- אפשר לשלוט ב
WebView
באמצעותWebViewController
- חסימת אתרים באמצעות
NavigationDelegate
- חשבו ביטויים ב-JavaScript
- טיפול בקריאות חוזרות מ-JavaScript באמצעות
JavascriptChannels
- הגדרה, הסרה, הוספה או הצגה של קובצי Cookie
- טעינה והצגה של HTML מתוך נכסים, קבצים או מחרוזות המכילות HTML
מה תלמדו
ב-Codelab הזה תלמדו איך להשתמש בפלאגין webview_flutter
במגוון דרכים, כולל:
- איך להגדיר את הפלאגין
webview_flutter
- איך להאזין לאירועי התקדמות של טעינת דף
- איך לשלוט בניווט בדפים
- איך להורות על ה-
WebView
לחזור אחורה וקדימה בהיסטוריה שלו - איך לבדוק את JavaScript, כולל שימוש בתוצאות שהוחזרו
- איך רושמים קריאות חוזרות (callback) לקריאה של קוד Drt מ-JavaScript
- כיצד לנהל קובצי cookie
- איך לטעון ולהציג דפי HTML מנכסים, מקבצים או ממחרוזות שמכילות HTML
למה תזדקק?
- Android Studio 4.1 ואילך (לפיתוח Android)
- Xcode 12 ואילך (לפיתוח iOS)
- Flutter SDK
- עורך קוד, כמו Android Studio, Visual Studio Code או Emacs.
2. הגדרת סביבת הפיתוח של Flutter
כדי להשלים את שיעור ה-Lab הזה אתם צריכים שתי תוכנות: Flutter SDK וכלי עריכה.
אפשר להריץ את Codelab באמצעות כל אחד מהמכשירים הבאים:
- מכשיר פיזי שמשמש ל-Android או ל-iOS שמחובר למחשב ומוגדר ל'מצב פיתוח'.
- הסימולטור של iOS (צריך להתקין כלים של Xcode).
- האמולטור של Android (נדרשת הגדרה ב-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 כתלות
באמצעות חבילות Pub אפשר להוסיף יכולות נוספות לאפליקציית Flutter. ב-Codelab הזה מוסיפים את הפלאגין webview_flutter
לפרויקט. מריצים את הפקודות הבאות בטרמינל.
$ 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.
אם בודקים את ה-pubspec.yaml, רואים עכשיו שיש בו שורה בקטע יחסי התלות של הפלאגין webview_flutter
.
הגדרת Android minSDK
כדי להשתמש בפלאגין 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 מושבת כרגע ועיבוד flutter.dev כראוי דורש JavaScript.
הפעלת האפליקציה
מפעילים את אפליקציית 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
באמצעות קריאה חוזרת (callback). האופי האסינכרוני של זמינות נאמן המידע הזה הופך אותו למועמד העיקרי לכיתה Completer<T>
האסינכרונית של דארט.
מעדכנים את 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
.
הוספת פקדי ניווט ל-AppBar
עכשיו יש לך את הגרסה המעודכנת של 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. מעקב אחר הניווט באמצעות התכונה 'גישה אוטומטית'
WebView
מספק לאפליקציה NavigationDelegate,
שמאפשר לאפליקציה לעקוב אחר הניווט בדפים בווידג'ט WebView
ולשלוט בו. כשניווט מופעל על ידי WebView,
, לדוגמה, כשמשתמש לוחץ על קישור, מתבצעת קריאה לפעולה של NavigationDelegate
. אפשר להשתמש בקריאה החוזרת (callback) של NavigationDelegate
כדי לקבוע אם WebView
ימשיך בניווט.
רישום נציג ניווט מותאם אישית
בשלב הזה, המערכת תרשום קריאה חוזרת (callback) של NavigationDelegate
כדי לחסום את הניווט אל 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
בשלבים הבאים תיצרו לחצן תפריט בווידג'ט 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'),
),
],
);
}
}
כשהמשתמש בוחר באפשרות ניווט ל-YouTube בתפריט, המערכת מפעילה את השיטה loadRequest
של WebViewController
. הניווט הזה ייחסם על ידי הקריאה החוזרת (callback) של 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),
);
}
}
מפעילים את האפליקציה ומקישים על האפשרות ניווט ל-YouTube בתפריט. אמור להופיע לידך סרגל SnackBar, שיודיע לך שבקר הניווט חסם את האפשרות לנווט אל 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, אפשר להוסיף לתפריט אפשרות להשתמש ב-method runJavaScriptReturningResult
.
באמצעות Editor או פעולות מקלדת מסוימות, ממירים את המחלקה (תפריט) ל-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.
],
);
}
}
כשמקישים על 'הצגת סוכן משתמש' האפשרות בתפריט, התוצאה של הרצת ביטוי ה-JavaScript navigator.userAgent
מוצגת ב-Snackbar
. כשתפעילו את האפליקציה, ייתכן שהדף Flutter.dev נראה שונה. זאת התוצאה של הרצה כש-JavaScript מופעל.
10. עבודה עם ערוצי JavaScript
ערוצי JavaScript מאפשרים לאפליקציה שלך לרשום מטפלים בקריאה חוזרת (callback) בהקשר של WebViewWidget
ב-JavaScript, שניתן להפעיל כדי להעביר את הערכים בחזרה לקוד Drt של האפליקציה. בשלב הזה המערכת תרשום ערוץ 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 כדי לשלוח הודעה שמועברת ל-handler של הקריאה החוזרת onMessageReceived
של JavascriptChannel
.
כדי להשתמש בערוץ 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 (דוגמה לערוץ 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();
הקוד הזה שולח בקשת GET
ל-Public Address API ומחזיר את כתובת ה-IP של המכשיר. התוצאה הזו מוצגת בSnackBar
על ידי הפעלת postMessage
בJavascriptChannel
של SnackBar
.
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 למחלקה של המדינה שמתקבלת באופן הבא:
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) {
// ...
המחלקה _MenuState
תכיל את הקוד שנוסף בעבר בכיתה Menu
, יחד עם הקוד החדש CookieManager
שנוסף. בסדרת הקטעים הבאה, תוסיפו פונקציות מסייעות ל-_MenuState
, שכתוצאה מכך יופעלו על ידי אפשרויות בתפריט שעדיין לא נוספו.
קבלת רשימה של כל קובצי ה-Cookie
בכוונתך להשתמש ב-JavaScript כדי לקבל רשימה של כל קובצי ה-cookie. כדי לעשות זאת, צריך להוסיף שיטה עוזרת לסוף הכיתה _MenuState
, שנקראת _onListCookies
. באמצעות השיטה 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
כדי להשתמש בכל הפונקציונליות שהוספתם עכשיו לאפליקציה, נסו את השלבים הבאים:
- בוחרים באפשרות הצגת רשימה של קובצי Cookie. הקובץ צריך לכלול רשימה של קובצי ה-Cookie של Google Analytics שהוגדרו על ידי Flutter.dev.
- בוחרים באפשרות ניקוי קובצי cookie. הוא אמור לדווח שקובצי ה-cookie אכן נוקו.
- בוחרים שוב באפשרות ניקוי קובצי cookie. אמור להתקבל דיווח שאין קובצי Cookie זמינים לניקוי.
- בוחרים באפשרות הצגת רשימה של קובצי Cookie. אמור להתקבל דיווח שאין קובצי Cookie.
- בוחרים באפשרות הוספת קובץ Cookie. הוא אמור לדווח על קובץ ה-cookie כפי שנוסף.
- בוחרים באפשרות הגדרת קובץ Cookie. היא אמורה לדווח על קובץ ה-cookie כפי שהוגדר.
- בוחרים באפשרות List cookies (רשימת קובצי cookie) ולאחר מכן לוחצים על Remove cookie (הסרת קובץ cookie).
12. לטעון נכסים, קבצים ומחרוזות HTML של Flutter ב-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
:
<!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>
עבור style.css, השתמשו במספר השורות הבאות כדי להגדיר את סגנון כותרת ה-HTML:
h1 {
color: blue;
}
עכשיו, כשהנכסים מוכנים ומוכנים לשימוש, אתם יכולים להטמיע את השיטות הנדרשות לטעינה ולהצגה של נכסים, קבצים או מחרוזות HTML של Flutter.
טעינת נכס של Flutter
כדי לטעון את הנכס שיצרתם, כל מה שצריך לעשות הוא להפעיל את השיטה loadFlutterAsset
באמצעות WebViewController
ולציין את הנתיב לנכס כפרמטר. מוסיפים את השיטה הבאה בסוף הקוד:
lib/src/menu.dart
Future<void> _onLoadFlutterAssetExample(
WebViewController controller, BuildContext context) async {
await controller.loadFlutterAsset('assets/www/index.html');
}
טעינת קובץ מקומי
כדי לטעון קובץ במכשיר, אפשר להוסיף שיטה שתשתמש ב-method loadFile
. שוב, על ידי שימוש ב-WebViewController
שלוקחת את ה-String
שמכיל את הנתיב לקובץ.
קודם אתם צריכים ליצור קובץ שמכיל את קוד ה-HTML. אפשר פשוט להוסיף את קוד ה-HTML כמחרוזת בחלק העליון של הקוד, בקובץ 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
יטען את הקובץ על ידי ציון הנתיב כמחרוזת שמוחזרת באמצעות ה-method _prepareLocalFile()
. מוסיפים לקוד את השיטות הבאות:
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 כארגומנט. לאחר מכן, WebView
יציג את דף ה-HTML שסופק. מוסיפים לקוד את השיטה הבאה:
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.
הוספת האפשרויות לתפריט
עכשיו, אחרי שהנכסים מוכנים ומוכנים לשימוש, והשיטות עם כל הפונקציונליות נוצרו, אפשר לעדכן את התפריט. מוסיפים את הערכים הבאים ל-enum _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 codelabs האחרות.