Aggiungere WebView all'app Flutter

1. Introduzione

Ultimo aggiornamento: 2021-10-19

Con il plug-in Flutter di WebView 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ò visualizzare i widget Flutter nella vista web. Quindi, ad esempio, è possibile visualizzare un menu a discesa sulla vista web.

Cosa creerai

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

  • Visualizza i contenuti web in un WebView
  • Mostra widget Flutter sopra WebView
  • Reagisci agli eventi di avanzamento del caricamento pagina
  • Controlla WebView tramite WebViewController
  • Blocca i siti web che utilizzano l'NavigationDelegate
  • Valutare le espressioni JavaScript
  • Gestisci i callback da JavaScript con JavascriptChannels
  • Impostare, rimuovere, aggiungere o mostrare cookie
  • Carica e visualizza HTML da asset, file o stringhe contenenti HTML

Uno screenshot di un simulatore per iPhone che esegue un'app Flutter con un componente WebView incorporato che mostra la home page di Flutter.dev

Uno screenshot di un emulatore Android che esegue un'app Flutter con un componente WebView incorporato che mostra la home page di Flutter.dev

Cosa imparerai a fare

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

  • Come configurare il plug-in webview_flutter
  • Come ascoltare gli eventi di avanzamento del caricamento pagina
  • Come controllare la navigazione nelle pagine
  • Come ordinare 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 o file o una stringa contenente HTML

Che cosa ti serve

2. Configura l'ambiente di sviluppo di Flutter

Per completare questo lab sono necessari due software: l'SDK Flutter e l'editor.

Puoi eseguire il codelab utilizzando uno 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

Guida introduttiva a Flutter

Esistono diversi modi per creare un nuovo progetto Flutter. Gli strumenti disponibili per questa attività sono sia Android Studio sia Visual Studio Code. Segui le procedure collegate per creare un progetto oppure esegui i comandi riportati di seguito in un pratico terminale a riga di comando.

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

Aggiunta del plug-in Flutter WebView come dipendenza

È facile aggiungere ulteriori funzionalità a un'app Flutter utilizzando i pacchetti Pub. In questo codelab, aggiungerai il plug-in webview_flutter al tuo progetto. Esegui i seguenti comandi nel terminale.

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

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

Configura Android minSDK

Per usare 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. I componenti WebView sono visualizzazioni native ospitate, e gli sviluppatori di app possono scegliere come ospitarle nella propria app. Su Android puoi scegliere tra Display virtuali, attualmente 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 sull'hosting delle viste Android e iOS native nell'app Flutter con le viste piattaforma.

Visualizzare un componente 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 esegui questa operazione su iOS o Android, sul dispositivo verrà visualizzata una WebView come finestra del browser a schermo intero, il che significa che il browser viene visualizzato sul tuo dispositivo a schermo intero senza alcun tipo di bordo o margine. Mentre scorri, noterai che alcune parti della pagina potrebbero avere un aspetto un po' strano. Il motivo è che JavaScript è attualmente disattivato e il rendering di flutter.dev richiede correttamente JavaScript.

Esecuzione dell'app

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

$ flutter run

Supponendo che sia in esecuzione l'emulatore o il simulatore appropriato oppure sia collegato un dispositivo fisico, dopo aver compilato e implementato l'app sul tuo dispositivo, il risultato dovrebbe essere simile al seguente:

Uno screenshot di un simulatore per iPhone che esegue un&#39;app Flutter con un componente WebView incorporato che mostra la home page di Flutter.dev

Uno screenshot di un emulatore Android che esegue un&#39;app Flutter con un componente WebView incorporato che mostra la home page di Flutter.dev

5. In ascolto degli eventi di caricamento pagina

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

Aggiungere eventi di caricamento pagina all'app

Crea un nuovo file di origine all'indirizzo lib/src/web_view_stack.dart e compilalo con 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 aggregato il widget WebView all'interno di un elemento Stack, sovrapponendo in modo condizionale WebView a LinearProgressIndicator quando la percentuale di caricamento della pagina è inferiore al 100%. Poiché lo stato del programma cambia nel tempo, hai archiviato questo stato in una classe State associata a StatefulWidget.

