Ajouter WebView à votre application Flutter

1. Présentation

Dernière mise à jour : 19/10/2021

Le plug-in WebView de Flutter permet d'ajouter un widget WebView à votre application Flutter pour Android ou iOS. Sur iOS, le widget WebView repose sur WKWebView, tandis que sur Android, le widget WebView s'appuie sur une instance WebView. Le plug-in peut afficher des widgets Flutter par-dessus la vue Web. Par exemple, il est possible d'afficher un menu déroulant par-dessus la vue Web.

Ce que vous allez faire

Dans cet atelier de programmation par étapes, vous allez créer une application mobile intégrant une instance WebView avec le SDK Flutter. Cette appli pourra :

  • Afficher le contenu Web dans WebView
  • Afficher les widgets Flutter empilés sur WebView
  • Réagir aux événements de progression du chargement des pages
  • Contrôler WebView via WebViewController
  • Bloquer les sites Web à l'aide de NavigationDelegate
  • Évaluer des expressions JavaScript
  • Gérer les rappels à partir de JavaScript avec JavascriptChannels
  • Définir, supprimer, ajouter ou afficher des cookies
  • Charger et afficher le code HTML à partir d'éléments, de fichiers ou de chaînes contenant du code HTML

Points abordés

Dans cet atelier de programmation, vous allez apprendre à utiliser le plug-in webview_flutter de différentes manières, y compris les suivantes :

  • Configuration du plug-in webview_flutter
  • Détection des événements de progression du chargement des pages
  • Contrôle de la navigation sur les pages
  • Contrôle de WebView pour revenir en arrière et avancer dans l'historique
  • Évaluation JavaScript, y compris à l'aide des résultats renvoyés
  • Enregistrement des rappels pour appeler le code Dart à partir de JavaScript
  • Gestion des cookies
  • Chargement et affichage des pages HTML à partir d'éléments, de fichiers ou de chaînes contenant du code HTML

Prérequis

2. Configurer votre environnement Flutter

Pour cet atelier, vous avez besoin de deux logiciels : le SDK Flutter et un éditeur.

Vous pouvez exécuter cet atelier de programmation sur l'un des appareils suivants :

  • Un appareil mobile physique (Android ou iOS) connecté à votre ordinateur et réglé en mode développeur
  • Le simulateur iOS (macOS uniquement et outils Xcode à installer).
  • L'émulateur Android (qui doit être configuré dans Android Studio)

3. Premiers pas

Premiers pas avec Flutter

Vous pouvez créer un projet Flutter de plusieurs façons, en utilisant les outils fournis par Android Studio et Visual Studio Code pour cette tâche. Suivez les procédures associées pour créer un projet, ou exécutez les commandes suivantes dans un terminal de ligne de commande pratique.

$ flutter create webview_in_flutter
Creating project webview_in_flutter...
[Listing of created files elided]
Wrote 81 files.

All done!
In order to run your application, type:

  $ cd webview_in_flutter
  $ flutter run

Your application code is in webview_in_flutter\lib\main.dart.

Ajouter le plug-in WebView de Flutter en tant que dépendance

Il est facile d'ajouter des fonctionnalités supplémentaires à une application Flutter à l'aide de packages Pub. Dans cet atelier de programmation, vous allez ajouter le plug-in webview_flutter à votre projet. Exécutez les commandes suivantes dans le terminal.

$ cd webview_in_flutter
$ flutter pub add webview_flutter
Resolving dependencies...
  async 2.8.1 (2.8.2 available)
  characters 1.1.0 (1.2.0 available)
  matcher 0.12.10 (0.12.11 available)
+ plugin_platform_interface 2.0.2
  test_api 0.4.2 (0.4.8 available)
  vector_math 2.1.0 (2.1.1 available)
+ webview_flutter 3.0.0
+ webview_flutter_android 2.8.0
+ webview_flutter_platform_interface 1.8.0
+ webview_flutter_wkwebview 2.7.0
Downloading webview_flutter 3.0.0...
Downloading webview_flutter_wkwebview 2.7.0...
Downloading webview_flutter_android 2.8.0...
Changed 5 dependencies!

Si vous inspectez le fichier pubspec.yaml, vous constaterez qu'il comporte désormais une ligne dans la section des dépendances du plug-in webview_flutter.

Configurer la version minimale (minSDK) d'Android

Pour utiliser le plug-in webview_flutter sur Android, vous devez définir minSDK sur 19 ou 20, en fonction de la vue de la plate-forme Android que vous souhaitez utiliser. Pour en savoir plus sur la vue de la plate-forme Android, consultez la page du plug-in webview_flutter. Modifiez votre fichier android/app/build.gradle comme suit :

android/app/build.gradle

defaultConfig {
    // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
    applicationId "com.example.webview_in_flutter"
    minSdkVersion 20        // MODIFY
    targetSdkVersion 30
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}

