210 lines
5.7 KiB
Dart
210 lines
5.7 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'config.dart';
|
|
|
|
Future<void> main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
// 全面屏:内容延伸到状态栏/导航栏之下,刘海与手势区交给 SafeArea 处理
|
|
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
|
runApp(const DshApp());
|
|
}
|
|
|
|
class DshApp extends StatelessWidget {
|
|
const DshApp({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
|
// 站点为深色主题:状态栏透明、图标反白
|
|
value: const SystemUiOverlayStyle(
|
|
statusBarColor: Colors.transparent,
|
|
statusBarIconBrightness: Brightness.light,
|
|
systemNavigationBarColor: Colors.transparent,
|
|
systemNavigationBarIconBrightness: Brightness.light,
|
|
),
|
|
child: MaterialApp(
|
|
title: 'DSH 工作台',
|
|
debugShowCheckedModeBanner: false,
|
|
theme: ThemeData(
|
|
useMaterial3: true,
|
|
colorScheme: ColorScheme.fromSeed(
|
|
seedColor: const Color(kThemeColorValue),
|
|
brightness: Brightness.dark,
|
|
),
|
|
scaffoldBackgroundColor: const Color(kThemeColorValue),
|
|
),
|
|
home: const HomePage(),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class HomePage extends StatefulWidget {
|
|
const HomePage({super.key});
|
|
|
|
@override
|
|
State<HomePage> createState() => _HomePageState();
|
|
}
|
|
|
|
class _HomePageState extends State<HomePage> {
|
|
MethodChannel? _channel;
|
|
bool _loading = true;
|
|
String? _loadError;
|
|
|
|
Future<void> _onViewCreated(int id) async {
|
|
final channel = MethodChannel('dsh.workbench/gecko/$id');
|
|
_channel = channel;
|
|
channel.setMethodCallHandler((call) async {
|
|
if (!mounted) return;
|
|
switch (call.method) {
|
|
case 'onPageStarted':
|
|
setState(() {
|
|
_loading = true;
|
|
_loadError = null;
|
|
});
|
|
case 'onPageFinished':
|
|
setState(() => _loading = false);
|
|
case 'onLoadError':
|
|
setState(() {
|
|
_loading = false;
|
|
_loadError = call.arguments as String? ?? '页面加载失败';
|
|
});
|
|
case 'onDownloadError':
|
|
ScaffoldMessenger.of(context)
|
|
.showSnackBar(SnackBar(content: Text('下载失败:${call.arguments}')));
|
|
}
|
|
});
|
|
await _invoke('load');
|
|
}
|
|
|
|
Future<T?> _invoke<T>(String method) async {
|
|
try {
|
|
return await _channel?.invokeMethod<T>(method);
|
|
} on PlatformException catch (error) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_loading = false;
|
|
_loadError = error.message ?? '浏览器启动失败';
|
|
});
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<void> _onPopInvoked(bool didPop, Object? result) async {
|
|
if (didPop) return;
|
|
if (await _invoke<bool>('goBack') == false && mounted) {
|
|
await SystemNavigator.pop();
|
|
}
|
|
}
|
|
|
|
void _retry() {
|
|
setState(() {
|
|
_loadError = null;
|
|
_loading = true;
|
|
});
|
|
unawaited(_invoke<void>('reload'));
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_channel?.setMethodCallHandler(null);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return PopScope(
|
|
canPop: false,
|
|
onPopInvokedWithResult: _onPopInvoked,
|
|
child: Scaffold(
|
|
backgroundColor: const Color(kThemeColorValue),
|
|
body: SafeArea(
|
|
child: Stack(
|
|
children: [
|
|
AndroidView(
|
|
viewType: 'dsh.workbench/gecko',
|
|
creationParams: const {'url': kSiteUrl},
|
|
creationParamsCodec: const StandardMessageCodec(),
|
|
onPlatformViewCreated: _onViewCreated,
|
|
),
|
|
if (_loadError != null)
|
|
_ErrorView(message: _loadError!, onRetry: _retry)
|
|
else if (_loading)
|
|
const _LoadingView(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _LoadingView extends StatelessWidget {
|
|
const _LoadingView();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return const ColoredBox(
|
|
color: Color(kThemeColorValue),
|
|
child: Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
CircularProgressIndicator(color: Colors.white70),
|
|
SizedBox(height: 16),
|
|
Text('正在加载…', style: TextStyle(color: Colors.white70)),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ErrorView extends StatelessWidget {
|
|
const _ErrorView({required this.message, required this.onRetry});
|
|
|
|
final String message;
|
|
final VoidCallback onRetry;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ColoredBox(
|
|
color: const Color(kThemeColorValue),
|
|
child: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(32),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Icon(Icons.cloud_off, size: 56, color: Colors.white54),
|
|
const SizedBox(height: 16),
|
|
const Text(
|
|
'无法连接到服务器',
|
|
style: TextStyle(color: Colors.white, fontSize: 16),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
message,
|
|
textAlign: TextAlign.center,
|
|
maxLines: 3,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(color: Colors.white38, fontSize: 12),
|
|
),
|
|
const SizedBox(height: 20),
|
|
FilledButton.icon(
|
|
onPressed: onRetry,
|
|
icon: const Icon(Icons.refresh),
|
|
label: const Text('重试'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|