WebView zur Flutter-App hinzufügen

1. Einführung

Zuletzt aktualisiert:19.10.2021

Mit dem WebView Flutter-Plug-in können Sie Ihrer Flutter-App für Android oder iOS ein WebView-Widget hinzufügen. Auf iOS-Geräten wird das WebView-Widget durch WKWebView unterstützt, unter Android über ein WebView-Widget. Das Plug-in kann Flutter-Widgets in der Webansicht rendern. So ist es beispielsweise möglich, ein Dropdown-Menü über der Webansicht zu rendern.

Aufgaben

In diesem Codelab erstellen Sie mithilfe des Flutter SDK Schritt für Schritt eine mobile App mit WebView. Mit der Anwendung können Sie Folgendes tun:

  • Webinhalte in einem WebView anzeigen
  • Flutter-Widgets einblenden, die über WebView gestapelt sind
  • Auf Ereignisse beim Seitenaufbau reagieren
  • WebView über WebViewController steuern
  • Websites über die NavigationDelegate blockieren
  • JavaScript-Ausdrücke auswerten
  • Callbacks von JavaScript mit JavascriptChannels verarbeiten
  • Cookies setzen, entfernen, hinzufügen oder anzeigen
  • HTML aus Assets, Dateien oder Strings, die HTML enthalten, laden und anzeigen

Screenshot eines iPhone-Simulators, auf dem eine Flutter-App ausgeführt wird. Eingebettetes WebView zeigt die Flutter.dev-Startseite.

Screenshot eines Android-Emulators, der eine Flutter-App mit eingebetteter WebView ausführt, die die Flutter.dev-Startseite zeigt

Aufgaben in diesem Lab

In diesem Codelab erfahren Sie, wie Sie das webview_flutter-Plug-in auf verschiedene Arten verwenden:

  • webview_flutter-Plug-in konfigurieren
  • Auf Fortschrittsereignisse beim Seitenaufbau warten
  • Seitennavigation steuern
  • Mit WebView im Verlauf vor- und zurückspringen
  • JavaScript auswerten und zurückgegebene Ergebnisse verwenden
  • Callbacks registrieren, um Dart-Code aus JavaScript aufzurufen
  • Cookies verwalten
  • HTML-Seiten aus Assets oder Dateien oder einem String mit HTML laden und anzeigen

Voraussetzungen

2. Flutter-Entwicklungsumgebung einrichten

Für dieses Lab benötigen Sie zwei Softwareprogramme: das Flutter SDK und einen Editor.

Sie können das Codelab auf jedem dieser Geräte ausführen:

  • Ein physisches Android- oder iOS, das mit Ihrem Computer verbunden ist und sich im Entwicklermodus befindet.
  • Den iOS-Simulator (erfordert die Installation von Xcode-Tools).
  • Android-Emulator (Einrichtung in Android Studio erforderlich)

3. Erste Schritte

Erste Schritte mit Flutter

Es gibt verschiedene Möglichkeiten, ein neues Flutter-Projekt zu erstellen. Sowohl Android Studio als auch Visual Studio Code bieten Tools für diese Aufgabe. Befolgen Sie entweder die verlinkten Verfahren, um ein Projekt zu erstellen, oder führen Sie die folgenden Befehle in einem praktischen Befehlszeilenterminal aus.

$ 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-Plug-in als Abhängigkeit hinzufügen

Mithilfe von Pub-Paketen können Sie einer Flutter-App ganz einfach zusätzliche Funktionen hinzufügen. In diesem Codelab fügen Sie Ihrem Projekt das webview_flutter-Plug-in hinzu. Führen Sie im Terminal die folgenden Befehle aus.

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

Wenn Sie die Datei pubspec.yaml überprüfen, sehen Sie jetzt, dass sie im Abschnitt „Abhängigkeiten“ für das Plug-in webview_flutter eine Zeile enthält.

minSDK konfigurieren

Wenn Sie das Plug-in „webview_flutter“ unter Android verwenden möchten, müssen Sie „minSDK“ auf 20 setzen. Ändern Sie die Datei android/app/build.gradle so:

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-Widget zur Flutter App hinzufügen