4. Ajouter le widget WebView à l'application Flutter

À cette étape, vous allez ajouter WebView à votre application. Les WebViews sont des vues natives hébergées, et en tant que développeur d'applications, vous pouvez choisir comment les héberger dans votre application. Sur Android, vous pouvez opter pour des affichages virtuels, actuellement définis par défaut pour Android et pour le mode mixte. Toutefois, iOS utilise toujours le mode mixte.

Pour une présentation détaillée des différences entre les écrans virtuels et le mode mixte, consultez la documentation sur l'hébergement des vues natives Android et iOS dans votre application Flutter avec les vues de la plate-forme.

Afficher une instance WebView à l'écran

Remplacez le contenu du bloc lib/main.dart par le code suivant :

lib/main.dart

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  runApp(
    const MaterialApp(
      home: WebViewApp(),
    ),
  );
}

class WebViewApp extends StatefulWidget {
  const WebViewApp({Key? key}) : super(key: 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 WebView(
        initialUrl: 'https://flutter.dev',
      ),
    );
  }
}

Exécuter cette commande sur iOS ou Android affiche une instance WebView dans une fenêtre de navigateur à fond perdu sur votre appareil. Le navigateur s'affiche donc en plein écran sur votre appareil, sans aucune forme de bordure ni de marge. En faisant défiler la page, vous remarquerez peut-être que certaines parties de celle-ci peuvent sembler un peu bizarres. En effet, JavaScript est actuellement désactivé, et l'affichage correct du fichier flutter.dev requiert JavaScript.

Activer le mode mixte

Si vous souhaitez utiliser le mode mixte pour les appareils Android, vous devez procéder à quelques modifications mineures. Modifiez votre lib/main.dart comme suit :

lib/main.dart

import 'dart:io';                            // Add this import.
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  runApp(
    const MaterialApp(
      home: WebViewApp(),
    ),
  );
}

class WebViewApp extends StatefulWidget {
  const WebViewApp({Key? key}) : super(key: key);

  @override
  State<WebViewApp> createState() => _WebViewAppState();
}

class _WebViewAppState extends State<WebViewApp> {
  // Add from here ...
  @override
  void initState() {
    if (Platform.isAndroid) {
      WebView.platform = SurfaceAndroidWebView();
    }
    super.initState();
  }
  // ... to here.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView'),
      ),
      body: const WebView(
        initialUrl: 'https://flutter.dev',
      ),
    );
  }
}

N'oubliez pas de remplacer minSdkVersion dans build.gradle par 19 lorsque vous souhaitez utiliser la vue de la plate-forme en mode mixte.

Exécuter l'application

Exécutez l'application Flutter sur iOS ou Android pour afficher une vue de la plate-forme qui comporte le site Web flutter.dev. Vous pouvez également exécuter l'application dans un émulateur Android ou un simulateur iOS. N'hésitez pas à remplacer l'URL WebView initiale par votre site Web, par exemple.

$ flutter run

Si vous avez installé le simulateur ou l'émulateur approprié ou si vous avez connecté un appareil physique, vous devriez obtenir ce qui suit après avoir compilé et déployé l'application :

5. Détecter les événements de chargement de pages

Grâce au widget WebView, votre application peut détecter plusieurs événements de progression du chargement de la page. Au cours du cycle de chargement de la page WebView, trois événements de chargement de page différents se déclenchent : onPageStarted, onProgress et onPageFinished. À cette étape, vous allez implémenter un indicateur de chargement de page. En bonus, vous pouvez afficher le contenu Flutter dans la zone de contenu WebView.

Ajouter des événements de chargement de pages à votre application

Créez un fichier source à l'adresse lib/src/web_view_stack.dart et renseignez-le comme suit :

lib/src/web_view_stack.dart

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class WebViewStack extends StatefulWidget {
  const WebViewStack({Key? key}) : super(key: key);

  @override
  State<WebViewStack> createState() => _WebViewStackState();
}

class _WebViewStackState extends State<WebViewStack> {
  var loadingPercentage = 0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebView(
          initialUrl: 'https://flutter.dev',
          onPageStarted: (url) {
            setState(() {
              loadingPercentage = 0;
            });
          },
          onProgress: (progress) {
            setState(() {
              loadingPercentage = progress;
            });
          },
          onPageFinished: (url) {
            setState(() {
              loadingPercentage = 100;
            });
          },
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}

Ce code a encapsulé le widget WebView dans Stack, superposant de manière conditionnelle WebView avec LinearProgressIndicator lorsque le pourcentage de chargement de la page est de moins de 100 %. Puisque cet état de programme change au fil du temps, vous l'avez stocké dans une classe State associée à StatefulWidget.

Pour utiliser ce nouveau widget WebViewStack, modifiez le fichier lib/main.dart comme suit :

import 'package:flutter/material.dart';
// Delete the package:webview_flutter/webview_flutter.dart import
import 'src/web_view_stack.dart';  // Add this import

void main() {
  runApp(
    const MaterialApp(
      home: WebViewApp(),
    ),
  );
}

class WebViewApp extends StatefulWidget {
  const WebViewApp({Key? key}) : super(key: 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(),   // Replace the WebView widget with WebViewStack
    );
  }
}

