Dodawanie komponentu WebView do aplikacji Flutter

Dodawanie WebView do aplikacji Flutter

Informacje o tym ćwiczeniu (w Codelabs)

subjectOstatnia aktualizacja: wrz 12, 2025
account_circleAutorzy: Maurits van Beusekom, Floris Smit and Jan-Derk de Vries

1. Wprowadzenie

Ostatnia aktualizacja: 2021-10-19

Za pomocą wtyczki WebView Flutter możesz dodać widżet WebView do aplikacji Flutter na Androida lub iOS. Na iOS widżet WebView jest obsługiwany przez WKWebView, a na Androidzie przez WebView. Wtyczka może renderować widżety Fluttera w widoku internetowym. Można na przykład renderować menu w widoku internetowym.

W tym laboratorium kodowania krok po kroku utworzysz aplikację mobilną z komponentem WebView za pomocą pakietu Flutter SDK. Twoja aplikacja będzie:

  • Wyświetlanie treści w WebView
  • Wyświetlanie widżetów Fluttera ułożonych na WebView
  • Reagowanie na zdarzenia postępu wczytywania strony
  • Sterowanie WebView za pomocą WebViewController
  • Blokowanie witryn za pomocą NavigationDelegate
  • Obliczanie wartości wyrażeń JavaScript
  • Obsługa wywołań zwrotnych z JavaScriptu za pomocą JavascriptChannels
  • Ustawianie, usuwanie, dodawanie i wyświetlanie plików cookie
  • Wczytywanie i wyświetlanie kodu HTML z zasobów, plików lub ciągów znaków zawierających kod HTML

Symulator iPhone’a z aplikacją Flutter z osadzonym widokiem internetowym, na którym wyświetla się strona główna Flutter.dev

Emulator Androida z aplikacją Flutter z osadzonym widokiem internetowym, na którym wyświetla się strona główna Flutter.dev

Czego się nauczysz

Z tego ćwiczenia z programowania dowiesz się, jak korzystać z webview_flutterwtyczki na różne sposoby, m.in.:

  • Jak skonfigurować wtyczkę webview_flutter
  • Jak nasłuchiwać zdarzeń postępu wczytywania strony
  • Jak sterować nawigacją po stronie
  • jak cofać i przewijać historię WebView,
  • Jak oceniać kod JavaScript, w tym korzystać z zwróconych wyników
  • Rejestrowanie wywołań zwrotnych do wywoływania kodu Dart z JavaScriptu
  • Zarządzanie plikami cookie
  • Jak wczytywać i wyświetlać strony HTML z zasobów, plików lub ciągu znaków zawierającego kod HTML

Czego potrzebujesz

2. Konfigurowanie środowiska programistycznego Fluttera

Aby ukończyć ten moduł, potrzebujesz 2 programów: pakietu Flutter SDKedytora.

Codelab możesz uruchomić na dowolnym z tych urządzeń:

  • fizyczne urządzenie z Android lub iOS podłączone do komputera i ustawione w trybie deweloperskim;
  • Symulator iOS (wymaga zainstalowania narzędzi Xcode).
  • Emulator Androida (wymaga konfiguracji w Android Studio).

3. Pierwsze kroki

Pierwsze kroki z Flutterem

Nowy projekt Fluttera można utworzyć na różne sposoby. Zarówno Android Studio, jak i Visual Studio Code udostępniają narzędzia do tego zadania. Aby utworzyć projekt, wykonaj procedury, do których prowadzą linki, lub uruchom te polecenia w wygodnym terminalu wiersza poleceń.

$ 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.

Dodawanie wtyczki WebView Flutter jako zależności

Najlepszym sposobem na dodanie dodatkowych funkcji do aplikacji Flutter jest użycie pakietów Pub. W tym laboratorium kodowania dodasz do projektu webview_flutter wtyczkę. Uruchom w terminalu te polecenia.

$ 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.