In diesem Schritt fügen Sie Ihrer Anwendung eine WebView hinzu. WebViews sind gehostete native Ansichten und Sie als App-Entwickler können entscheiden, wie diese nativen Ansichten in Ihrer App gehostet werden sollen. Auf Android-Geräten kannst du zwischen virtuellen Displays (derzeit Standard für Android) und hybrider Zusammenstellung wählen. iOS verwendet jedoch immer die hybride Zusammensetzung.

Eine ausführliche Erläuterung der Unterschiede zwischen virtuellen Displays und hybrider Zusammensetzung finden Sie in der Dokumentation zum Hosten nativer Android- und iOS-Ansichten in Ihrer Flutter-App mit Plattformansichten.

WebView auf dem Bildschirm einfügen

Ersetzen Sie den Inhalt von lib/main.dart durch Folgendes:

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

Unter iOS oder Android wird WebView auf Ihrem Gerät als randloses Browserfenster angezeigt. Das bedeutet, dass der Browser auf Ihrem Gerät im Vollbildmodus ohne Rahmen oder Rand dargestellt wird. Wenn Sie scrollen, bemerken Sie, dass Teile der Seite ein wenig seltsam aussehen. Das liegt daran, dass JavaScript derzeit deaktiviert ist und für das korrekte Rendern von flutter.dev JavaScript erforderlich ist.

App ausführen

Führen Sie die Flutter-App auf einem iOS- oder Android-Gerät aus, um eine WebView mit der Website flutter.dev aufzurufen. Alternativ können Sie die App in einem Android-Emulator oder einem iOS-Simulator ausführen. Sie können die ursprüngliche WebView-URL beispielsweise durch Ihre eigene Website ersetzen.

$ flutter run

Wenn Sie nach dem Kompilieren und Bereitstellen der App auf Ihrem Gerät den entsprechenden Simulator oder Emulator ausführen oder ein physisches Gerät angeschlossen ist, sollte Folgendes angezeigt werden:

Screenshot eines iPhone-Simulators, auf dem eine Flutter-App ausgeführt wird. Eingebettetes WebView zeigt die Flutter.dev-Startseite.

Screenshot eines Android-Emulators, der eine Flutter-App mit eingebetteter WebView ausführt, die die Flutter.dev-Startseite zeigt

5. Auf Seitenladeereignisse warten

Das WebView-Widget bietet verschiedene Statusereignisse für den Seitenaufbau, auf die deine App reagieren kann. Während des Seitenladezyklus von WebView werden drei verschiedene Ereignisse zum Seitenaufbau ausgelöst: onPageStarted, onProgress und onPageFinished. In diesem Schritt implementieren Sie eine Anzeige für den Seitenaufbau. Außerdem können Sie so Flutter-Inhalte über den Inhaltsbereich WebView rendern.

Seitenaufbauereignisse zu Ihrer App hinzufügen

Erstellen Sie unter lib/src/web_view_stack.dart eine neue Quelldatei und füllen Sie sie mit dem folgenden Inhalt:

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

Mit diesem Code wurde das WebView-Widget in ein Stack eingebunden. Dabei wird WebView bedingt mit einem LinearProgressIndicator überlagert, wenn der Prozentsatz des Seitenaufbaus weniger als 100 % beträgt. Da es sich hierbei um einen Programmstatus handelt, der sich im Laufe der Zeit ändert, haben Sie diesen Status in einer State-Klasse gespeichert, die mit einer StatefulWidget verknüpft ist.

Um das neue WebViewStack-Widget zu verwenden, ändern Sie lib/main.dart wie folgt:

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

Wenn du die App startest, wird dir abhängig von deinen Netzwerkbedingungen und davon, ob der Browser die Seite, die du aufrufst, im Cache gespeichert, über dem Inhaltsbereich WebView eine Ladeanzeige eingeblendet.

6. Mit WebViewController arbeiten

Auf WebViewController über das WebView-Widget zugreifen