Per utilizzare questo nuovo widget WebViewStack, modifica il tuo file lib/main.ARROW 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 della rete e se il browser ha memorizzato nella cache la pagina visualizzata, vedrai un indicatore di caricamento della pagina sovrapposto all'area dei contenuti di 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 creazione del widget WebView tramite un callback. La natura asincrona della disponibilità di questo controller lo rende un candidato ideale per la classe Completer<T> asincrona di Dart.

Aggiorna lib/src/web_view_stack.dart nel seguente modo:

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 verrà facilmente condiviso con altre parti dell'app.

Controlli di navigazione nella creazione di lavori

Avere un WebView funzionante è una cosa, ma essere in grado di navigare avanti e indietro nella cronologia delle pagine e di ricaricarla sarebbe un utile insieme di aggiunte. Per fortuna, con un WebViewController puoi aggiungere questa funzionalità alla tua app.

Crea un nuovo file di origine all'indirizzo lib/src/navigation_controls.dart e compilalo con 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 usa il WebViewController condiviso in fase di creazione per consentire all'utente di controllare WebView tramite una serie di IconButton.

Aggiunta dei controlli di navigazione ad AppBar

Con la nuova versione di WebViewStack e il nuovo NavigationControls in mano, è il momento di riassemblare tutto in un WebViewApp aggiornato. È qui che costruiamo l'elemento WebViewController condiviso. Con WebViewApp in cima alla struttura ad albero dei widget in questa app, ha senso 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 dei controlli:

Uno screenshot di un simulatore per iPhone che esegue un&#39;app Flutter con una WebView incorporata che mostra la home page di Flutter.dev con la pagina precedente, la pagina successiva e i controlli di ricarica della pagina

Uno screenshot di un emulatore Android che esegue un&#39;app Flutter con una WebView incorporata che mostra la home page di Flutter.dev con la pagina precedente, la pagina successiva e i controlli di ricarica della pagina

7. Tenere traccia della navigazione con NavigationDelega

WebView fornisce alla tua app un NavigationDelegate,, che consente all'app di monitorare e controllare la navigazione nelle pagine del widget WebView. Quando WebView, avvia una navigazione, ad esempio quando un utente fa clic su un link, viene chiamato NavigationDelegate. È possibile utilizzare il callback NavigationDelegate per controllare se WebView procede con la navigazione.

Registrare un NavigationDelegato personalizzato

In questo passaggio registrerai un callback NavigationDelegate per bloccare la navigazione su YouTube.com. Tieni presente che questa implementazione semplicistica blocca anche i contenuti in linea di YouTube, che sono disponibili 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 consentire di testare NavigationDelegate usando la classe WebViewController. Il lettore ha il compito di potenziare la logica del callback in modo da bloccare solo la navigazione dell'intera pagina su YouTube.com, pur consentendo l'incorporamento dei contenuti di YouTube nella documentazione dell'API.

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

Nei prossimi passaggi, creerai un pulsante di menu nel widget AppBar che verrà utilizzato per valutare JavaScript, richiamare canali JavaScript e gestire i cookie. Nel complesso, un menu davvero utile.

Crea un nuovo file di origine all'indirizzo lib/src/menu.dart e compilalo con 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. La navigazione verrà bloccata dal callback navigationDelegate che hai creato nel passaggio precedente.

Per aggiungere il menu allo schermo di WebViewApp, modifica lib/main.dart in questo modo:

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 ricevere uno SnackBar che ti informa che il controller di navigazione ha bloccato la navigazione su YouTube.

Uno screenshot di un emulatore Android che esegue un&#39;app Flutter con un componente WebView incorporato che mostra la home page di Flutter.dev con una voce di menu che mostra l&#39;opzione &quot;Vai a YouTube&quot;