Lorsque vous exécutez l'application, suivant l'état de votre réseau et si le navigateur a mis en cache la page que vous consultez, un indicateur de chargement de page s'affiche en superposition sur la zone de contenu WebView.

6. Utiliser WebViewController

Accéder à WebViewController à partir du widget WebView

Le widget WebView permet un contrôle programmatique à l'aide de WebViewController. Ce contrôleur est mis à disposition après la construction du widget WebView via un rappel. Le caractère asynchrone de ce contrôleur en fait un candidat idéal pour la classe asynchrone Completer<T> de Dart.

Mettez à jour lib/src/web_view_stack.dart comme indiqué ci-dessous :

lib/src/web_view_stack.dart

import 'dart:async';     // Add this import for Completer
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class WebViewStack extends StatefulWidget {
  const WebViewStack({required this.controller, Key? key}) : super(key: key); // Modify

  final Completer<WebViewController> controller;   // Add this attribute

  @override
  State<WebViewStack> createState() => _WebViewStackState();
}

class _WebViewStackState extends State<WebViewStack> {
  var loadingPercentage = 0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebView(
          initialUrl: 'https://flutter.dev',
          // Add from here ...
          onWebViewCreated: (webViewController) {
            widget.controller.complete(webViewController);
          },
          // ... to here.
          onPageStarted: (url) {
            setState(() {
              loadingPercentage = 0;
            });
          },
          onProgress: (progress) {
            setState(() {
              loadingPercentage = progress;
            });
          },
          onPageFinished: (url) {
            setState(() {
              loadingPercentage = 100;
            });
          },
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}

Le widget WebViewStack publie désormais le contrôleur créé de manière asynchrone à l'aide de Completer<WebViewController>. Il s'agit d'une alternative plus légère à la création d'un argument de fonction de rappel pour fournir le contrôleur au reste de l'application.

Créer des commandes de navigation

Disposer d'un WebView fonctionnel est une chose, mais il pourrait également être utile de pouvoir parcourir les pages ou l'historique, de revenir en arrière et d'actualiser la page. Heureusement, avec WebViewController, vous pouvez ajouter cette fonctionnalité à votre application.

Créez un fichier source à l'adresse lib/src/navigation_controls.dart et renseignez-le comme suit :

lib/src/navigation_controls.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class NavigationControls extends StatelessWidget {
  const NavigationControls({required this.controller, Key? key})
      : super(key: key);

  final Completer<WebViewController> controller;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: controller.future,
      builder: (context, snapshot) {
        final WebViewController? controller = snapshot.data;
        if (snapshot.connectionState != ConnectionState.done ||
            controller == null) {
          return Row(
            children: const <Widget>[
              Icon(Icons.arrow_back_ios),
              Icon(Icons.arrow_forward_ios),
              Icon(Icons.replay),
            ],
          );
        }

        return Row(
          children: <Widget>[
            IconButton(
              icon: const Icon(Icons.arrow_back_ios),
              onPressed: () async {
                if (await controller.canGoBack()) {
                  await controller.goBack();
                } else {
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('No back history item')),
                  );
                  return;
                }
              },
            ),
            IconButton(
              icon: const Icon(Icons.arrow_forward_ios),
              onPressed: () async {
                if (await controller.canGoForward()) {
                  await controller.goForward();
                } else {
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('No forward history item')),
                  );
                  return;
                }
              },
            ),
            IconButton(
              icon: const Icon(Icons.replay),
              onPressed: () {
                controller.reload();
              },
            ),
          ],
        );
      },
    );
  }
}

Ce widget utilise un widget FutureBuilder<T> qui se repeint en conséquence dès que le contrôleur est disponible. En attendant que le contrôleur soit disponible, une rangée de trois icônes s'affiche. Cependant, une fois le contrôleur affiché, il est remplacé par Row sur IconButton avec des gestionnaires onPressed qui utilisent controller pour mettre en œuvre leurs fonctionnalités.

Ajouter des commandes de navigation à AppBar

Il est temps d'assembler WebViewStack qui vient d'être mis à jour et NavigationControls qui vient d'être créé dans une nouvelle version de WebViewApp. Vous avez appris à utiliser Completer<T> précédemment, mais pas à l'endroit où vous l'avez créé. Avec WebViewApp dans la partie supérieure de l'arborescence des widgets de cette application, il est judicieux d'effectuer la création à ce niveau.

Mettez à jour le fichier lib/main.dart comme suit :

lib/main.dart