Jeśli sprawdzisz plik pubspec.yaml, zobaczysz, że w sekcji zależności znajduje się wiersz wtyczki webview_flutter.

Konfigurowanie minimalnej wersji pakietu SDK na Androida

Aby używać wtyczki webview_flutter na Androidzie, musisz ustawić wartość minSDK na 20. Zmodyfikuj plik android/app/build.gradle w ten sposób:

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. Dodawanie widżetu WebView do aplikacji Flutter

W tym kroku dodasz do aplikacji element WebView. WebView to hostowane widoki wbudowane, a Ty jako deweloper aplikacji możesz wybrać sposób hostowania tych widoków wbudowanych w aplikacji. Na Androidzie możesz wybrać między wyświetlaczami wirtualnymi (domyślnie w Androidzie) a kompozycją hybrydową. iOS zawsze używa jednak kompozycji hybrydowej.

Szczegółowe omówienie różnic między wirtualnymi wyświetleniami a kompozycją hybrydową znajdziesz w dokumentacji na temat hostowania natywnych widoków Androida i iOS w aplikacji Flutter za pomocą widoków platformy .

Wyświetlanie widoku WebView na ekranie

Zamień zawartość pliku lib/main.dart na taką:

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,
     
),
   
);
 
}
}

Uruchomienie tego na urządzeniu z iOS lub Androidem spowoduje wyświetlenie elementu WebView jako pełnoekranowego okna przeglądarki, co oznacza, że przeglądarka będzie wyświetlana na urządzeniu w trybie pełnoekranowym bez żadnych obramowań ani marginesów. Podczas przewijania możesz zauważyć fragmenty strony, które wyglądają nieco dziwnie. Dzieje się tak, ponieważ JavaScript jest wyłączony, a prawidłowe renderowanie strony flutter.dev wymaga jego włączenia.

Uruchamianie aplikacji

Uruchom aplikację Flutter na iOS lub Androidzie, aby zobaczyć widok Webview, który wyświetla witrynę flutter.dev. Możesz też uruchomić aplikację w emulatorze Androida lub symulatorze iOS. Możesz zastąpić początkowy adres URL WebView np. adresem swojej witryny.

$ flutter run

Zakładając, że masz uruchomiony odpowiedni symulator lub emulator albo podłączone urządzenie fizyczne, po skompilowaniu i wdrożeniu aplikacji na urządzeniu powinna pojawić się mniej więcej taka zawartość:

Symulator iPhone’a z aplikacją Flutter z osadzonym widokiem internetowym, na którym wyświetla się strona główna Flutter.dev

Emulator Androida z aplikacją Flutter z osadzonym widokiem internetowym, na którym wyświetla się strona główna Flutter.dev

5. Nasłuchiwanie zdarzeń wczytania strony

Widżet WebView udostępnia kilka zdarzeń postępu wczytywania strony, których może nasłuchiwać Twoja aplikacja. Podczas WebView cyklu ładowania strony wywoływane są 3 różne zdarzenia ładowania strony: onPageStarted, onProgressonPageFinished. W tym kroku zaimplementujesz wskaźnik ładowania strony. Dodatkowo pokaże to, że możesz renderować treści Fluttera w obszarze treści WebView.

Dodawanie do aplikacji zdarzeń wczytania strony

Utwórz nowy plik źródłowy w lokalizacji lib/src/web_view_stack.dart i wypełnij go tą treścią:

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,
         
),
     
],
   
);
 
}
}

Ten kod otacza widżet WebView elementem Stack, warunkowo nakładając element WebView na element LinearProgressIndicator, gdy odsetek wczytania strony jest mniejszy niż 100%. Ponieważ wiąże się to ze stanem programu, który zmienia się z czasem, ten stan jest przechowywany w klasie State powiązanej z StatefulWidget.

Aby użyć nowego widżetu WebViewStack, zmodyfikuj plik lib/main.dart w ten sposób:

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(),
   
);
 
}
}

