Aggiungere WebView all'app Flutter

1. Introduzione

Ultimo aggiornamento: 19/10/2021

Con il plug-in WebView Flutter puoi aggiungere un widget WebView alla tua app Flutter per Android o iOS. Su iOS, il widget WebView è supportato da WKWebView, mentre su Android è supportato da WebView. Il plug-in può eseguire il rendering dei widget Flutter sulla visualizzazione web. Ad esempio, è possibile visualizzare un menu a discesa sopra la visualizzazione web.

Cosa creerai

In questo codelab, creerai passo passo un'app mobile con una WebView utilizzando l'SDK Flutter. La tua app sarà in grado di:

  • Visualizzare contenuti web in un WebView
  • Visualizza i widget Flutter impilati sopra WebView
  • Reagire agli eventi di avanzamento del caricamento della pagina
  • Controlla WebView tramite WebViewController
  • Bloccare i siti web utilizzando NavigationDelegate
  • Valutare le espressioni JavaScript
  • Gestire i callback da JavaScript con JavascriptChannels
  • Impostare, rimuovere, aggiungere o mostrare i cookie
  • Caricare e visualizzare HTML da asset, file o stringhe contenenti HTML

Simulatore di iPhone che esegue un'app Flutter con una WebView incorporata che mostra la home page di Flutter.dev

Emulatore Android che esegue un'app Flutter con una WebView incorporata che mostra la home page di Flutter.dev

Cosa imparerai

In questo codelab imparerai a utilizzare il webview_flutter plug-in in vari modi, tra cui:

  • Come configurare il plug-in webview_flutter
  • Come rilevare gli eventi di avanzamento del caricamento della pagina
  • Come controllare la navigazione nelle pagine
  • Come comandare a WebView di andare avanti e indietro nella cronologia
  • Come valutare JavaScript, incluso l'utilizzo dei risultati restituiti
  • Come registrare i callback per chiamare il codice Dart da JavaScript
  • Come gestire i cookie
  • Come caricare e visualizzare pagine HTML da asset, file o stringhe contenenti HTML

Requisiti

2. Configura l'ambiente di sviluppo Flutter

Per completare questo lab, hai bisogno di due software: l'SDK Flutter e un editor.

