「Flutter アプリに WebView を追加する」Code Labをwebview_flutter4.0.0以降でやってみた

### 「Flutter アプリに WebView を追加する」はwebview_flutter4.0.0以降に対応していない
詳しく調べた訳ではないのでおそらくです。
[Flutter アプリに WebView を追加する](https://codelabs.developers.google.com/codelabs/flutter-webview?hl=ja#0)

### 実際のコード

```Dart

import 'dart:io';

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

const request = '''
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();''';
const addCookie = '''var date = new Date();
  date.setTime(date.getTime()+(30*24*60*60*1000));
  document.cookie = "FirstName=John; expires=" + date.toGMTString();''';
const removeCookie =
    '''document.cookie = "FirstName=; expires=Thu, 01 Jan 1970 00:00:00 UTC";''';
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>
''';

class WebView extends StatelessWidget {
  WebView({super.key});

  static final url = Uri(
    scheme: 'https',
    host: 'flutter.dev',
  );

  final loadPercentage = ValueNotifier(0);

  @override
  Widget build(BuildContext context) {
    var controller = WebViewController();

    void createJavascriptChannels(JavaScriptMessage message) {
      ScaffoldMessenger.of(context)
          .showSnackBar(SnackBar(content: Text(message.message)));
    }

    controller
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..addJavaScriptChannel('SnackBar',
          onMessageReceived: createJavascriptChannels)
      ..setNavigationDelegate(NavigationDelegate(
        onPageStarted: (url) => {},
        onProgress: (progress) => loadPercentage.value = progress,
        onPageFinished: (url) => loadPercentage.value = 100,
        onNavigationRequest: (request) {
          final host = Uri.parse(request.url).host;
          if (host.contains('youtube.com')) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text(
                  'Blocking navigation to $host',
                ),
              ),
            );
            return NavigationDecision.prevent;
          }
          return NavigationDecision.navigate;
        },
      ))
      ..loadRequest(url);

    var webViewWidget = WebViewWidget(
      controller: controller,
    );

    var valueListenableBuilder = ValueListenableBuilder(
        valueListenable: loadPercentage,
        builder: (context, count, child) {
          return LinearProgressIndicator(
            value: count / 100,
          );
        });

    var stack = Stack(
      children: [webViewWidget, valueListenableBuilder],
    );

    var appBar = AppBar(
      title: const Text('Flutter WebView'),
      actions: [
        NavigationConrtoller(controller: controller),
        Menu(controller: controller)
      ],
    );

    var scaffold = Scaffold(
      appBar: appBar,
      body: stack,
    );

    return scaffold;
  }
}

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

  @override
  Widget build(BuildContext context) {
    late final Row row;
    var left = IconButton(
      icon: const Icon(Icons.arrow_back_ios),
      onPressed: () {
        controller.canGoBack().then((value) => {
              if (value)
                {controller.goBack()}
              else
                {
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('No back history item')),
                  )
                }
            });
      },
    );
    var center = 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;
        }
      },
    );
    var right = IconButton(
      icon: const Icon(Icons.replay),
      onPressed: () {
        controller.reload();
      },
    );
    row = Row(
      children: <Widget>[
        left,
        center,
        right,
      ],
    );
    return row;
  }
}

enum _MenuOptions {
  navigationDelegate,
  userAgent,
  javascriptChannel,
  listCookies,
  clearCookies,
  addCookie,
  setCookie,
  removeCookie,
  loadFlutterAsset,
  loadLocalFile,
  loadHtmlString,
}

class Menu extends StatelessWidget {
  static final url = Uri(
    scheme: 'https',
    host: 'youtube.com',
  );
  final WebViewController controller;
  final cookieManager = WebViewCookieManager();
  Menu({Key? key, required this.controller}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    var menu = PopupMenuButton(itemBuilder: (context) {
      return <PopupMenuEntry>[
        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>(
          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'),
        ),
      ];
    }, onSelected: (value) {
      switch (value) {
        case _MenuOptions.navigationDelegate:
          controller.loadRequest(url);
          break;
        case _MenuOptions.userAgent:
          controller.getUserAgent().then((value) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text('User-Agent: $value'),
              ),
            );
          });
          break;
        case _MenuOptions.javascriptChannel:
          controller.runJavaScript(request).then((value) => {});
          break;
        case _MenuOptions.clearCookies:
          cookieManager.clearCookies();
          break;
        case _MenuOptions.listCookies:
          controller.runJavaScriptReturningResult('document.cookie').then(
            (value) {
              final String cookies = value as String;
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text(
                      cookies.isNotEmpty ? cookies : 'There are no cookies.'),
                ),
              );
            },
          );
          break;
        case _MenuOptions.addCookie:
          controller.runJavaScript(addCookie).then((value) => {
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(
                    content: Text('Custom cookie added.'),
                  ),
                ),
              });
          break;
        case _MenuOptions.setCookie:
          cookieManager
              .setCookie(
                const WebViewCookie(
                    name: 'foo', value: 'bar', domain: 'flutter.dev'),
              )
              .then((value) => {
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(
                        content: Text('Custom cookie is set.'),
                      ),
                    ),
                  });
          break;
        case _MenuOptions.removeCookie:
          controller.runJavaScript(removeCookie).then((value) => {
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(
                    content: Text('Custom cookie removed.'),
                  ),
                ),
              });
          break;
        case _MenuOptions.loadFlutterAsset:
          _onLoadFlutterAssetExample(controller, context);
          break;
        case _MenuOptions.loadLocalFile:
          _onLoadLocalFileExample(controller, context);
          break;
        case _MenuOptions.loadHtmlString:
          _onLoadHtmlStringExample(controller, context);
          break;
      }
    });
    return menu;
  }

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

  Future<void> _onLoadHtmlStringExample(
      WebViewController controller, BuildContext context) async {
    await controller.loadHtmlString(kExamplePage);
  }
}


```