Po uruchomieniu aplikacji, w zależności od warunków sieciowych i tego, czy przeglądarka ma w pamięci podręcznej stronę, do której przechodzisz, zobaczysz wskaźnik ładowania strony nałożony na obszar treści WebView.

6. Praca z kontrolerem WebViewController

Uzyskiwanie dostępu do WebViewController z widżetu WebView

Widżet WebView umożliwia programowe sterowanie za pomocą WebViewController. Ten kontroler jest udostępniany po utworzeniu widżetu WebView za pomocą wywołania zwrotnego. Asynchroniczny charakter dostępności tego kontrolera sprawia, że jest on doskonałym kandydatem do użycia w asynchronicznej klasie Completer<T> w Dart.

Zaktualizuj lib/src/web_view_stack.dart w ten sposób:

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,
         
),
     
],
   
);
 
}
}

Widżet WebViewStack korzysta teraz z kontrolera utworzonego w otaczającym go widżecie. Umożliwi to udostępnianie kontrolera WebViewWidget innym częściom aplikacji.

Tworzenie elementów sterujących nawigacją

Działający WebView to jedno, ale możliwość przechodzenia w przód i w tył w historii strony oraz ponownego jej wczytywania to przydatne dodatki. Na szczęście dzięki WebViewController możesz dodać tę funkcję do swojej aplikacji.

Utwórz nowy plik źródłowy w lokalizacji lib/src/navigation_controls.dart i wypełnij go tymi informacjami:

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();
         
},
       
),
     
],
   
);
 
}
}

Ten widżet używa WebViewController udostępnionego mu w momencie tworzenia, aby umożliwić użytkownikowi sterowanie WebView za pomocą serii IconButton.

Dodawanie elementów sterujących nawigacją do paska aplikacji

Po zapoznaniu się ze zaktualizowanym WebViewStack i nowym NavigationControls możesz teraz połączyć je w zaktualizowanym WebViewApp. W tym miejscu tworzymy wspólny WebViewController. Skoro w tej aplikacji element WebViewApp znajduje się w górnej części drzewa widżetów, warto go utworzyć na tym poziomie.

Zaktualizuj plik lib/main.dart w ten sposób:

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
   
);
 
}
}

Po uruchomieniu aplikacji powinna wyświetlić się strona internetowa z elementami sterującymi:

Symulator iPhone’a z aplikacją Flutter z osadzonym widokiem internetowym, który wyświetla stronę główną Flutter.dev z elementami sterującymi poprzednią stroną, następną stroną i ponownym wczytaniem strony

Emulator Androida z aplikacją Fluttera z osadzonym widokiem internetowym, na którym widać stronę główną Flutter.dev z elementami sterującymi poprzednią stroną, następną stroną i ponownym wczytaniem strony

7. Śledzenie nawigacji za pomocą NavigationDelegate

WebView udostępnia aplikacji NavigationDelegate,, który umożliwia jej śledzenie i kontrolowanie nawigacji po stronach widżetu WebView. Gdy nawigacja jest inicjowana przez WebView,, na przykład gdy użytkownik kliknie link, wywoływana jest funkcja NavigationDelegate. Wywołanie zwrotne NavigationDelegate może służyć do kontrolowania, czy WebView ma kontynuować nawigację.

Rejestrowanie niestandardowego delegata NavigationDelegate

W tym kroku zarejestrujesz NavigationDelegate wywołanie zwrotne, aby zablokować nawigację do YouTube.com. Pamiętaj, że ta uproszczona implementacja blokuje też treści wbudowane w YouTube, które pojawiają się na różnych stronach dokumentacji interfejsu Flutter API.

Zaktualizuj lib/src/web_view_stack.dart w ten sposób:

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,
         
),
     
],
   
);
 
}
}

W następnym kroku dodasz element menu, aby umożliwić testowanie NavigationDelegate za pomocą klasy WebViewController. Pozostawiamy czytelnikowi zadanie rozszerzenia logiki wywołania zwrotnego, aby blokować tylko nawigację na pełnej stronie do YouTube.com, ale nadal zezwalać na treści wbudowane w YouTube w dokumentacji interfejsu API.