Puoi eseguire il codelab utilizzando uno qualsiasi di questi dispositivi:

  • Un dispositivo fisico Android o iOS connesso al computer e impostato sulla modalità sviluppatore.
  • Il simulatore iOS (richiede l'installazione degli strumenti Xcode).
  • L'emulatore Android (richiede la configurazione in Android Studio).

3. Per iniziare

Introduzione a Flutter

Esistono diversi modi per creare un nuovo progetto Flutter. Sia Android Studio che Visual Studio Code forniscono strumenti per questa attività. Segui le procedure collegate per creare un progetto oppure esegui i seguenti comandi in un terminale a riga di comando pratico.

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

Aggiungere il plug-in WebView Flutter come dipendenza

L'aggiunta di funzionalità aggiuntive a un'app Flutter si ottiene al meglio utilizzando i pacchetti Pub. In questo codelab aggiungerai il plug-in webview_flutter al tuo progetto. Esegui questi comandi nel terminale.

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

Se esamini il file pubspec.yaml, vedrai che ora contiene una riga nella sezione delle dipendenze per il plug-in webview_flutter.

Configurare minSDK Android

Per utilizzare il plug-in webview_flutter su Android, devi impostare minSDK su 20. Modifica il file android/app/build.gradle come segue:

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. Aggiunta del widget WebView all'app Flutter

In questo passaggio aggiungerai un WebView alla tua applicazione. Le WebView sono viste integrate ospitate e, in qualità di sviluppatore di app, puoi scegliere come ospitare queste viste integrate nella tua app. Su Android puoi scegliere tra display virtuali, l'impostazione predefinita per Android, e composizione ibrida. Tuttavia, iOS utilizza sempre la composizione ibrida.

Per una discussione approfondita sulle differenze tra display virtuali e composizione ibrida, leggi la documentazione su Hosting di visualizzazioni native di Android e iOS nella tua app Flutter con Platform Views .

Inserimento di una WebView sullo schermo

Sostituisci i contenuti di lib/main.dart come segue:

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

Se lo esegui su iOS o Android, sul tuo dispositivo verrà visualizzata una WebView come finestra del browser a tutta pagina, il che significa che il browser viene visualizzato sul tuo dispositivo a schermo intero senza bordi o margini. Man mano che scorri, noterai parti della pagina che potrebbero sembrare un po' strane. Questo perché JavaScript è disattivato e il rendering corretto di flutter.dev richiede JavaScript.

Esecuzione dell'app

Esegui l'app Flutter in iOS o Android per visualizzare una WebView, che mostra il sito web flutter.dev. In alternativa, esegui l'app in un emulatore Android o in un simulatore iOS. Puoi sostituire l'URL iniziale di WebView con, ad esempio, il tuo sito web.

$ flutter run

Supponendo che sia in esecuzione il simulatore o l'emulatore appropriato o che sia collegato un dispositivo fisico, dopo aver compilato e distribuito l'app sul dispositivo, dovresti visualizzare un risultato simile al seguente:

Simulatore di iPhone che esegue un&#39;app Flutter con una WebView incorporata che mostra la home page di Flutter.dev

Emulatore Android che esegue un&#39;app Flutter con una WebView incorporata che mostra la home page di Flutter.dev

5. In ascolto di eventi di caricamento pagina

Il widget WebView fornisce diversi eventi di avanzamento del caricamento della pagina, che la tua app può ascoltare. Durante il ciclo di caricamento della pagina WebView vengono attivati tre diversi eventi di caricamento della pagina: onPageStarted, onProgress e onPageFinished. In questo passaggio implementerai un indicatore di caricamento della pagina. Inoltre, dimostrerai di poter eseguire il rendering dei contenuti Flutter nell'area dei contenuti WebView.

Aggiungere eventi di caricamento pagina all'app

Crea un nuovo file di origine in lib/src/web_view_stack.dart e inserisci i seguenti contenuti:

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

Questo codice ha racchiuso il widget WebView in un Stack, sovrapponendo in modo condizionale il WebView con un LinearProgressIndicator quando la percentuale di caricamento della pagina è inferiore al 100%. Poiché ciò comporta uno stato del programma che cambia nel tempo, hai memorizzato questo stato in una classe State associata a un StatefulWidget.

Per utilizzare questo nuovo widget WebViewStack, modifica lib/main.dart come segue:

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

Quando esegui l'app, a seconda delle condizioni di rete e se il browser ha memorizzato nella cache la pagina a cui stai navigando, vedrai un indicatore di caricamento della pagina sovrapposto all'area dei contenuti WebView.

6. Utilizzo di WebViewController

Accesso a WebViewController dal widget WebView

Il widget WebView consente il controllo programmatico con un WebViewController. Questo controller viene reso disponibile dopo la costruzione del widget WebView tramite un callback. La natura asincrona della disponibilità di questo controller lo rende un candidato ideale per la classe asincrona Completer<T> di Dart.

Aggiorna lib/src/web_view_stack.dart come segue:

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

Il widget WebViewStack ora utilizza un controller creato nel widget circostante. In questo modo, il controller per WebViewWidget potrà essere condiviso con altre parti dell'app.

Creare controlli di navigazione

Avere un WebView funzionante è una cosa, ma poter navigare avanti e indietro nella cronologia delle pagine e ricaricare la pagina sarebbe un insieme utile di aggiunte. Fortunatamente, con un WebViewController puoi aggiungere questa funzionalità alla tua app.

Crea un nuovo file di origine in lib/src/navigation_controls.dart e inserisci quanto segue:

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

Questo widget utilizza il WebViewController condiviso al momento della creazione per consentire all'utente di controllare WebView tramite una serie di IconButton.

Aggiunta di controlli di navigazione alla barra delle app

Con l'WebViewStack aggiornato e il nuovo NavigationControls in mano, è ora di mettere tutto insieme in un WebViewApp aggiornato. Qui costruiamo il WebViewController condiviso. Con WebViewApp vicino alla parte superiore dell'albero dei widget in questa app, è logico crearlo a questo livello.

Aggiorna il file lib/main.dart come segue:

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

L'esecuzione dell'app dovrebbe mostrare una pagina web con i controlli:

Simulatore di iPhone che esegue un&#39;app Flutter con una webview incorporata che mostra la home page di Flutter.dev con i controlli per la pagina precedente, la pagina successiva e il ricaricamento della pagina

Emulatore Android che esegue un&#39;app Flutter con una webview incorporata che mostra la home page di Flutter.dev con i controlli per la pagina precedente, la pagina successiva e il ricaricamento della pagina

7. Monitorare la navigazione con NavigationDelegate

WebView fornisce alla tua app un NavigationDelegate, che le consente di monitorare e controllare la navigazione delle pagine del widget WebView. Quando una navigazione viene avviata da WebView,, ad esempio quando un utente fa clic su un link, viene chiamato NavigationDelegate. Il callback NavigationDelegate può essere utilizzato per controllare se WebView procede con la navigazione.

Registra un NavigationDelegate personalizzato

In questo passaggio, registrerai un callback NavigationDelegate per bloccare la navigazione su YouTube.com. Tieni presente che questa implementazione semplificata blocca anche i contenuti YouTube incorporati, che vengono visualizzati in varie pagine della documentazione dell'API Flutter.

Aggiorna lib/src/web_view_stack.dart come segue:

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

Nel passaggio successivo, aggiungerai una voce di menu per attivare il test di NavigationDelegate utilizzando la classe WebViewController. Il lettore è invitato a migliorare la logica del callback per bloccare solo la navigazione a schermo intero su YouTube.com e consentire comunque i contenuti di YouTube incorporati nella documentazione dell'API.

8. Aggiunta di un pulsante di menu alla barra delle app

Nei passaggi successivi, creerai un pulsante del menu nel widget AppBar utilizzato per valutare JavaScript, richiamare i canali JavaScript e gestire i cookie. Nel complesso, un menu davvero utile.

Crea un nuovo file di origine in lib/src/menu.dart e inserisci quanto segue:

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

Quando l'utente seleziona l'opzione di menu Vai a YouTube, viene eseguito il metodo loadRequest di WebViewController. Questa navigazione verrà bloccata dal callback navigationDelegate che hai creato nel passaggio precedente.

Per aggiungere il menu alla schermata di WebViewApp, modifica lib/main.dart come segue:

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

Esegui l'app e tocca la voce di menu Vai a YouTube. Dovresti visualizzare una notifica SnackBar che ti informa che il controller di navigazione ha bloccato la navigazione su YouTube.

Emulatore Android che esegue un&#39;app Flutter con una webview incorporata che mostra la home page di Flutter.dev con una voce di menu che mostra l&#39;opzione &quot;Vai su YouTube&quot;

Emulatore Android che esegue un&#39;app Flutter con una webview incorporata che mostra la home page di Flutter.dev con un popup di notifica che indica &quot;Blocco della navigazione su m.youtube.com&quot;

9. Valutazione di JavaScript

WebViewController può valutare le espressioni JavaScript nel contesto della pagina corrente. Esistono due modi diversi per valutare JavaScript: per il codice JavaScript che non restituisce un valore, utilizza runJavaScript e per il codice JavaScript che restituisce un valore, utilizza runJavaScriptReturningResult.

Per attivare JavaScript, devi configurare WebViewController con la proprietà javaScriptMode impostata su JavascriptMode.unrestricted. Per impostazione predefinita, javascriptMode è impostato su JavascriptMode.disabled.

Aggiorna la classe _WebViewStackState aggiungendo l'impostazione javascriptMode come segue:

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

Ora che WebViewWidget può eseguire JavaScript, puoi aggiungere un'opzione al menu per utilizzare il metodo runJavaScriptReturningResult.

Utilizzando l'editor o la tastiera, converti la classe Menu in StatefulWidget. Modifica lib/src/menu.dart in modo che corrisponda a quanto segue:

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

Quando tocchi l'opzione di menu "Mostra user agent", il risultato dell'esecuzione dell'espressione JavaScript navigator.userAgent viene visualizzato in un Snackbar. Quando esegui l'app, potresti notare che la pagina Flutter.dev ha un aspetto diverso. Questo è il risultato dell'esecuzione con JavaScript attivato.

Simulatore di iPhone che esegue un&#39;app Flutter con una WebView incorporata che mostra la home page di Flutter.dev con voci di menu che mostrano le opzioni &quot;Vai su YouTube&quot; o &quot;Mostra user agent&quot;

Simulatore di iPhone che esegue un&#39;app Flutter con una webview incorporata che mostra la home page di Flutter.dev con un popup toast che mostra la stringa user agent.

10. Utilizzo dei canali JavaScript

I canali JavaScript consentono alla tua app di registrare i gestori di callback nel contesto JavaScript di WebViewWidget che possono essere richiamati per trasmettere i valori al codice Dart dell'app. In questo passaggio registrerai un canale SnackBar che verrà chiamato con il risultato di un XMLHttpRequest.

Aggiorna la classe WebViewStack come segue:

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

Per ogni canale JavaScript in Set, un oggetto canale viene reso disponibile nel contesto JavaScript come proprietà della finestra con lo stesso nome del canale JavaScript name. L'utilizzo di questo elemento dal contesto JavaScript comporta la chiamata di postMessage sul canale JavaScript per inviare un messaggio che viene passato al gestore di callback onMessageReceived di JavascriptChannel denominato.

Per utilizzare il canale JavaScript che hai aggiunto in precedenza, aggiungi un'altra voce di menu che esegue un XMLHttpRequest nel contesto JavaScript e restituisce i risultati utilizzando il canale JavaScript SnackBar.

Ora che WebViewWidgetconosce i nostri canali JavaScript,, aggiungerai un esempio per espandere ulteriormente l'app. Per farlo, aggiungi un PopupMenuItem extra alla classe Menu e aggiungi la funzionalità extra.

Aggiorna _MenuOptions con l'opzione di menu aggiuntiva, aggiungendo il valore di enumerazione javascriptChannel e un'implementazione alla classe Menu come segue:

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

Questo codice JavaScript viene eseguito quando l'utente sceglie l'opzione di menu Esempio di canale 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();

Questo codice invia una richiesta GET a un'API per indirizzi IP pubblici, restituendo l'indirizzo IP del dispositivo. Questo risultato viene mostrato in un SnackBar richiamando postMessage sul SnackBar JavascriptChannel.

11. Gestione dei cookie

La tua app può gestire i cookie in WebView utilizzando la classe CookieManager. In questo passaggio, mostrerai un elenco di cookie, lo cancellerai, eliminerai i cookie e ne imposterai di nuovi. Aggiungi voci al _MenuOptions per ciascuno dei casi d'uso dei cookie nel seguente modo:

lib/src/menu.dart

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

Il resto delle modifiche in questo passaggio si concentra sulla classe Menu, inclusa la conversione della classe Menu da stateless a stateful. Questa modifica è importante perché Menu deve possedere CookieManager e lo stato modificabile nei widget stateless è una combinazione negativa.

Aggiungi CookieManager alla classe State risultante nel seguente modo:

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

La classe _MenuState conterrà il codice aggiunto in precedenza nella classe Menu, insieme a CookieManager appena aggiunto. Nella serie successiva di sezioni, aggiungerai funzioni di supporto a _MenuState che, a loro volta, verranno richiamate dalle voci di menu ancora da aggiungere.

Visualizzare un elenco di tutti i cookie

Utilizzerai JavaScript per ottenere un elenco di tutti i cookie. Per farlo, aggiungi un metodo helper alla fine della classe _MenuState, chiamato _onListCookies. Utilizzando il metodo runJavaScriptReturningResult, il metodo helper esegue document.cookie nel contesto JavaScript, restituendo un elenco di tutti i cookie.

Aggiungi quanto segue al corso _MenuState:

lib/src/menu.dart

Future<void> _onListCookies(WebViewController controller) async {
  final String cookies = await controller
      .runJavaScriptReturningResult('document.cookie') as String;
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text(cookies.isNotEmpty ? cookies : 'There are no cookies.'),
    ),
  );
}