Das WebView-Widget ermöglicht die programmatische Steuerung mit einer WebViewController. Dieser Controller wird nach der Erstellung des WebView-Widgets über einen Callback zur Verfügung gestellt. Da dieser Controller asynchron verfügbar ist, eignet er sich vor allem für die asynchrone Completer<T>-Klasse von Dart.

Aktualisieren Sie lib/src/web_view_stack.dart so:

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

Das WebViewStack-Widget verwendet jetzt einen Controller, der im umgebenden Widget erstellt wurde. Dadurch kann der Controller für das WebViewWidget ganz einfach mit anderen Teilen der App geteilt werden.

Navigationsbedienelemente erstellen

Ein funktionierendes WebView ist eine Sache, aber die Möglichkeit, im Seitenverlauf vorwärts und rückwärts zu navigieren und die Seite neu zu laden, wäre eine Ergänzung. Mit einem WebViewController kannst du deiner App diese Funktion zum Glück hinzufügen.

Erstellen Sie unter lib/src/navigation_controls.dart eine neue Quelldatei und füllen Sie sie mit Folgendem:

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

Dieses Widget verwendet das bei der Erstellung freigegebene WebViewController, damit der Nutzer das WebView über eine Reihe von IconButtons steuern kann.

Navigationssteuerelemente zur AppBar hinzufügen

Nachdem du das aktualisierte WebViewStack und das neue NavigationControls in der Hand hast, ist es jetzt an der Zeit, alles in einem aktualisierten WebViewApp zusammenzustellen. Hier wird die gemeinsame WebViewController erstellt. Da sich WebViewApp in dieser App oben in der Widget-Struktur befindet, ist es sinnvoll, das Widget auf dieser Ebene zu erstellen.

Aktualisieren Sie die Datei lib/main.dart so:

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

Beim Ausführen der App sollte eine Webseite mit Steuerelementen angezeigt werden:

Screenshot eines iPhone-Simulators, auf dem eine Flutter-App mit eingebetteter WebView ausgeführt wird, die die Flutter.dev-Startseite mit Steuerelementen für die vorherige und nächste Seite sowie die Aktualisierung der Seite zeigt

Screenshot eines Android-Emulators, der eine Flutter-App mit einer eingebetteten WebView ausführt, die die Flutter.dev-Startseite mit den Steuerelementen für die vorherige und nächste Seite sowie die Aktualisierung von Seiten zeigt

7. Navigation mit NavigationDelegate im Blick behalten

WebView stellt ein NavigationDelegate, für deine App bereit, mit dem die Seitennavigation des WebView-Widgets erfasst und gesteuert werden kann. Wenn eine Navigation von WebView, initiiert wird, beispielsweise wenn ein Nutzer auf einen Link klickt, wird NavigationDelegate aufgerufen. Mit dem NavigationDelegate-Callback kann gesteuert werden, ob WebView mit der Navigation fortfahren soll.

Benutzerdefiniertes NavigationDelegate registrieren

In diesem Schritt registrierst du einen NavigationDelegate-Callback, um die Navigation zu YouTube.com zu blockieren. Hinweis: Durch diese vereinfachte Implementierung werden auch Inline-YouTube-Inhalte blockiert, die auf verschiedenen Seiten mit der Flutter API-Dokumentation erscheinen.

Aktualisieren Sie lib/src/web_view_stack.dart so:

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

Im nächsten Schritt fügen Sie einen Menüpunkt hinzu, mit dem Sie NavigationDelegate mithilfe der Klasse WebViewController testen können. Es bleibt dem Leser als Übung überlassen, die Logik des Callbacks zu erweitern, sodass nur die gesamte Seitennavigation zu YouTube.com blockiert und der Inline-YouTube-Inhalt weiterhin in der API-Dokumentation zugelassen wird.

8. Hinzufügen einer Menüschaltfläche zur AppBar

In den nächsten Schritten erstellst du eine Menüschaltfläche im AppBar-Widget, mit der du JavaScript evaluieren, JavaScript-Kanäle aufrufen und Cookies verwalten kannst. Alles in allem ein nützliches Menü.