import 'dart:async';                                    // Add this import

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';  // Add this import back

import 'src/navigation_controls.dart';                  // Add this import
import 'src/web_view_stack.dart';

void main() {
  runApp(
    const MaterialApp(
      home: WebViewApp(),
    ),
  );
}

class WebViewApp extends StatefulWidget {
  const WebViewApp({Key? key}) : super(key: key);

  @override
  State<WebViewApp> createState() => _WebViewAppState();
}

class _WebViewAppState extends State<WebViewApp> {
  final controller = Completer<WebViewController>();    // Instantiate the controller

  @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),       // Add the controller argument
    );
  }
}

En exécutant l'application, vous devriez voir une page Web contenant les commandes :

7. Suivre la navigation avec la fonctionnalité NavigationDelegate

WebView fournit NavigationDelegate, à votre application pour lui permettre de suivre et de contrôler la navigation sur les pages du widget WebView. Quand une navigation est initiée par WebView,, par exemple lorsqu'un utilisateur clique sur un lien, NavigationDelegate est appelé. Le rappel NavigationDelegate peut être utilisé pour contrôler si WebView poursuit la navigation.

Enregistrer une version personnalisée de NavigationDelegate

À cette étape, vous allez enregistrer un rappel NavigationDelegate afin de bloquer la navigation vers YouTube.com. Notez que cette implémentation simplifiée bloque également le contenu YouTube intégré, qui apparaît dans différentes pages de documentation de l'API Flutter.

Mettez à jour lib/src/web_view_stack.dart comme suit :

lib/src/web_view_stack.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class WebViewStack extends StatefulWidget {
  const WebViewStack({required this.controller, Key? key}) : super(key: key);

  final Completer<WebViewController> controller;

  @override
  State<WebViewStack> createState() => _WebViewStackState();
}

class _WebViewStackState extends State<WebViewStack> {
  var loadingPercentage = 0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebView(
          initialUrl: 'https://flutter.dev',
          onWebViewCreated: (webViewController) {
            widget.controller.complete(webViewController);
          },
          onPageStarted: (url) {
            setState(() {
              loadingPercentage = 0;
            });
          },
          onProgress: (progress) {
            setState(() {
              loadingPercentage = progress;
            });
          },
          onPageFinished: (url) {
            setState(() {
              loadingPercentage = 100;
            });
          },
          // Add from here ...
          navigationDelegate: (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.
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}

À l'étape suivante, vous allez ajouter un élément de menu pour activer le test de votre NavigationDelegate à l'aide de la classe WebViewController. Il revient au lecteur de développer la logique du rappel afin de ne bloquer que la navigation en pleine page sur YouTube.com et de continuer à autoriser le contenu YouTube intégré dans la documentation de l'API.

8. Ajouter un bouton de menu à AppBar

Lors des prochaines étapes, vous allez créer un bouton de menu dans le widget AppBar, qui permet d'évaluer JavaScript, d'appeler des canaux JavaScript et de gérer les cookies. Dans l'ensemble, un menu bien utile.

Créez un fichier source à l'adresse lib/src/menu.dart et renseignez-le comme suit :

lib/src/menu.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

enum _MenuOptions {
  navigationDelegate,
}

class Menu extends StatelessWidget {
  const Menu({required this.controller, Key? key}) : super(key: key);

  final Completer<WebViewController> controller;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: controller.future,
      builder: (context, controller) {
        return PopupMenuButton<_MenuOptions>(
          onSelected: (value) async {
            switch (value) {
              case _MenuOptions.navigationDelegate:
                controller.data!.loadUrl('https://youtube.com');
                break;
            }
          },
          itemBuilder: (context) => [
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.navigationDelegate,
              child: Text('Navigate to YouTube'),
            ),
          ],
        );
      },
    );
  }
}

Quand l'utilisateur sélectionne l'option de menu Accéder à YouTube, la méthode loadUrl de WebViewController s'exécute. Cette navigation va être bloquée par le rappel navigationDelegate que vous avez créé à l'étape précédente.

Pour ajouter le menu à l'écran de WebViewApp, modifiez lib/main.dart comme suit :

lib/main.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

import 'src/menu.dart';                                // Add this import
import 'src/navigation_controls.dart';
import 'src/web_view_stack.dart';

void main() {
  runApp(
    const MaterialApp(
      home: WebViewApp(),
    ),
  );
}

class WebViewApp extends StatefulWidget {
  const WebViewApp({Key? key}) : super(key: key);

  @override
  State<WebViewApp> createState() => _WebViewAppState();
}

class _WebViewAppState extends State<WebViewApp> {
  final controller = Completer<WebViewController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView'),
        actions: [
          NavigationControls(controller: controller),
          Menu(controller: controller),                // Add this line
        ],
      ),
      body: WebViewStack(controller: controller),
    );
  }
}