Cancella tutti i cookie

Per cancellare tutti i cookie nella WebView, utilizza il metodo clearCookies della classe CookieManager. Il metodo restituisce un Future<bool> che si risolve in true se CookieManager ha cancellato i cookie e false se non c'erano cookie da cancellare.

Aggiungi quanto segue al corso _MenuState:

lib/src/menu.dart

Future<void> _onClearCookies() async {
  final hadCookies = await cookieManager.clearCookies();
  String message = 'There were cookies. Now, they are gone!';
  if (!hadCookies) {
    message = 'There were no cookies to clear.';
  }
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text(message),
    ),
  );
}

L'aggiunta di un cookie può essere eseguita richiamando JavaScript. L'API utilizzata per aggiungere un cookie a un documento JavaScript è documentata in dettaglio su MDN.

Aggiungi quanto segue al corso _MenuState:

lib/src/menu.dart

Future<void> _onAddCookie(WebViewController controller) async {
  await controller.runJavaScript('''var date = new Date();
  date.setTime(date.getTime()+(30*24*60*60*1000));
  document.cookie = "FirstName=John; expires=" + date.toGMTString();''');
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Custom cookie added.'),
    ),
  );
}

I cookie possono essere impostati anche utilizzando CookieManager come segue.

Aggiungi quanto segue al corso _MenuState:

lib/src/menu.dart

Future<void> _onSetCookie(WebViewController controller) async {
  await cookieManager.setCookie(
    const WebViewCookie(name: 'foo', value: 'bar', domain: 'flutter.dev'),
  );
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Custom cookie is set.'),
    ),
  );
}

La rimozione di un cookie comporta l'aggiunta di un cookie con una data di scadenza impostata nel passato.

Aggiungi quanto segue al corso _MenuState:

lib/src/menu.dart

Future<void> _onRemoveCookie(WebViewController controller) async {
  await controller.runJavaScript(
      'document.cookie="FirstName=John; expires=Thu, 01 Jan 1970 00:00:00 UTC" ');
  if (!mounted) return;
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Custom cookie removed.'),
    ),
  );
}

Aggiunta delle voci di menu di CookieManager

Non ti resta che aggiungere le opzioni di menu e collegarle ai metodi helper che hai appena aggiunto. Aggiorna la classe _MenuState come segue:

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

Utilizzo di CookieManager

Per utilizzare tutte le funzionalità che hai appena aggiunto all'app, prova a seguire questi passaggi:

  1. Seleziona Elenca cookie. Dovrebbe elencare i cookie di Google Analytics impostati da flutter.dev.
  2. Seleziona Cancella cookie. Dovrebbe segnalare che i cookie sono stati effettivamente cancellati.
  3. Seleziona di nuovo Cancella cookie. Dovrebbe segnalare che non erano disponibili cookie da cancellare.
  4. Seleziona Elenca cookie. Dovrebbe segnalare che non sono presenti cookie.
  5. Seleziona Aggiungi cookie. Dovrebbe segnalare che il cookie è stato aggiunto.
  6. Seleziona Imposta cookie. Dovrebbe segnalare che il cookie è stato impostato.
  7. Seleziona Elenca cookie e, come tocco finale, seleziona Rimuovi cookie.