Uno screenshot di un emulatore Android che esegue un&#39;app Flutter con una WebView incorporata che mostra la home page di Flutter.dev con un popup con la scritta &quot;Blocca la navigazione verso 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, utilizzare runJavaScript e per il codice JavaScript che restituisce un valore, utilizzare runJavaScriptReturningResult.

Per attivare JavaScript, devi configurare WebViewController con la proprietà javaScriptMode impostata su JavascriptMode.unrestricted. Per impostazione predefinita, l'opzione javascriptMode è impostata 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 l'WebViewWidget può eseguire JavaScript, puoi aggiungere al menu un'opzione per utilizzare il metodo runJavaScriptReturningResult.

Utilizzando l'Editor o alcune funzioni da tastiera, converti la classe Menu in uno 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 "Mostra user agent" , il risultato dell'esecuzione dell'espressione JavaScript navigator.userAgent viene visualizzato in Snackbar. Quando esegui l'app, potresti notare che la pagina di Flutter.dev ha un aspetto diverso. Questo è il risultato dell'esecuzione con JavaScript attivato.

Uno screenshot di un simulatore per iPhone che esegue un&#39;app Flutter con un componente WebView incorporato che mostra la home page di Flutter.dev con una voce di menu che mostra le opzioni &quot;Vai a YouTube&quot; o &quot;Mostra user agent&quot;

Uno screenshot di un simulatore per iPhone che esegue un&#39;app Flutter con un componente WebView incorporato che mostra la home page di Flutter.dev con un popup che mostra la stringa dello user agent.

10. Utilizzo dei canali JavaScript

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

Aggiorna il corso 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à finestra denominata con lo stesso nome del canale JavaScript name. L'utilizzo di questo codice dal contesto JavaScript comporta la chiamata a postMessage sul canale JavaScript per l'invio di un messaggio che viene passato al gestore di callback onMessageReceived denominato JavascriptChannel.

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

Ora che WebViewWidget conosce i nostri canali JavaScript,aggiungi un esempio per espandere ulteriormente l'app. Per farlo, aggiungi un altro PopupMenuItem alla classe Menu e 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 Public IP Address, restituendo l'indirizzo IP del dispositivo. Questo risultato viene mostrato in SnackBar richiamando postMessage in JavascriptChannel di SnackBar.

11. Gestione dei cookie

La tua app può gestire i cookie in WebView usando la classe CookieManager. In questo passaggio, visualizzerai un elenco di cookie, cancellerai l'elenco di cookie, eliminerai i cookie e imposterai nuovi cookie. Aggiungi voci a _MenuOptions per ciascuno dei casi d'uso dei cookie, come segue:

lib/src/menu.dart

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

Le altre modifiche apportate in questo passaggio sono incentrate sulla classe Menu, inclusa la conversione della classe Menu da stateless a stateful. Questa modifica è importante perché Menu deve essere proprietario del CookieManager e lo stato modificabile nei widget stateless è una combinazione errata.

Aggiungi il CookieManager alla classe State risultante come segue:

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

Il corso _MenuState conterrà il codice aggiunto in precedenza alla classe Menu, insieme al nuovo CookieManager appena aggiunto. Nella prossima serie di sezioni, aggiungerai a _MenuState funzioni helper che, a loro volta, verranno richiamate dalle voci di menu ancora da aggiungere.

Ottenere un elenco di tutti i cookie

Stai utilizzando JavaScript per ottenere un elenco di tutti i cookie. A questo scopo, aggiungi un metodo helper alla fine della lezione _MenuState, chiamato _onListCookies. Se utilizzi il metodo runJavaScriptReturningResult, il tuo metodo helper esegue document.cookie nel contesto JavaScript, restituendo un elenco di tutti i cookie.

Aggiungi quanto segue alla classe _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.'),
    ),
  );
}

Cancellare tutti i cookie

Per cancellare tutti i cookie in 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 erano presenti cookie da cancellare.

Aggiungi quanto segue alla classe _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 effettuata richiamando JavaScript. L'API utilizzata per aggiungere un cookie a un documento JavaScript è documentata in dettaglio su MDN.

