import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:go_router/go_router.dart'; /// WebView 容器(替代 Android AgentWeb) /// 支持:进度条 / 标题自动同步 / 返回键关闭 / 拦截外链 / JS 桥 class AgentWebPage extends StatefulWidget { final String url; final String title; const AgentWebPage({ super.key, required this.url, required this.title, }); @override State createState() => _AgentWebPageState(); } class _AgentWebPageState extends State { InAppWebViewController? _controller; double _progress = 0; String _title = ''; bool _canGoBack = false; @override void initState() { super.initState(); _title = widget.title; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(_title.isEmpty ? '加载中…' : _title), leading: IconButton( icon: const Icon(Icons.close), onPressed: () => context.pop(), ), actions: [ if (_canGoBack) IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => _controller?.goBack(), ), IconButton( icon: const Icon(Icons.refresh), onPressed: () => _controller?.reload(), ), PopupMenuButton( onSelected: (v) async { switch (v) { case 'copy': // ignore: deprecated_member_use // await Clipboard.setData(ClipboardData(text: widget.url)); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('链接已复制')), ); } break; case 'browser': // ignore: deprecated_member_use // await launchUrl(Uri.parse(widget.url)); break; } }, itemBuilder: (_) => const [ PopupMenuItem(value: 'copy', child: Text('复制链接')), PopupMenuItem(value: 'browser', child: Text('浏览器打开')), ], ), ], bottom: _progress < 1 ? PreferredSize( preferredSize: const Size.fromHeight(2), child: LinearProgressIndicator( value: _progress, backgroundColor: Colors.white24, valueColor: const AlwaysStoppedAnimation(Colors.white), ), ) : null, ), body: InAppWebView( initialUrlRequest: URLRequest(url: WebUri(widget.url)), initialSettings: InAppWebViewSettings( javaScriptEnabled: true, javaScriptCanOpenWindowsAutomatically: true, useShouldOverrideUrlLoading: true, mediaPlaybackRequiresUserGesture: false, allowFileAccess: true, domStorageEnabled: true, databaseEnabled: true, cacheEnabled: true, supportZoom: true, transparentBackground: false, ), onWebViewCreated: (c) => _controller = c, onProgressChanged: (_, p) { if (!mounted) return; setState(() => _progress = p / 100); }, onTitleChanged: (_, t) { if (!mounted) return; setState(() => _title = t ?? widget.title); }, onUpdateVisitedHistory: (_, url, __) { _controller?.canGoBack().then((v) { if (mounted) setState(() => _canGoBack = v); }); }, shouldOverrideUrlLoading: (_, nav) async { final uri = nav.request.url; if (uri == null) return NavigationActionPolicy.ALLOW; // 拦截 tel:/mailto:/weixin:/alipays: final s = uri.toString(); if (s.startsWith('tel:') || s.startsWith('mailto:') || s.startsWith('weixin://') || s.startsWith('alipays://')) { return NavigationActionPolicy.CANCEL; } return NavigationActionPolicy.ALLOW; }, ), ); } }