Exécutez votre application et appuyez sur l'élément de menu Accéder à YouTube. Vous devriez recevoir un message SnackBar vous informant que la manette de navigation a bloqué la navigation sur YouTube.

9. Évaluer JavaScript

WebViewController peut évaluer des expressions JavaScript dans le contexte de la page actuelle. Pour évaluer JavaScript, vous avez le choix entre deux méthodes : pour le code JavaScript qui ne renvoie pas de valeur, utilisez runJavaScript et pour le code JavaScript renvoyant une valeur, utilisez runJavaScriptReturningResult.

Pour activer JavaScript, vous devez configurer le widget WebView avec la propriété javaScriptMode définie sur JavascriptMode.unrestricted. Par défaut, javascriptMode est défini sur JavascriptMode.disabled.

Mettez à jour la classe _WebViewStackState en ajoutant le paramètre javascriptMode comme suit :

lib/src/web_view_stack.dart

class _WebViewStackState extends State<WebViewStack> {
  var loadingPercentage = 0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebView(
          initialUrl: 'https://flutter.dev',
          onWebViewCreated: (webViewController) {
            widget.controller.complete(webViewController);
          },
          onPageStarted: (url) {
            setState(() {
              loadingPercentage = 0;
            });
          },
          onProgress: (progress) {
            setState(() {
              loadingPercentage = progress;
            });
          },
          onPageFinished: (url) {
            setState(() {
              loadingPercentage = 100;
            });
          },
          navigationDelegate: (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;
          },
          javascriptMode: JavascriptMode.unrestricted,        // Add this line
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }
}

Maintenant que WebView peut exécuter JavaScript, vous pouvez ajouter une option au menu pour utiliser la méthode runJavaScriptReturningResult.

Modifiez lib/src/menu.dart comme suit :

lib/src/menu.dart

enum _MenuOptions {
  navigationDelegate,
  userAgent,                                          // Add this line
}

class Menu extends StatelessWidget {
  const Menu({required this.controller, Key? key}) : super(key: key);

  final Completer<WebViewController> controller;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: controller.future,
      builder: (context, controller) {
        return PopupMenuButton<_MenuOptions>(
          onSelected: (value) async {
            switch (value) {
              case _MenuOptions.navigationDelegate:
                controller.data!.loadUrl('https://youtube.com');
                break;
              // Add from here ...
              case _MenuOptions.userAgent:
                final userAgent = await controller.data!
                    .runJavascriptReturningResult('navigator.userAgent');
                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                  content: Text(userAgent),
                ));
                break;
              // ... to here.
            }
          },
          itemBuilder: (context) => [
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.navigationDelegate,
              child: Text('Navigate to YouTube'),
            ),
            // Add from here ...
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.userAgent,
              child: Text('Show user-agent'),
            ),
            // ... to here.
          ],
        );
      },
    );
  }
}

Lorsque vous appuyez sur l'option de menu "Afficher le user-agent", le résultat de l'exécution de l'expression JavaScript navigator.userAgent est affiché dans Snackbar. Lors de l'exécution de l'application, vous remarquerez peut-être que la page Flutter.dev s'affiche différemment. C'est le résultat de l'exécution avec JavaScript activé.

10. Utiliser les canaux JavaScript

Les JavascriptChannel permettent à votre application d'enregistrer des gestionnaires de rappel dans le contexte JavaScript de WebView, qui peut être appelé pour transmettre les valeurs au code Dart de l'application. À cette étape, vous allez enregistrer une chaîne SnackBar qui sera appelée avec le résultat de XMLHttpRequest.

Mettez à jour la classe WebViewStack comme suit :

lib/src/web_view_stack.dart

class WebViewStack extends StatefulWidget {
  const WebViewStack({required this.controller, Key? key}) : super(key: key);

  final Completer<WebViewController> controller;

  @override
  State<WebViewStack> createState() => _WebViewStackState();
}

class _WebViewStackState extends State<WebViewStack> {
  var loadingPercentage = 0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebView(
          initialUrl: 'https://flutter.dev',
          onWebViewCreated: (webViewController) {
            widget.controller.complete(webViewController);
          },
          onPageStarted: (url) {
            setState(() {
              loadingPercentage = 0;
            });
          },
          onProgress: (progress) {
            setState(() {
              loadingPercentage = progress;
            });
          },
          onPageFinished: (url) {
            setState(() {
              loadingPercentage = 100;
            });
          },
          navigationDelegate: (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;
          },
          javascriptMode: JavascriptMode.unrestricted,
          javascriptChannels: _createJavascriptChannels(context),  // Add this line
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }

  // Add from here ...
  Set<JavascriptChannel> _createJavascriptChannels(BuildContext context) {
    return {
      JavascriptChannel(
        name: 'SnackBar',
        onMessageReceived: (message) {
          ScaffoldMessenger.of(context)
              .showSnackBar(SnackBar(content: Text(message.message)));
        },
      ),
    };
  }
  // ... to here.
}