Emulatore Android che esegue un&#39;app Flutter con una webview incorporata che mostra la home page di Flutter.dev con un elenco di opzioni di menu che riguardano la navigazione su YouTube, la visualizzazione dello user agent e l&#39;interazione con il cookie jar del browser

Emulatore Android che esegue un&#39;app Flutter con una webview incorporata che mostra la home page di Flutter.dev con una finestra di dialogo toast che mostra i cookie impostati nel browser

Emulatore Android che esegue un&#39;app Flutter con una webview incorporata che mostra la home page di Flutter.dev con una finestra di dialogo toast con il messaggio &quot;There were cookies. Ora non ci sono più!&quot;

Emulatore Android che esegue un&#39;app Flutter con una webview incorporata che mostra la home page di Flutter.dev con una finestra di dialogo toast con il messaggio &quot;Custom cookie added&quot; (Cookie personalizzato aggiunto).

12. Caricare asset, file e stringhe HTML di Flutter in WebView

La tua app può caricare file HTML utilizzando metodi diversi e visualizzarli in WebView. In questo passaggio caricherai una risorsa Flutter specificata nel file pubspec.yaml, un file che si trova nel percorso specificato e una pagina utilizzando una stringa HTML.

Se vuoi caricare un file che si trova in un percorso specificato, devi aggiungere path_provider a pubspec.yaml. Questo è un plug-in Flutter per trovare le posizioni di uso comune sul file system.