Erstellen Sie unter lib/src/menu.dart eine neue Quelldatei und füllen Sie sie mit Folgendem:

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

Wenn der Nutzer die Menüoption Zu YouTube navigieren auswählt, wird die Methode loadRequest von WebViewController ausgeführt. Diese Navigation wird durch den navigationDelegate-Callback blockiert, den du im vorherigen Schritt erstellt hast.

Wenn Sie das Menü zum Bildschirm von WebViewApp hinzufügen möchten, ändern Sie lib/main.dart so:

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

Starte die App und tippe auf den Menüpunkt Zu YouTube gehen. Du solltest mit einer SnackBar begrüßt werden, in der dir mitgeteilt wird, dass der Navigations-Controller die Navigation zu YouTube blockiert hat.

Screenshot eines Android-Emulators, der eine Flutter-App ausführt, mit eingebettetem WebView, auf dem die Startseite von Flutter.dev mit einem Menüpunkt mit der Option „Zu YouTube navigieren“ zu sehen ist

Screenshot eines Android-Emulators, der eine Flutter-App ausführt, mit eingebettetem WebView, auf dem die Startseite von Flutter.dev mit Pop-up-Fenster „Blockierung der Navigation zu m.youtube.com“ zu sehen ist

9. JavaScript evaluieren

WebViewController kann JavaScript-Ausdrücke im Kontext der aktuellen Seite auswerten. Es gibt zwei Möglichkeiten, JavaScript auszuwerten: Verwenden Sie für JavaScript-Code, der keinen Wert zurückgibt, runJavaScript und für JavaScript-Code, der einen Wert zurückgibt, runJavaScriptReturningResult.

Um JavaScript zu aktivieren, musst du WebViewController mit der Property javaScriptMode auf JavascriptMode.unrestricted konfigurieren. Standardmäßig ist javascriptMode auf JavascriptMode.disabled eingestellt.

Aktualisieren Sie die Klasse _WebViewStackState, indem Sie die Einstellung javascriptMode so hinzufügen:

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

Da WebViewWidget nun JavaScript ausführen kann, können Sie dem Menü eine Option zur Verwendung der runJavaScriptReturningResult-Methode hinzufügen.

Konvertieren Sie die Menu-Klasse mithilfe des Editors oder mithilfe von Tastatureingaben in ein StatefulWidget. Ändern Sie lib/src/menu.dart wie folgt:

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

Wenn Sie auf „User-Agent anzeigen“ Menüoption sein, wird das Ergebnis nach Ausführung des JavaScript-Ausdrucks navigator.userAgent in einem Snackbar angezeigt. Beim Ausführen der App stellen Sie möglicherweise fest, dass die Seite Flutter.dev anders aussieht. Dies ist das Ergebnis der Ausführung mit aktiviertem JavaScript.

Screenshot eines iPhone-Simulators, auf dem eine Flutter-App ausgeführt wird. Eingebettetes WebView zeigt die Startseite von Flutter.dev mit Menüoptionen mit den Optionen „Zu YouTube navigieren“. oder „User-Agent anzeigen“

Screenshot eines iPhone-Simulators, auf dem eine Flutter-App ausgeführt wird. Eingebettetes WebView zeigt die Flutter.dev-Startseite mit Pop-up-Fenster für den User-Agent-String.

10. Arbeiten mit JavaScript-Kanälen

Mithilfe von JavaScript-Kanälen kann deine App Callback-Handler im JavaScript-Kontext von WebViewWidget registrieren, die aufgerufen werden können, um Werte an den Dart-Code der App zurückzusenden. In diesem Schritt registrieren Sie einen SnackBar-Kanal, der mit dem Ergebnis einer XMLHttpRequest aufgerufen wird.

Aktualisieren Sie die WebViewStack-Klasse so:

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

Für jeden JavaScript-Kanal in der Set wird im JavaScript-Kontext ein Kanalobjekt als Fenstereigenschaft mit demselben Namen wie der JavaScript-Kanal name zur Verfügung gestellt. Dazu muss im JavaScript-Kontext postMessage im JavaScript-Kanal aufgerufen werden, um eine Nachricht zu senden, die an den onMessageReceived-Callback-Handler des benannten JavascriptChannel übergeben wird.