8. Dodawanie przycisku menu do paska aplikacji

W kolejnych krokach utworzysz w widżecie AppBar przycisk menu, który będzie służyć do sprawdzania kodu JavaScript, wywoływania kanałów JavaScript i zarządzania plikami cookie. Podsumowując, jest to przydatne menu.

Utwórz nowy plik źródłowy w lokalizacji lib/src/menu.dart i wypełnij go tymi informacjami:

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'),
       
),
     
],
   
);
 
}
}

Gdy użytkownik wybierze opcję menu Przejdź do YouTube, zostanie wykonana metoda WebViewControllerloadRequest. Nawigacja zostanie zablokowana przez wywołanie zwrotne navigationDelegate utworzone w poprzednim kroku.

Aby dodać menu do ekranu WebViewApp, zmodyfikuj lib/main.dart w ten sposób:

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),
   
);
 
}
}

Uruchom aplikację i kliknij pozycję menu Navigate to YouTube (Przejdź do YouTube). Powinien pojawić się SnackBar z informacją, że kontroler nawigacji zablokował przejście do YouTube.

Emulator Androida z aplikacją Flutter z osadzonym widokiem internetowym wyświetlającym stronę główną Flutter.dev z elementem menu z opcją „Przejdź do YouTube”.

Emulator Androida z aplikacją Flutter z osadzonym widokiem internetowym, na którym wyświetla się strona główna Flutter.dev z wyskakującym powiadomieniem „Blokowanie nawigacji do m.youtube.com”

9. Ocena kodu JavaScript

WebViewController może oceniać wyrażenia JavaScript w kontekście bieżącej strony. JavaScript można oceniać na 2 sposoby: w przypadku kodu JavaScript, który nie zwraca wartości, użyj runJavaScript, a w przypadku kodu JavaScript, który zwraca wartość, użyj runJavaScriptReturningResult.

Aby włączyć JavaScript, musisz skonfigurować WebViewController z właściwością javaScriptMode ustawioną na JavascriptMode.unrestricted. Domyślnie parametr javascriptMode ma wartość JavascriptMode.disabled.

Zaktualizuj klasę _WebViewStackState, dodając ustawienie javascriptMode w ten sposób:

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,
          ),
      ],
    );
  }
}

Teraz, gdy WebViewWidget może wykonywać JavaScript, możesz dodać do menu opcję użycia metody runJavaScriptReturningResult.

Korzystając z edytora lub klawiatury, przekształć klasę Menu w klasę StatefulWidget. Zmodyfikuj lib/src/menu.dart tak, aby pasowało do tego:

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.
     
],
   
);
 
}
}

Gdy klikniesz opcję menu „Pokaż ciąg tekstowy User-Agent”, w elemencie Snackbar pojawi się wynik wykonania wyrażenia JavaScript navigator.userAgent. Po uruchomieniu aplikacji możesz zauważyć, że strona Flutter.dev wygląda inaczej. Oto wynik działania przy włączonym JavaScript.

Symulator iPhone&#39;a z aplikacją Flutter z osadzonym widokiem WebView, który wyświetla stronę główną Flutter.dev z elementami menu zawierającymi opcje „Przejdź do YouTube” i „Pokaż ciąg tekstowy User-Agent”.

Symulator iPhone’a z aplikacją Flutter z osadzonym widokiem internetowym, który wyświetla stronę główną Flutter.dev z wyskakującym okienkiem z ciągiem tekstowym agenta użytkownika.

10. Praca z kanałami JavaScript

Kanały JavaScript umożliwiają aplikacji rejestrowanie w kontekście JavaScriptu WebViewWidget procedur obsługi wywołań zwrotnych, które można wywoływać w celu przekazywania wartości z powrotem do kodu Dart aplikacji. W tym kroku zarejestrujesz kanał SnackBar, który będzie wywoływany z wynikiem XMLHttpRequest.