Pour chaque JavascriptChannel dans Set, un objet de canal est disponible dans le contexte JavaScript en tant que propriété de fenêtre portant le même nom que JavascriptChannel.name. L'utilisation de ce code depuis le contexte JavaScript implique d'appeler postMessage sur le code JavascriptChannel pour envoyer un message transmis au gestionnaire de rappel onMessageReceived nommé JavascriptChannel.

Pour utiliser JavascriptChannel ajouté ci-dessus, ajoutez un autre élément de menu qui exécute XMLHttpRequest dans le contexte JavaScript et renvoie les résultats à l'aide du JavascriptChannel de SnackBar.

Maintenant que WebView connaît notre JavascriptChannels,, vous pouvez ajouter un exemple pour étendre l'application. Pour cela, ajoutez une autre instance de PopupMenuItem à la classe Menu, puis ajoutez les fonctionnalités supplémentaires.

Mettez à jour _MenuOptions avec l'option de menu supplémentaire, en ajoutant la valeur d'énumération javascriptChannel et en ajoutant une implémentation à la classe Menu comme suit :

lib/src/menu.dart

enum _MenuOptions {
  navigationDelegate,
  userAgent,
  javascriptChannel,                                    // Add this line
}

class Menu extends StatelessWidget {
  const Menu({required this.controller, Key? key}) : super(key: key);

  final Completer<WebViewController> controller;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: controller.future,
      builder: (context, controller) {
        return PopupMenuButton<_MenuOptions>(
          onSelected: (value) async {
            switch (value) {
              case _MenuOptions.navigationDelegate:
                controller.data!.loadUrl('https://youtube.com');
                break;
              case _MenuOptions.userAgent:
                final userAgent = await controller.data!
                    .runJavascriptReturningResult('navigator.userAgent');
                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                  content: Text(userAgent),
                ));
                break;
              // Add from here ...
              case _MenuOptions.javascriptChannel:
                await controller.data!.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();''');
                break;
              // ... 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'),
            ),
            // Add from here ...
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.javascriptChannel,
              child: Text('Lookup IP Address'),
            ),
            // ... to here.
          ],
        );
      },
    );
  }
}

Ce code JavaScript s'exécute lorsque l'utilisateur choisit l'option de menu Exemple de canal 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();

Ce code envoie une requête GET à une API d'adresse IP publique, en renvoyant l'adresse IP de l'appareil. Ce résultat s'affiche dans SnackBar en appelant postMessage sur le SnackBar JavascriptChannel.

11. Gérer les cookies

Votre application peut gérer les cookies dans WebView avec la classe CookieManager. À cette étape, vous allez afficher une liste de cookies, l'effacer, supprimer les cookies et en définir de nouveaux. Ajoutez des entrées à _MenuOptions pour chacun des cas d'utilisation de cookies comme suit :

lib/src/menu.dart

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

Les autres modifications de cette étape sont axées sur la classe Menu, y compris la conversion de la classe Menu de "sans état" à "avec état". Cette modification est importante, car Menu doit posséder CookieManager, et l'état modifiable dans les widgets sans état est une mauvaise combinaison.

À l'aide de votre éditeur ou d'un raccourci clavier convertissez la classe Menu en StatefulWidget et ajoutez CookieManager à la classe State suivante ainsi obtenue :

lib/src/menu.dart

class Menu extends StatefulWidget {                           // Convert to StatefulWidget
  const Menu({required this.controller, Key? key}) : super(key: key);

  final Completer<WebViewController> controller;

  @override
  State<Menu> createState() => _MenuState();                  // Add this line
}

class _MenuState extends State<Menu> {                       // New State class
  final CookieManager cookieManager = CookieManager();       // Add this line

  @override
  Widget build(BuildContext context) {
  // ...

La classe _MenuState contient le code précédemment ajouté dans la classe Menu, ainsi que la classe CookieManager que vous venez d'ajouter. Dans les sections suivantes, vous ajouterez des fonctions d'assistance à _MenuState, qui seront ensuite appelées par les éléments de menu à ajouter.

Obtenir la liste de tous les cookies

Vous allez utiliser JavaScript pour obtenir la liste de tous les cookies. Pour ce faire, ajoutez une méthode d'assistance à la fin de la classe _MenuState, appelée _onListCookies. À l'aide de la méthode runJavaScriptReturningResult, votre méthode d'assistance exécute document.cookie dans le contexte JavaScript et renvoie une liste de tous les cookies.

Ajoutez les éléments suivants à la classe _MenuState :

lib/src/menu.dart

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

Effacer tous les cookies

Pour effacer tous les cookies dans l'instance WebView, utilisez la méthode clearCookies de la classe CookieManager. La méthode renvoie Future<bool> qui renvoie vers true si CookieManager a effacé les cookies et false si aucun cookie n'a été supprimé.

Ajoutez les éléments suivants à la 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.';
  }
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text(message),
    ),
  );
}

Vous pouvez ajouter un cookie en appelant JavaScript. L'API qui permet d'ajouter un cookie à un document JavaScript est décrite en détail sur MDN.

Ajoutez les éléments suivants à la 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();''');
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Custom cookie added.'),
    ),
  );
}

