skywood/lib/pages/mine/page/sky_web_page.dart

310 lines
7.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:skywood/global/const/common_color.dart';
import 'package:skywood/utils/tools/widgets/sky_app_bar.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../../utils/tools/help_img.dart';
/// WebView 页面参数
class WebLinkParams {
final String title;
final String urlString;
final bool showAppbar;
const WebLinkParams({
this.title = 'Web View',
required this.urlString,
this.showAppbar = true,
});
}
/// WebView 页面
class SkyWebPage extends StatefulWidget {
final WebLinkParams params;
const SkyWebPage({
super.key,
required this.params,
});
@override
State<SkyWebPage> createState() => _WebLinkViewState();
}
class _WebLinkViewState extends State<SkyWebPage> {
late WebViewController _webViewController;
int _loadingProgress = 0;
bool _isLoading = true;
bool _hasError = false;
String _errorMessage = '';
String _urlString = '';
@override
void initState() {
super.initState();
_initializeParameters();
_initializeWebView();
_loadUrl();
}
void _initializeParameters() {
_urlString = _validateAndFixUrl(widget.params.urlString);
}
void _initializeWebView() {
_webViewController = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00FF0000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
setState(() {
_loadingProgress = progress;
});
},
onPageStarted: (String url) {
setState(() {
_isLoading = true;
_hasError = false;
_loadingProgress = 0;
});
},
onPageFinished: (String url) async {
setState(() {
_isLoading = false;
_loadingProgress = 100;
});
// 修改网页背景为透明,文字为白色
await _webViewController.runJavaScript('''
document.body.style.background = 'none';
document.body.style.color = '#fff';
// 修改标题样式
var titleElements = document.getElementsByClassName('title');
for (var i = 0; i < titleElements.length; i++) {
titleElements[i].style.display = 'none';
}
// 修改文本背景为白色
var bgElements = document.getElementsByClassName('bg-fff');
for (var i = 0; i < bgElements.length; i++) {
bgElements[i].style.background = 'none';
bgElements[i].style.paddingTop = '0px';
}
// 修改所有文本元素为白色
var allElements = document.getElementsByTagName('*');
for (var i = 0; i < allElements.length; i++) {
allElements[i].style.color = '#fff';
}
// 修改链接颜色
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
links[i].style.color = '#87CEEB';
}
''');
},
onHttpError: (HttpResponseError error) {
setState(() {
_hasError = true;
_errorMessage = 'HTTP Error: ${error.response?.statusCode ?? "Unknown"}';
_isLoading = false;
});
},
onWebResourceError: (WebResourceError error) {
setState(() {
_hasError = true;
_errorMessage = error.description;
_isLoading = false;
});
},
onNavigationRequest: (NavigationRequest request) {
return NavigationDecision.navigate;
},
),
);
}
void _loadUrl() {
if (_urlString.isEmpty) {
setState(() {
_hasError = true;
_errorMessage = 'Invalid URL';
_isLoading = false;
});
return;
}
try {
final uri = Uri.parse(_urlString);
_webViewController.loadRequest(uri);
} catch (e) {
setState(() {
_hasError = true;
_errorMessage = 'Failed to load URL: $e';
_isLoading = false;
});
}
}
String _validateAndFixUrl(String url) {
if (url.isEmpty) {
return '';
}
url = url.trim();
// Already has a valid scheme
if (url.startsWith('http://') || url.startsWith('https://')) {
return url;
}
// Looks like a domain (has a dot and no spaces)
if (url.contains('.') && !url.contains(' ')) {
return 'https://$url';
}
// Treat as search query
return 'https://www.google.com/search?q=${Uri.encodeComponent(url)}';
}
void _reload() {
setState(() {
_hasError = false;
_isLoading = true;
_loadingProgress = 0;
_errorMessage = '';
});
_initializeWebView();
_loadUrl();
}
void _goBack() {
_webViewController.goBack();
}
void _goForward() {
_webViewController.goForward();
}
@override
void dispose() {
_webViewController.clearCache();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: widget.params.showAppbar
? _buildAppBar()
: null,
body: _buildBody(),
);
}
PreferredSizeWidget _buildAppBar() {
return SkyAppBar(
backgroundColor: Colors.transparent,
title: widget.params.title,
titleColor: Colors.white,
actions: [
IconButton(
icon: const Icon(Icons.refresh, color: Colors.white,),
onPressed: _reload,
),
],
);
}
Widget _buildBody() {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
CommonColor.primacyColor,
Color(0xFF2D1B4E),
],
),
),
child: SafeArea(
child: Column(
children: [
// 加载进度条(可选)
if (_isLoading)
LinearProgressIndicator(
value: _loadingProgress / 100,
backgroundColor: Colors.white.withOpacity(0.2),
valueColor: const AlwaysStoppedAnimation<Color>(
Color(0xFFFB98E0),
),
),
Expanded(
child: _buildContent(),
),
],
),
),
);
}
Widget _buildContent() {
if (_isLoading) {
return Center(
child: SizedBox(
width: 120,
height: 120,
child: Image.asset(ConstImg.loading, fit: BoxFit.fill),
),
);
}
if (_hasError) {
return _buildErrorWidget();
}
return WebViewWidget(
controller: _webViewController,
);
}
Widget _buildErrorWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.error_outline,
size: 64.w,
color: Colors.white.withOpacity(0.6),
),
SizedBox(height: 16.h),
Text(
_errorMessage,
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 16.sp,
),
textAlign: TextAlign.center,
),
SizedBox(height: 24.h),
ElevatedButton(
onPressed: _reload,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFFB98E0),
foregroundColor: Colors.white,
),
child: const Text('重试'),
),
],
),
);
}
}