Um den oben hinzugefügten JavaScript-Kanal zu nutzen, fügen Sie einen weiteren Menüpunkt hinzu, der ein XMLHttpRequest im JavaScript-Kontext ausführt und die Ergebnisse mithilfe des JavaScript-Kanals SnackBar zurückgibt.

Da WebViewWidget nun mehr über unsere JavaScript-Kanäle weiß,, fügen Sie ein Beispiel hinzu, um die App weiter zu erweitern. Fügen Sie dazu der Menu-Klasse ein zusätzliches PopupMenuItem-Element und die entsprechende Funktion hinzu.

Aktualisieren Sie _MenuOptions mit der zusätzlichen Menüoption, indem Sie den Aufzählungswert javascriptChannel hinzufügen und der Menu-Klasse wie folgt eine Implementierung hinzufügen:

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

Dieses JavaScript wird ausgeführt, wenn der Nutzer die Menüoption JavaScript Channel Example auswählt.

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

Mit diesem Code wird eine GET-Anfrage an eine Public IP Address API gesendet und die IP-Adresse des Geräts zurückgegeben. Dieses Ergebnis wird in einer SnackBar angezeigt, indem postMessage im SnackBar JavascriptChannel aufgerufen wird.

11. Cookies verwalten

Deine App kann Cookies in der WebView mithilfe der Klasse CookieManager verwalten. In diesem Schritt wird eine Liste der Cookies angezeigt und die Liste der Cookies sowie die neuen Cookies werden gelöscht. Fügen Sie dem _MenuOptions für jeden Cookie-Anwendungsfall folgende Einträge hinzu:

lib/src/menu.dart

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

Die restlichen Änderungen in diesem Schritt konzentrieren sich auf die Menu-Klasse, einschließlich der Konvertierung der Menu-Klasse von zustandslos in zustandsorientiert. Diese Änderung ist wichtig, da Menu Inhaber der CookieManager sein muss und der änderbare Status in zustandslosen Widgets keine gute Kombination ist.

Fügen Sie den CookieManager wie folgt zur resultierenden State-Klasse hinzu:

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

Die _MenuState-Klasse enthält den Code, der zuvor in der Menu-Klasse hinzugefügt wurde, sowie die neu hinzugefügte CookieManager. In den nächsten Abschnitten fügen Sie _MenuState Hilfsfunktionen hinzu, die wiederum von den noch nicht hinzugefügten Menüelementen aufgerufen werden.

Liste aller Cookies abrufen

Sie verwenden JavaScript, um eine Liste aller Cookies abzurufen. Dazu fügen Sie am Ende der _MenuState-Klasse eine Hilfsmethode namens _onListCookies hinzu. Mit der Methode runJavaScriptReturningResult führt die Hilfsmethode document.cookie im JavaScript-Kontext aus und gibt eine Liste aller Cookies zurück.

Fügen Sie der Klasse _MenuState Folgendes hinzu:

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

Alle Cookies löschen

Verwenden Sie die Methode clearCookies der Klasse CookieManager, um alle Cookies in WebView zu löschen. Die Methode gibt einen Future<bool> zurück, der in true aufgelöst wird, wenn CookieManager die Cookies gelöscht hat, und false, wenn keine Cookies zum Löschen vorhanden waren.

Fügen Sie der Klasse _MenuState Folgendes hinzu:

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

Das Hinzufügen eines Cookies kann durch Aufrufen von JavaScript erfolgen. Die API, die zum Hinzufügen eines Cookies zu einem JavaScript-Dokument verwendet wird, ist im Detail auf MDN dokumentiert.

Fügen Sie der Klasse _MenuState Folgendes hinzu:

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

Cookies können auch mit CookieManager gesetzt werden. Gehen Sie dazu so vor:

Fügen Sie der Klasse _MenuState Folgendes hinzu:

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

Um ein Cookie zu entfernen, muss ein Cookie hinzugefügt werden, dessen Ablaufdatum in der Vergangenheit liegt.

