FunyMeeAI/lib/features/web/app_web_view_screen.dart

155 lines
4.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../design/pencil_theme.dart';
/// 通用应用内 Web 页(任意 HTTPS/HTTP 链接),供用户协议、隐私政策及其他 H5 复用。
class AppWebViewScreen extends StatefulWidget {
const AppWebViewScreen({
super.key,
required this.title,
required this.initialUrl,
});
final String title;
/// 完整 URL含 `http`/`https`)。
final String initialUrl;
@override
State<AppWebViewScreen> createState() => _AppWebViewScreenState();
}
class _AppWebViewScreenState extends State<AppWebViewScreen> {
WebViewController? _controller;
bool _loading = true;
String? _loadError;
@override
void initState() {
super.initState();
final uri = _parseHttpUrl(widget.initialUrl);
if (uri == null) {
_loadError = 'Invalid link';
_loading = false;
return;
}
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (_) {
if (mounted) setState(() => _loading = true);
},
onPageFinished: (_) {
if (mounted) setState(() => _loading = false);
},
onWebResourceError: (WebResourceError e) {
if (mounted) {
setState(() {
_loading = false;
_loadError = e.description.isNotEmpty
? e.description
: 'Failed to load page';
});
}
},
),
)
..loadRequest(uri);
}
static Uri? _parseHttpUrl(String raw) {
final u = Uri.tryParse(raw.trim());
if (u == null || !u.hasScheme) return null;
if (u.scheme != 'http' && u.scheme != 'https') return null;
return u;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
surfaceTintColor: Colors.transparent,
elevation: 0,
foregroundColor: PencilTheme.stone900,
title: Text(
widget.title,
style: GoogleFonts.inter(
fontSize: 17,
fontWeight: FontWeight.w600,
color: PencilTheme.stone900,
),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20),
onPressed: () => Navigator.of(context).pop(),
),
),
body: _buildBody(),
);
}
Widget _buildBody() {
if (_loadError != null && _controller == null) {
return Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Text(
_loadError!,
textAlign: TextAlign.center,
style: GoogleFonts.inter(
fontSize: 15,
color: PencilTheme.stone600,
),
),
),
);
}
final c = _controller;
if (c == null) {
return const SizedBox.shrink();
}
return Stack(
fit: StackFit.expand,
children: [
WebViewWidget(controller: c),
if (_loading)
const ColoredBox(
color: Color(0x0F000000),
child: Center(
child: CircularProgressIndicator(
color: PencilTheme.underlineGold,
),
),
),
if (_loadError != null && _controller != null)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Material(
color: const Color(0xFFFFF7ED),
child: SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(
_loadError!,
style: GoogleFonts.inter(
fontSize: 13,
color: const Color(0xFF9A3412),
),
),
),
),
),
),
],
);
}
}