Aggiungi quanto segue alla classe _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 con CookieManager come descritto di seguito.

Aggiungi quanto segue alla classe _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 alla classe _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.'),
    ),
  );
}

Aggiungere le voci di menu di CookieManager

Devi solo aggiungere le opzioni di menu e collegarle ai metodi di supporto appena aggiunti. Aggiorna il corso _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.
      ],
    );
  }

Allenamento di CookieManager

Per utilizzare tutte le funzionalità che hai appena aggiunto all'app, prova a procedere nel seguente modo:

  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. Deve indicare che non sono disponibili cookie da cancellare.
  4. Seleziona Elenca cookie. Deve segnalare che non sono presenti cookie.
  5. Seleziona Aggiungi cookie. Il cookie dovrebbe essere segnalato come aggiunto.
  6. Seleziona Imposta cookie. Il cookie dovrebbe essere segnalato come impostato.
  7. Seleziona Elenca cookie e, come ritocco finale, seleziona Rimuovi cookie.

Uno screenshot di un 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 comprendono la navigazione su YouTube, lo user agent e l&#39;interazione con il barattolo di cookie del browser

Uno screenshot di un emulatore Android che esegue un&#39;app Flutter con una WebView incorporata che mostra la home page di Flutter.dev con un popup che mostra i cookie impostati nel browser

Uno screenshot di un emulatore Android che esegue un&#39;app Flutter con un componente WebView incorporato che mostra la home page di Flutter.dev con un popup che mostra il messaggio &quot;C&#39;erano cookie. Ora se ne sono andati!&quot;

Uno screenshot di un emulatore Android che esegue un&#39;app Flutter con un componente WebView incorporato che mostra la home page di Flutter.dev con un popup che mostra il messaggio &quot;Cookie personalizzato aggiunto&quot;.

12. Carica asset Flutter, file e stringhe HTML in WebView

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

Se vuoi caricare un file che si trova in un percorso specificato, devi aggiungere path_provider al file pubspec.yaml. Si tratta di un plug-in Flutter per trovare le posizioni di uso comune nel file system.

Nella riga di comando, esegui questo comando:

$ flutter pub add path_provider

Per caricare l'asset dobbiamo specificare il relativo percorso nel 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>

Utilizza le seguenti righe per impostare lo stile dell'intestazione HTML nel file style.css:

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

Carica asset Flutter

Per caricare l'asset appena creato, non devi fare altro che richiamare il metodo loadFlutterAsset utilizzando WebViewController e assegnare 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 dispositivo puoi aggiungere un metodo che utilizzerà il metodo loadFile, sempre utilizzando WebViewController che richiede un String contenente il percorso del file.

Devi prima creare un file contenente il codice HTML. A tale scopo, aggiungi il codice HTML come stringa nella parte superiore del codice nel file menu.dart immediatamente 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, che viene 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 offre un metodo utilizzabile denominato loadHtmlString in cui è possibile assegnare la stringa HTML come argomento. WebView mostrerà la pagina HTML fornita. Aggiungi il seguente metodo al 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 del menu

Ora che gli asset sono impostati e pronti per l'uso e i metodi con tutte le funzionalità sono pronti, puoi aggiornare il menu. Aggiungi le seguenti voci all'enum _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'enum è stata aggiornata, puoi aggiungere le opzioni di menu e collegarle ai metodi helper appena aggiunti. Aggiorna il corso _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 di asset, file e stringa HTML

Per verificare se il codice funzionava dopo l'implementazione, esegui il codice sul tuo dispositivo e fai clic su una delle voci di menu appena aggiunte. Nota come _onLoadFlutterAssetExample utilizza il style.css che abbiamo aggiunto per modificare l'intestazione del file HTML nel colore blu.

Uno screenshot di un emulatore Android che esegue un&#39;app Flutter con un componente WebView incorporato che mostra una pagina con l&#39;etichetta &quot;Pagina demo locale&quot; con il titolo in blu

Uno screenshot di un emulatore Android che esegue un&#39;app Flutter con un componente WebView incorporato 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 codelab.

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