Fügen Sie der Klasse _MenuState Folgendes hinzu:

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

Elemente des Menüs „CookieManager“ hinzufügen

Nun müssen Sie nur noch die Menüoptionen hinzufügen und sie mit den soeben hinzugefügten Hilfsmethoden verbinden. Aktualisieren Sie die _MenuState-Klasse so:

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 nutzen

Führen Sie die folgenden Schritte aus, um alle Funktionen zu nutzen, die Sie der App gerade hinzugefügt haben:

  1. Wählen Sie Cookies auflisten aus. Hier sollten die von flutter.dev gesetzten Google Analytics-Cookies aufgeführt sein.
  2. Wähle Cookies löschen aus. Es sollte eine Meldung angezeigt werden, dass die Cookies tatsächlich gelöscht wurden.
  3. Wähle noch einmal Cookies löschen aus. Es sollte gemeldet werden, dass keine Cookies zum Löschen verfügbar sind.
  4. Wählen Sie Cookies auflisten aus. Es sollte gemeldet werden, dass keine Cookies vorhanden sind.
  5. Wählen Sie Cookie hinzufügen aus. Das Cookie sollte als hinzugefügt gemeldet werden.
  6. Wähle Cookie setzen aus. Das Cookie sollte als eingestellt gemeldet werden.
  7. Wählen Sie Cookies auflisten und zum Schluss noch Cookie entfernen aus.

Screenshot eines Android-Emulators, der eine Flutter-App ausführt, mit eingebettetem WebView, auf dem die Startseite von Flutter.dev mit einer Liste von Menüoptionen zu sehen ist, die die Navigation zu YouTube, den User-Agent und die Interaktion mit dem Cookie-Jar des Browsers umfassen

Screenshot eines Android-Emulators, der eine Flutter-App ausführt, mit eingebettetem WebView, auf dem die Startseite von Flutter.dev mit einem Pop-up zu sehen ist, in dem die im Browser gesetzten Cookies zu sehen sind

Screenshot eines Android-Emulators, der eine Flutter-App ausführt, mit eingebetteter WebView-Ansicht der Flutter.dev-Startseite mit einem Pop-up mit der Meldung „Es gab Cookies. Jetzt sind sie weg!“

Screenshot eines Android-Emulators, der eine Flutter-App ausführt, mit eingebetteter WebView, auf der die Startseite von Flutter.dev mit einem Pop-up-Fenster mit dem Text „Benutzerdefiniertes Cookie hinzugefügt“ zu sehen ist.

12. Flutter-Assets, Dateien und HTML-Strings in WebView laden

Ihre App kann HTML-Dateien mithilfe verschiedener Methoden laden und in WebView anzeigen. In diesem Schritt laden Sie ein Flutter-Asset aus der Datei pubspec.yaml, laden eine Datei unter dem angegebenen Pfad und laden eine Seite mithilfe eines HTML-Strings.

Wenn Sie eine Datei laden möchten, die sich unter einem bestimmten Pfad befindet, müssen Sie path_provider zum pubspec.yaml hinzufügen. Dies ist ein Flutter-Plug-in zum Suchen häufig verwendeter Speicherorte im Dateisystem.

Führen Sie in der Befehlszeile den folgenden Befehl aus:

$ flutter pub add path_provider

Zum Laden des Assets müssen wir den Pfad zum Asset im pubspec.yaml angeben. Fügen Sie in pubspec.yaml die folgenden Zeilen hinzu:

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.

So fügen Sie Ihrem Projekt die Assets hinzu:

  1. Erstellen Sie im Stammordner Ihres Projekts ein neues Verzeichnis namens assets.
  2. Erstellen Sie im Ordner assets ein neues Verzeichnis namens www.
  3. Erstellen Sie im Ordner www ein neues Verzeichnis namens styles.
  4. Erstellen Sie im Ordner www eine neue File namens index.html.
  5. Erstellen Sie im Ordner styles eine neue File namens style.css.

Kopieren Sie den folgenden Code und fügen Sie ihn in die Datei index.html ein:

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>