Vous pouvez également définir les cookies à l'aide de CookieManager comme suit.

Ajoutez les éléments suivants à la classe _MenuState :

lib/src/menu.dart

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

Pour supprimer un cookie, vous devez ajouter un cookie avec une date d'expiration déjà passée.

Ajoutez les éléments suivants à la 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" ');
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('Custom cookie removed.'),
    ),
  );
}

Ajouter les éléments du menu CookieManager

Il vous suffit d'ajouter les options de menu et de les lier aux méthodes d'assistance que vous venez d'ajouter. Mettez à jour la classe _MenuState comme suit :

lib/src/menu.dart

class _MenuState extends State<Menu> {
  final CookieManager cookieManager = CookieManager();

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: widget.controller.future,
      builder: (context, controller) {
        return PopupMenuButton<_MenuOptions>(
          onSelected: (value) async {
            switch (value) {
              case _MenuOptions.navigationDelegate:
                controller.data!.loadUrl('https://youtube.com');
                break;
              case _MenuOptions.userAgent:
                final userAgent = await controller.data!
                    .runJavascriptReturningResult('navigator.userAgent');
                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                  content: Text(userAgent),
                ));
                break;
              case _MenuOptions.javascriptChannel:
                await controller.data!.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();''');
                break;
              // Add from here ...
              case _MenuOptions.clearCookies:
                _onClearCookies();
                break;
              case _MenuOptions.listCookies:
                _onListCookies(controller.data!);
                break;
              case _MenuOptions.addCookie:
                _onAddCookie(controller.data!);
                break;
              case _MenuOptions.setCookie:
                _onSetCookie(controller.data!);
                break;
              case _MenuOptions.removeCookie:
                _onRemoveCookie(controller.data!);
                break;
              // ... 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'),
            ),
            // Add from here ...
            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'),
            ),
            // ... to here.
          ],
        );
      },
    );
  }

Exécuter CookieManager

Pour utiliser toutes les fonctionnalités que vous venez d'ajouter à l'application, procédez comme suit :

  1. Sélectionnez Répertorier les cookies. Cette option doit répertorier les cookies Google Analytics définis par flutter.dev.
  2. Sélectionnez Effacer les cookies. Cette option doit indiquer que les cookies ont bien été supprimés.
  3. Sélectionnez à nouveau Effacer les cookies. Cette option doit indiquer qu'il n'y a aucun cookie à supprimer.
  4. Sélectionnez Répertorier les cookies. Cette option doit indiquer qu'il n'y a pas de cookie.
  5. Sélectionnez Ajouter un cookie. Cette option doit indiquer que le cookie a été ajouté.
  6. Sélectionnez Définir des cookies. Cette option doit indiquer que le cookie a été défini.
  7. Sélectionnez Répertorier les cookies, puis, en dernier lieu, sélectionnez Supprimer les cookies.

12. Charger des éléments, des fichiers et des chaînes HTML Flutter dans WebView

Votre application peut charger des fichiers HTML à l'aide de différentes méthodes et les afficher dans l'instance WebView. À cette étape, vous allez charger un élément Flutter spécifié dans le fichier pubspec.yaml, charger un fichier situé au chemin spécifié et charger une page à l'aide d'une chaîne HTML.

Si vous souhaitez charger un fichier situé à un chemin spécifié, vous devez ajouter path_provider à pubspec.yaml. Il s'agit d'un plug-in Flutter permettant de trouver des emplacements couramment utilisés dans le système de fichiers.

Dans le fichier pubspec.yaml, ajoutez la ligne suivante :

pubspec.yaml

dependencies:
 flutter:
   sdk: flutter

 # The following adds the Cupertino Icons font to your application.
 # Use with the CupertinoIcons class for iOS style icons.
 cupertino_icons: ^1.0.2
 webview_flutter: ^3.0.0
 path_provider: ^2.0.7   # Add this line

Pour charger l'élément, vous devez spécifier son chemin dans pubspec.yaml. Dans pubspec.yaml, ajoutez les lignes suivantes :

pubspec.yaml.

# The following section is specific to Flutter.
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.

Pour ajouter les éléments à votre projet procédez comme suit :

  1. Créez un répertoire nommé assets dans le dossier racine de votre projet.
  2. Créez un répertoire nommé www dans le dossier assets.
  3. Créez un répertoire nommé styles dans le dossier www.
  4. Créez un fichier nommé index.html dans le dossier www.
  5. Créez un fichier nommé style.css dans le dossier styles.

Copiez et collez le code suivant dans le fichier 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>

Pour définir le style d'en-tête HTML, utilisez le code suivant pour les styles.css :

assets/www/styles/style.css

h1 {
   color: blue;
}

Maintenant que les éléments sont définis et prêts à être utilisés, vous pouvez mettre en œuvre les méthodes nécessaires pour charger et afficher les éléments, les fichiers ou les chaînes HTML Flutter.

Charger l'élément Flutter

Pour charger l'élément que vous venez de créer, il vous suffit d'appeler la méthode loadFlutterAsset à l'aide de WebViewController et d'indiquer le chemin d'accès à l'élément en tant que paramètre. Ajoutez la méthode suivante à la fin de votre code :

lib/src/menu.dart

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

Charger un fichier local

Pour charger un fichier sur votre appareil, vous pouvez ajouter une méthode qui utilisera la méthode loadFile, à nouveau à l'aide de WebViewController qui redirige String contenant le chemin d'accès vers le fichier.

Vous devez d'abord créer un fichier contenant le code HTML. Pour ce faire, vous pouvez simplement ajouter le code HTML en tant que chaîne en haut de votre code dans le fichier menu.dart, juste en dessous des importations.

lib/src/menu.dart

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
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.

Pour créer File et écrire la chaîne HTML dans le fichier, vous allez ajouter deux méthodes. _onLoadLocalFileExample charge le fichier en fournissant le chemin d'accès sous forme de chaîne, qui est renvoyée par la méthode _prepareLocalFile(). Ajoutez les méthodes suivantes à votre code :

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;
}
// ... to here.

Charger une chaîne HTML

Il est plutôt simple d'afficher une page en fournissant une chaîne HTML. WebViewController utilise une méthode appelée loadHtmlString, qui vous permet d'utiliser la chaîne HTML comme argument. WebView affiche alors la page HTML fournie. Ajoutez la méthode suivante à votre code :

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.

Ajouter les éléments du menu

Maintenant que les éléments sont définis et prêts à être utilisés, et que les méthodes comprenant toutes les fonctionnalités ont été créées, le menu peut être mis à jour. Ajoutez les entrées suivantes à l'énumération _MenuOptions :

lib/src/menu.dart

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

Maintenant que l'énumération est mise à jour, vous pouvez ajouter les options de menu et les associer aux méthodes d'assistance que vous venez d'ajouter. Mettez à jour la classe _MenuState comme suit :

lib/src/menu.dart

class _MenuState extends State<Menu> {
 final CookieManager cookieManager = CookieManager();

 @override
 Widget build(BuildContext context) {
   return FutureBuilder<WebViewController>(
     future: widget.controller.future,
     builder: (context, controller) {
       return PopupMenuButton<_MenuOptions>(
         onSelected: (value) async {
           switch (value) {
             case _MenuOptions.navigationDelegate:
               controller.data!.loadUrl('https://youtube.com');
               break;
             case _MenuOptions.userAgent:
               final userAgent = await controller.data!
                   .runJavascriptReturningResult('navigator.userAgent');
               ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                 content: Text(userAgent),
               ));
               break;
             case _MenuOptions.javascriptChannel:
               await controller.data!.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();''');
               break;
             case _MenuOptions.clearCookies:
               _onClearCookies();
               break;
             case _MenuOptions.listCookies:
               _onListCookies(controller.data!);
               break;
             case _MenuOptions.addCookie:
               _onAddCookie(controller.data!);
               break;
             case _MenuOptions.setCookie:
               _onSetCookie(controller.data!);
               break;
             case _MenuOptions.removeCookie:
               _onRemoveCookie(controller.data!);
               Break;
             // Add from here ...
             case _MenuOptions.loadFlutterAsset:
               _onLoadFlutterAssetExample(controller.data!, context);
               break;
             case _MenuOptions.loadLocalFile:
               _onLoadLocalFileExample(controller.data!, context);
               break;
             case _MenuOptions.loadHtmlString:
               _onLoadHtmlStringExample(controller.data!, context);
               Break;
             // ... 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'),
           ),
           // Add from here ...
           const PopupMenuItem<_MenuOptions>(
             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.
         ],
       );
     },
   );
 }

Tester les éléments, le fichier et la chaîne HTML

Pour vérifier si le code précédemment mis en œuvre fonctionne correctement, vous pouvez l'exécuter sur votre appareil et cliquer sur l'un des éléments de menu nouvellement ajouté. Vous remarquerez que _onLoadFlutterAssetExample utilise style.css que nous avons ajouté pour remplacer l'en-tête du fichier HTML par la couleur bleue.

13. Terminé !

Félicitations ! Vous avez terminé l'atelier de programmation. Vous trouverez le code final de cet atelier de programmation dans le dossier atelier de programmation.

Pour aller plus loin, suivez les autres ateliers de programmation Flutter.