Flutter: отображение веб страницы в виджете
Работа с webview во Flutter не так проста как на «чистом» Java/Kotlin. В основном из-за того что, на pub.dev есть большое разнообразие собственно вапперов к webview, а потому выбрать рабочую и полноценную реализацию не так просто. Я например потратил с полдня, чтобы выяснить, что:
webview_flutter — не отображает страницы с «плохими» сертификатами SSL. Например самоподписанными, или с сертификатами с истёкшим сроком действия, или с сертификатом к которому нет доверия (теперь это все сайты госучреждений и некоторых банков). И нет никакого способа (или не нашел), заставить это сделать.
flutter_inappwebview — та же самая проблема что и в вышеуказанном плагине. Да, там есть ключ «игнорировать ошибки SSL», но он не работает
flutter_webview_plugin — выпущен 21 месяц назад, и при компиляции уже ругается на отсутствие поддержки Android 12. Хотя как раз этот плагин, работает со страницами с плохим SSL именно так как нужно
flutter_webview_plugin_ios_android — а вот этот плагин, это как я понял «подхваченый из ослабеших» рук разработчиков flutter_webview_plugin, и доработаный уже под Android 12. На нём мои поиски и закончились. Ниже минимальный пример для работы с ним:

| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | import 'package:flutter/material.dart'; import 'package:flutter_webview_plugin_ios_android/flutter_webview_plugin_ios_android.dart'; import 'dart:async'; void main() {   WidgetsFlutterBinding.ensureInitialized();   runApp(const MyApp()); } class MyApp extends StatelessWidget {   const MyApp({super.key});   @override   Widget build(BuildContext context) {     return MaterialApp(       title: 'Flutter Demo',       theme: ThemeData(         primarySwatch: Colors.blue,       ),       routes: {          '/': (_) => const MyHomePage(),       }     );   } } class MyHomePage extends StatefulWidget {   const MyHomePage({super.key});   @override   State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> {   String selectedUrl = 'https://грибовы.рф';   final flutterWebViewPlugin = FlutterWebviewPlugin();   // при изменении url   late StreamSubscription<String> _onUrlChanged;   // при ошибке   late StreamSubscription<WebViewHttpError> _onHttpError;   // изменение процента загрузки страницы   late StreamSubscription<double> _onProgressChanged;   // поскролили вверх-вниз   late StreamSubscription<double> _onScrollYChanged;   // поскролили вправо-влево   late StreamSubscription<double> _onScrollXChanged;   @override   void dispose() {     _onUrlChanged.cancel();     _onHttpError.cancel();     _onProgressChanged.cancel();     _onScrollXChanged.cancel();     _onScrollYChanged.cancel();     flutterWebViewPlugin.dispose();     super.dispose();   }   @override   void initState() {     super.initState();     flutterWebViewPlugin.close();     // Слушатель изменения url страницы     _onUrlChanged = flutterWebViewPlugin.onUrlChanged.listen((String url) {       print("URL: $url");     });     // изменение прогресса загрузки     _onProgressChanged =         flutterWebViewPlugin.onProgressChanged.listen((double progress) {             setState(() {               print('onProgressChanged: $progress');             });         });     _onScrollYChanged =         flutterWebViewPlugin.onScrollYChanged.listen((double y) {         });     _onScrollXChanged =         flutterWebViewPlugin.onScrollXChanged.listen((double x) {         });     _onHttpError =         flutterWebViewPlugin.onHttpError.listen((WebViewHttpError error) {           print("Ошибка загрузки:  ${error.code} ${error.url}");         });   }   // перехват вызовов Javascript   final Set<JavascriptChannel> jsChannels = [     JavascriptChannel(         name: 'Print',         onMessageReceived: (JavascriptMessage message) {           print(message.message);         }),   ].toSet();   @override   Widget build(BuildContext context) {     return  WebviewScaffold(         ignoreSSLErrors: true,         url: selectedUrl,         javascriptChannels: jsChannels,         mediaPlaybackRequiresUserGesture: false,         appBar: AppBar(           title: const Text('Widget WebView'),         ),         withZoom: true,         withLocalStorage: true,         hidden: false,         initialChild: Container(           color: Colors.redAccent,           child: const Center(             child: Text('Ожидаем загрузки...'),           ),         ),     );   } } |