Zaktualizuj klasę WebViewStack w ten sposób:

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,
          ),
      ],
    );
  }
}

W przypadku każdego kanału JavaScript w Set w kontekście JavaScript jest dostępny obiekt kanału jako właściwość okna o nazwie takiej samej jak nazwa kanału JavaScript name. Użycie tej funkcji w kontekście JavaScriptu polega na wywołaniu funkcji postMessage w kanale JavaScript w celu wysłania wiadomości, która jest przekazywana do wywołania zwrotnego onMessageReceived o nazwie JavascriptChannel.

Aby użyć dodanego wcześniej kanału JavaScript, dodaj kolejny element menu, który wykonuje XMLHttpRequest w kontekście JavaScriptu i przekazuje wyniki za pomocą kanału JavaScript SnackBar.

Teraz, gdy WebViewWidgetwie już o naszych kanałach JavaScript,, dodasz przykład, aby rozwinąć aplikację. Aby to zrobić, dodaj dodatkowy znak PopupMenuItem do klasy Menu i dodaj dodatkową funkcję.

Zaktualizuj _MenuOptions o dodatkową opcję menu, dodając wartość wyliczenia javascriptChannel, i dodaj implementację do klasy Menu w ten sposób:

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.
      ],
    );
  }
}

Ten kod JavaScript jest wykonywany, gdy użytkownik wybierze opcję menu JavaScript Channel Example (Przykład kanału 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();

Ten kod wysyła GET żądanie do interfejsu Public IP Address API, który zwraca adres IP urządzenia. Ten wynik jest wyświetlany w SnackBar przez wywołanie postMessage na SnackBar JavascriptChannel.

11. Zarządzanie plikami cookie

Aplikacja może zarządzać plikami cookie w WebView za pomocą klasy CookieManager. Na tym etapie wyświetlisz listę plików cookie, wyczyścisz ją, usuniesz pliki cookie i ustawisz nowe. Dodaj wpisy do sekcji _MenuOptions dla każdego przypadku użycia plików cookie w ten sposób:

lib/src/menu.dart

enum _MenuOptions {
  navigationDelegate,
  userAgent,
  javascriptChannel,
  // Add from here ...
  listCookies,
  clearCookies,
  addCookie,
  setCookie,
  removeCookie,
  // ... to here.
}

Pozostałe zmiany w tym kroku dotyczą klasy Menu, w tym przekształcenia jej z bezstanowej w stanową.Menu Ta zmiana jest ważna, ponieważ Menu musi być właścicielem CookieManager, a zmienny stan w widżetach bezstanowych to złe połączenie.

Dodaj CookieManager do klasy State w ten sposób:

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) {
  // ...

Klasa _MenuState będzie zawierać kod dodany wcześniej w klasie Menu oraz nowo dodany element CookieManager. W kolejnych sekcjach dodasz do _MenuState funkcje pomocnicze, które będą wywoływane przez dodane później pozycje menu.

Wyświetlanie listy wszystkich plików cookie

Użyjesz JavaScriptu, aby uzyskać listę wszystkich plików cookie. Aby to zrobić, dodaj metodę pomocniczą na końcu klasy _MenuState o nazwie _onListCookies. Za pomocą metody runJavaScriptReturningResult metoda pomocnicza wykonuje document.cookie w kontekście JavaScriptu, zwracając listę wszystkich plików cookie.

Dodaj do klasy _MenuState te elementy:

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.'),
    ),
  );
}

Usuwanie wszystkich plików cookie

Aby wyczyścić wszystkie pliki cookie w obiekcie WebView, użyj metody clearCookies klasy CookieManager. Metoda zwraca obiekt Future<bool>, który przyjmuje wartość true, jeśli CookieManager wyczyścił pliki cookie, a wartość false, jeśli nie było plików cookie do wyczyszczenia.

Dodaj do klasy _MenuState te elementy:

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),
    ),
  );
}

