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. На нём мои поиски и закончились. Ниже минимальный пример для работы с ним:

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('Ожидаем загрузки...'),
),
),
);
}
}
