Files
observer/flutter_app/lib/home/home_screen.dart
T

217 lines
6.4 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:provider/provider.dart';
import '../auth/session_store.dart';
import 'home_view_model.dart';
/// 主界面:当前账号到期时间 + 搜索按钮(强制服务端校验后进相机)+ 充值入口
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<HomeViewModel>().refresh();
});
}
Future<void> _onSearch() async {
final vm = context.read<HomeViewModel>();
final allowed = await vm.verifyForCamera();
if (!mounted) return;
if (vm.sessionExpired) {
_toLogin();
return;
}
if (!allowed) {
if (vm.error != null) {
// 网络/服务端异常:无法确认授权状态,提示错误
_showBlocked(vm.error);
return;
}
// 服务端确认无有效授权:直接进充值页
await _onRecharge();
return;
}
await Navigator.of(context).pushNamed('/camera');
if (mounted) vm.refresh();
}
Future<void> _onRecharge() async {
await Navigator.of(context).pushNamed('/paywall');
if (mounted) context.read<HomeViewModel>().refresh();
}
Future<void> _toLogin() async {
final container = context.read<SessionStore>();
await container.clear();
if (!mounted) return;
Navigator.of(context).pushReplacementNamed('/login');
}
void _showBlocked(String? error) {
showDialog<void>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('授权不可用'),
content: Text(error ?? '授权已过期,请充值后续费'),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: const Text('取消'),
),
FilledButton(
onPressed: () {
Navigator.of(ctx).pop();
_onRecharge();
},
child: const Text('去充值'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
final vm = context.watch<HomeViewModel>();
final license = vm.license;
final active = license?.isActive ?? false;
final statusText = vm.loading
? '加载中…'
: (license == null || license.expiresAt == null
? '未充值'
: (active
? '有效至 ${_fmt(license.expiresAt!)}'
: '已过期(${_fmt(license.expiresAt!)}'));
return Scaffold(
appBar: AppBar(
title: const Text('视野'),
centerTitle: true,
actions: [
IconButton(
tooltip: '退出登录',
icon: const Icon(Icons.logout),
onPressed: _toLogin,
),
],
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 24),
_StatusCard(
licenseText: statusText,
active: active,
),
const SizedBox(height: 40),
SizedBox(
height: 88,
child: FilledButton.icon(
onPressed: vm.verifying ? null : _onSearch,
style: FilledButton.styleFrom(
backgroundColor: Colors.green,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(44),
),
),
icon: vm.verifying
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2, color: Colors.white),
)
: const Icon(Icons.visibility, size: 32),
label: Text(
vm.verifying ? '正在校验授权…' : '打开视野',
style: const TextStyle(
fontSize: 22, fontWeight: FontWeight.w600),
),
),
),
const SizedBox(height: 16),
SizedBox(
height: 52,
child: OutlinedButton.icon(
onPressed: _onRecharge,
icon: const Icon(Icons.payment),
label: const Text('充值', style: TextStyle(fontSize: 16)),
),
),
if (vm.error != null && vm.license != null) ...[
const SizedBox(height: 16),
Text(
vm.error!,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.red),
),
],
],
),
),
),
);
}
static String _fmt(DateTime t) {
String p(int n) => n.toString().padLeft(2, '0');
return '${t.year}-${p(t.month)}-${p(t.day)} ${p(t.hour)}:${p(t.minute)}';
}
}
class _StatusCard extends StatelessWidget {
final String licenseText;
final bool active;
const _StatusCard({
required this.licenseText,
required this.active,
});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Row(
children: [
Icon(
active ? Icons.verified_user : Icons.error_outline,
color: active ? Colors.green : Colors.orange,
size: 36,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('当前账户',
style: TextStyle(
color: Colors.grey.shade600, fontSize: 13)),
const SizedBox(height: 4),
Text(
licenseText,
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w600),
),
],
),
),
],
),
),
);
}
}