Plik cookie można dodać, wywołując JavaScript. Interfejs API używany do dodawania pliku cookie do dokumentu JavaScript jest szczegółowo opisany w MDN.

Dodaj do klasy _MenuState te elementy:

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.'),
    ),
  );
}

Pliki cookie można też ustawiać za pomocą interfejsu CookieManager w ten sposób:

Dodaj do klasy _MenuState te elementy:

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.'),
    ),
  );
}

Usunięcie pliku cookie polega na dodaniu pliku cookie z datą ważności ustawioną w przeszłości.

Dodaj do klasy _MenuState te elementy:

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.'),
    ),
  );
}

Dodawanie elementów menu CookieManager

Pozostało tylko dodać opcje menu i połączyć je z dodanymi przed chwilą metodami pomocniczymi. Zaktualizuj klasę _MenuState w ten sposób:

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.
      ],
    );
  }

Korzystanie z interfejsu CookieManager

Aby korzystać ze wszystkich funkcji, które właśnie dodano do aplikacji, wykonaj te czynności:

  1. Kliknij List cookies (Wyświetl listę plików cookie). Powinna zawierać listę plików cookie Google Analytics ustawionych przez flutter.dev.
  2. Kliknij Usuń pliki cookie. Powinien on potwierdzić, że pliki cookie zostały usunięte.
  3. Ponownie kliknij Usuń pliki cookie. Powinien on zgłosić, że nie ma plików cookie do wyczyszczenia.
  4. Kliknij List cookies (Wyświetl listę plików cookie). Powinien zgłosić, że nie ma plików cookie.
  5. Kliknij Dodaj plik cookie. Powinien on zgłosić dodanie pliku cookie.
  6. Kliknij Ustaw plik cookie. Powinien zgłosić ustawienie pliku cookie.
  7. Kliknij List cookies (Lista plików cookie), a następnie Remove cookie (Usuń plik cookie).

Emulator Androida z aplikacją Flutter z osadzonym widokiem internetowym, na którym wyświetla się strona główna Flutter.dev z listą opcji menu obejmujących nawigację do YouTube, wyświetlanie agenta użytkownika i interakcję z zasobnikiem plików cookie przeglądarki

Emulator Androida z aplikacją Flutter z osadzonym widokiem internetowym, który wyświetla stronę główną Flutter.dev z wyskakującym okienkiem z informacją o plikach cookie ustawionych w przeglądarce

Emulator Androida z aplikacją Fluttera z osadzonym widokiem internetowym, na którym wyświetla się strona główna Flutter.dev z wyskakującym okienkiem z tekstem „There were cookies” (Wykryto pliki cookie). Teraz już ich nie ma”.

Emulator Androida z aplikacją Flutter z osadzonym widokiem internetowym, który wyświetla stronę główną Flutter.dev z wyskakującym okienkiem z tekstem „Dodano niestandardowy plik cookie”.

12. Wczytywanie zasobów, plików i ciągów HTML Flattera w komponencie WebView

Aplikacja może wczytywać pliki HTML różnymi metodami i wyświetlać je w widoku WebView. W tym kroku wczytasz zasób Fluttera określony w pliku pubspec.yaml, wczytasz plik znajdujący się w określonej ścieżce i wczytasz stronę za pomocą ciągu HTML.

Jeśli chcesz wczytać plik znajdujący się w określonej ścieżce, musisz dodać path_provider do pubspec.yaml. Jest to wtyczka Fluttera do znajdowania często używanych lokalizacji w systemie plików.

W wierszu poleceń uruchom to polecenie:

$ flutter pub add path_provider

Aby wczytać komponent, musimy podać ścieżkę do niego w pubspec.yaml. W pubspec.yaml dodaj te wiersze:

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.

Aby dodać komponenty do projektu, wykonaj te czynności:

  1. Utwórz nowy katalog o nazwie assets w folderze głównym projektu.
  2. Utwórz nowy katalog o nazwie www w folderze assets.
  3. Utwórz nowy katalog o nazwie styles w folderze www.
  4. Utwórz nowy plik o nazwie index.html w folderze www.
  5. Utwórz nowy plik o nazwie style.css w folderze styles.