Verwenden Sie für die Datei style.css die folgenden Zeilen, um den Stil der HTML-Kopfzeile festzulegen:

assets/www/styles/style.css

h1 {
  color: blue;
}

Nachdem die Assets eingerichtet und einsatzbereit sind, können Sie die Methoden implementieren, die zum Laden und Anzeigen von Flutter-Assets, Dateien oder HTML-Strings erforderlich sind.

Load Flutter-Asset

Um das soeben erstellte Asset zu laden, müssen Sie nur die Methode loadFlutterAsset mit der WebViewController aufrufen und als Parameter den Pfad zum Asset angeben. Fügen Sie am Ende des Codes die folgende Methode hinzu:

lib/src/menu.dart

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

Lokale Datei laden

Zum Laden einer Datei auf Ihrem Gerät können Sie eine Methode hinzufügen, die die Methode loadFile verwendet. Dazu verwenden Sie wieder die WebViewController, die einen String mit dem Pfad zur Datei verwendet.

Sie müssen zuerst eine Datei mit dem HTML-Code erstellen. Dazu fügen Sie den HTML-Code einfach als String oben in Ihrem Code in die Datei menu.dart direkt unter den Importen ein.

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.

Um eine File zu erstellen und den HTML-String in die Datei zu schreiben, fügen Sie zwei Methoden hinzu. _onLoadLocalFileExample lädt die Datei, indem es den Pfad als String angibt, der von der _prepareLocalFile()-Methode zurückgegeben wird. Fügen Sie Ihrem Code die folgenden Methoden hinzu:

lib/src/menu.dart

Future<void> _onLoadLocalFileExample(
    WebViewController controller, BuildContext context) async {
  final String pathToIndex = await _prepareLocalFile();

  await controller.loadFile(pathToIndex);
}

static Future<String> _prepareLocalFile() async {
  final String tmpDir = (await getTemporaryDirectory()).path;
  final File indexFile = File('$tmpDir/www/index.html');

  await Directory('$tmpDir/www').create(recursive: true);
  await indexFile.writeAsString(kExamplePage);

  return indexFile.path;
}

HTML-String laden

Eine Seite mithilfe einer HTML-Zeichenfolge anzuzeigen, ist ziemlich einfach. WebViewController hat eine Methode namens loadHtmlString, bei der Sie den HTML-String als Argument angeben können. WebView zeigt dann die bereitgestellte HTML-Seite an. Fügen Sie Ihrem Code die folgende Methode hinzu:

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.

Artikel auf der Speisekarte hinzufügen

Nachdem die Assets eingerichtet und einsatzbereit sind und die Methoden mit allen Funktionen erstellt sind, kann das Menü aktualisiert werden. Fügen Sie der Aufzählung _MenuOptions die folgenden Einträge hinzu:

lib/src/menu.dart

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

Nachdem die Enum aktualisiert wurde, können Sie die Menüoptionen hinzufügen und mit den soeben hinzugefügten Hilfsmethoden verbinden. Aktualisieren Sie die _MenuState-Klasse so:

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

Assets, Datei und HTML-String testen

Um zu testen, ob der Code funktioniert hat, den Sie gerade implementiert haben, können Sie den Code auf Ihrem Gerät ausführen und auf einen der neu hinzugefügten Menüelemente klicken. Beachten Sie, wie _onLoadFlutterAssetExample das von uns hinzugefügte style.css verwendet, um den Header der HTML-Datei in Blau zu ändern.

Screenshot eines Android-Emulators, der eine Flutter-App ausführt, mit eingebettetem WebView und einer Seite mit dem Titel „Lokale Demoseite“ mit dem Titel in Blau

Screenshot eines Android-Emulators, der eine Flutter-App ausführt, mit eingebettetem WebView und einer Seite mit dem Titel „Lokale Demoseite“ mit dem Titel in Schwarz

13. Fertig

Herzlichen Glückwunsch!!! Du hast das Codelab abgeschlossen. Den fertigen Code für dieses Codelab finden Sie im Codelab-Repository.

Weitere Informationen finden Sie in den anderen Flutter-Codelabs.