import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; class AnnouncementDialog extends StatefulWidget { final String title; final String html; final String baseUrl; const AnnouncementDialog({ super.key, required this.title, required this.html, required this.baseUrl, }); static Future show( BuildContext context, { required String title, required String html, required String baseUrl, }) async { if (html.trim().isEmpty) return; await showDialog( context: context, barrierDismissible: true, builder: (_) => AnnouncementDialog( title: title, html: html, baseUrl: baseUrl, ), ); } @override State createState() => _AnnouncementDialogState(); } class _AnnouncementDialogState extends State { late final WebViewController _controller; bool _loading = true; @override void initState() { super.initState(); _controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setBackgroundColor(Colors.transparent) ..setNavigationDelegate( NavigationDelegate( onPageFinished: (_) { if (mounted) setState(() => _loading = false); }, ), ); _load(); } Future _load() async { final origin = Uri.parse(widget.baseUrl).origin; final html = _wrapHtml(widget.html); await _controller.loadHtmlString( html, baseUrl: '$origin/', ); } String _wrapHtml(String body) { final encodedTitle = const HtmlEscape().convert(widget.title); return ''' $encodedTitle $body '''; } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Dialog( insetPadding: const EdgeInsets.symmetric(horizontal: 18, vertical: 28), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(22)), child: ClipRRect( borderRadius: BorderRadius.circular(22), child: SizedBox( width: double.infinity, height: MediaQuery.of(context).size.height * 0.72, child: Column( children: [ Container( padding: const EdgeInsets.fromLTRB(18, 14, 8, 10), decoration: BoxDecoration( color: theme.colorScheme.surface, border: Border( bottom: BorderSide( color: theme.dividerColor.withValues(alpha: 0.45), ), ), ), child: Row( children: [ Expanded( child: Text( widget.title, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w800, ), ), ), IconButton( tooltip: '关闭', onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.close), ), ], ), ), Expanded( child: Stack( children: [ WebViewWidget(controller: _controller), if (_loading) const Center(child: CircularProgressIndicator()), ], ), ), ], ), ), ), ); } }