Nella riga di comando, esegui questo comando:

$ flutter pub add path_provider

Per caricare l'asset, dobbiamo specificare il percorso dell'asset in pubspec.yaml. In pubspec.yaml aggiungi le seguenti righe:

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.

Per aggiungere gli asset al progetto:

  1. Crea una nuova directory con il nome assets nella cartella principale del progetto.
  2. Crea una nuova directory con il nome www nella cartella assets.
  3. Crea una nuova directory con il nome styles nella cartella www.
  4. Crea un nuovo file con il nome index.html nella cartella www.
  5. Crea un nuovo file con il nome style.css nella cartella styles.

Copia e incolla il seguente codice nel file 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>

Per style.css, utilizza le seguenti righe per impostare lo stile dell'intestazione HTML:

assets/www/styles/style.css

h1 {
  color: blue;
}

Ora che gli asset sono impostati e pronti per l'uso, puoi implementare i metodi necessari per caricare e visualizzare asset, file o stringhe HTML di Flutter.

Caricare l'asset Flutter

Per caricare l'asset appena creato, devi solo chiamare il metodo loadFlutterAsset utilizzando WebViewController e fornire come parametro il percorso dell'asset. Aggiungi il seguente metodo alla fine del codice:

lib/src/menu.dart

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

Carica file locale

Per caricare un file sul tuo dispositivo, puoi aggiungere un metodo che utilizzerà il metodo loadFile, di nuovo utilizzando WebViewController, che accetta un String contenente il percorso del file.

Devi prima creare un file contenente il codice HTML. Per farlo, aggiungi il codice HTML come stringa nella parte superiore del codice nel file menu.dart appena sotto le importazioni.

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.

Per creare un File e scrivere la stringa HTML nel file, aggiungerai due metodi. _onLoadLocalFileExample caricherà il file fornendo il percorso come stringa restituita dal metodo _prepareLocalFile(). Aggiungi i seguenti metodi al tuo codice:

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

Carica stringa HTML

Visualizzare una pagina fornendo una stringa HTML è piuttosto semplice. WebViewController ha un metodo che puoi utilizzare chiamato loadHtmlString, in cui puoi fornire la stringa HTML come argomento. Il WebView mostrerà quindi la pagina HTML fornita. Aggiungi il seguente metodo al tuo codice:

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.

Aggiungere le voci di menu

Ora che gli asset sono impostati e pronti per l'uso e i metodi con tutte le funzionalità sono stati creati, il menu può essere aggiornato. Aggiungi le seguenti voci all'enumerazione _MenuOptions:

lib/src/menu.dart

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

Ora che l'enumerazione è aggiornata, puoi aggiungere le opzioni di menu e collegarle ai metodi helper che hai appena aggiunto. Aggiorna la classe _MenuState come segue:

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

Test degli asset, del file e della stringa HTML

Per verificare se il codice che hai appena implementato funziona, puoi eseguirlo sul tuo dispositivo e fare clic su una delle voci di menu appena aggiunte. Nota come _onLoadFlutterAssetExample utilizza style.css che abbiamo aggiunto per cambiare l'intestazione del file HTML in blu.

Emulatore Android che esegue un&#39;app Flutter con una webview incorporata che mostra una pagina con l&#39;etichetta &quot;Pagina demo locale&quot; con il titolo in blu

Emulatore Android che esegue un&#39;app Flutter con una visualizzazione web incorporata che mostra una pagina con l&#39;etichetta &quot;Pagina demo locale&quot; con il titolo in nero

13. Operazione completata.

Complimenti!!! Hai completato il codelab. Puoi trovare il codice completato per questo codelab nel repository del codelab.

Per saperne di più, prova gli altri codelab di Flutter.