Skopiuj ten kod i wklej go do pliku 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>

W pliku style.css użyj tych kilku wierszy, aby ustawić styl nagłówka HTML:

assets/www/styles/style.css

h1 {
  color: blue;
}

Gdy zasoby są już skonfigurowane i gotowe do użycia, możesz wdrożyć metody potrzebne do wczytywania i wyświetlania zasobów, plików lub ciągów HTML w Flutterze.

Wczytywanie zasobu Fluttera

Aby wczytać utworzony właśnie komponent, wystarczy wywołać metodę loadFlutterAsset, używając WebViewController, i jako parametr podać ścieżkę do komponentu. Na końcu kodu dodaj tę metodę:

lib/src/menu.dart

Future<void> _onLoadFlutterAssetExample(
    WebViewController controller, BuildContext context) async {
  await controller.loadFlutterAsset('assets/www/index.html');
}

Wczytywanie pliku lokalnego

Aby wczytać plik na urządzeniu, możesz dodać metodę, która będzie korzystać z metody loadFile, ponownie używając WebViewController, która przyjmuje String zawierający ścieżkę do pliku.

Najpierw musisz utworzyć plik zawierający kod HTML. Aby to zrobić, dodaj kod HTML jako ciąg znaków u góry kodu w pliku menu.dart, tuż pod importami.

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.

Aby utworzyć File i zapisać ciąg HTML w pliku, dodaj 2 metody. _onLoadLocalFileExample wczyta plik, podając ścieżkę jako ciąg tekstowy zwracany przez metodę _prepareLocalFile(). Dodaj do kodu te metody:

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;
}

Wczytaj ciąg HTML

Wyświetlanie strony przez podanie ciągu HTML jest dość proste. Obiekt WebViewController ma metodę o nazwie loadHtmlString, w której możesz podać ciąg HTML jako argument. WebView wyświetli podaną stronę HTML. Dodaj do kodu tę metodę:

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.

Dodawanie pozycji menu

Gdy komponenty są już skonfigurowane i gotowe do użycia, a metody ze wszystkimi funkcjami są utworzone, można zaktualizować menu. Dodaj do wyliczenia _MenuOptions te wpisy:

lib/src/menu.dart

enum _MenuOptions {
  navigationDelegate,
  userAgent,
  javascriptChannel,
  listCookies,
  clearCookies,
  addCookie,
  setCookie,
  removeCookie,
  // Add from here ...
  loadFlutterAsset,
  loadLocalFile,
  loadHtmlString,
  // ... to here.
}

Po zaktualizowaniu wyliczenia możesz dodać opcje menu i połączyć je z dodanymi przed chwilą metodami pomocniczymi. Zaktualizuj klasę _MenuState w ten sposób:

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.
      ],
    );
  }

Testowanie komponentów, pliku i ciągu HTML

Aby sprawdzić, czy wdrożony kod działa, możesz go uruchomić na urządzeniu i kliknąć jeden z nowo dodanych elementów menu. Zwróć uwagę, jak tag _onLoadFlutterAssetExample używa dodanego przez nas tagu style.css, aby zmienić kolor nagłówka pliku HTML na niebieski.

Emulator Androida z aplikacją Fluttera z osadzonym widokiem internetowym wyświetlającym stronę z etykietą „Lokalna strona demonstracyjna” i tytułem w kolorze niebieskim

Emulator Androida z aplikacją Flutter z osadzonym widokiem internetowym wyświetlającym stronę z etykietą „Local demo page” (Lokalna strona demonstracyjna) i tytułem w kolorze czarnym.

13. Wszystko gotowe

Gratulacje!!! To już koniec tego laboratorium. Gotowy kod do tego ćwiczenia znajdziesz w repozytorium ćwiczeń.

Aby dowiedzieć się więcej, wypróbuj inne codelaby Fluttera.