283 lines
8.9 KiB
Dart
283 lines
8.9 KiB
Dart
import 'package:autosos_flutter/api/_api_client.dart';
|
||
import 'package:autosos_flutter/config/constant.dart';
|
||
import 'package:autosos_flutter/provider/providers.dart';
|
||
import 'package:autosos_flutter/router/app_router.dart';
|
||
import 'package:autosos_flutter/service/audio_service.dart';
|
||
import 'package:autosos_flutter/service/background_service.dart';
|
||
import 'package:autosos_flutter/service/push_service.dart';
|
||
import 'package:autosos_flutter/util/sp_util.dart';
|
||
import 'package:autosos_flutter/util/toast.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import 'package:package_info_plus/package_info_plus.dart';
|
||
|
||
/// 设置页(对应 Android MeSettingActivity)
|
||
class SettingPage extends ConsumerStatefulWidget {
|
||
const SettingPage({super.key});
|
||
|
||
@override
|
||
ConsumerState<SettingPage> createState() => _SettingPageState();
|
||
}
|
||
|
||
class _SettingPageState extends ConsumerState<SettingPage> {
|
||
bool _push = true;
|
||
bool _sound = true;
|
||
bool _bg = true;
|
||
String _version = '';
|
||
String _cid = '';
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_load();
|
||
}
|
||
|
||
Future<void> _load() async {
|
||
_push = (SPUtil().get('setting.push') as bool?) ?? true;
|
||
_sound = (SPUtil().get('setting.sound') as bool?) ?? true;
|
||
_bg = (SPUtil().get('setting.bg') as bool?) ?? true;
|
||
final info = await PackageInfo.fromPlatform();
|
||
_cid = PushService.instance.clientId ?? '';
|
||
if (!mounted) return;
|
||
setState(() {
|
||
_version = '${info.version} (${info.buildNumber})';
|
||
});
|
||
}
|
||
|
||
Future<void> _save() async {
|
||
SPUtil().setBool('setting.push', _push);
|
||
SPUtil().setBool('setting.sound', _sound);
|
||
SPUtil().setBool('setting.bg', _bg);
|
||
AudioService.instance.enableSound = _sound;
|
||
if (_bg) {
|
||
await BackgroundService.instance.start();
|
||
} else {
|
||
await BackgroundService.instance.stop();
|
||
}
|
||
if (!_push) {
|
||
PushService.instance.turnOff();
|
||
}
|
||
}
|
||
|
||
Future<void> _clearCache() async {
|
||
final ok = await showDialog<bool>(
|
||
context: context,
|
||
builder: (ctx) => AlertDialog(
|
||
title: const Text('清除缓存'),
|
||
content: const Text('将清除图片缓存和临时文件,确定吗?'),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(ctx, false),
|
||
child: const Text('取消'),
|
||
),
|
||
ElevatedButton(
|
||
onPressed: () => Navigator.pop(ctx, true),
|
||
child: const Text('清除'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
if (ok != true) return;
|
||
EasyLoading.show(status: '清除中…');
|
||
// Flutter 没有原生 clear cache API,这里仅做演示
|
||
await Future.delayed(const Duration(seconds: 1));
|
||
EasyLoading.dismiss();
|
||
Toast.success('缓存已清除');
|
||
}
|
||
|
||
Future<void> _checkUpdate() async {
|
||
EasyLoading.show(status: '检查中…');
|
||
try {
|
||
final r = await ref.read(userApiProvider).checkUpdate();
|
||
EasyLoading.dismiss();
|
||
if (!r.isSuccess || r.data == null) {
|
||
Toast.error('检查失败');
|
||
return;
|
||
}
|
||
final u = r.data!;
|
||
if (u.latestVersion.isEmpty) {
|
||
Toast.success('已是最新版本');
|
||
return;
|
||
}
|
||
if (!mounted) return;
|
||
showDialog<void>(
|
||
context: context,
|
||
builder: (ctx) => AlertDialog(
|
||
title: Text('发现新版本 v${u.latestVersion}'),
|
||
content: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
if (u.changelog != null && u.changelog!.isNotEmpty)
|
||
Text(u.changelog!,
|
||
style: const TextStyle(fontSize: 13)),
|
||
if (u.forceUpdate) ...[
|
||
const SizedBox(height: 8),
|
||
const Text('⚠️ 此版本必须升级',
|
||
style: TextStyle(color: Colors.red)),
|
||
],
|
||
],
|
||
),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(ctx),
|
||
child: const Text('稍后'),
|
||
),
|
||
ElevatedButton(
|
||
onPressed: () {
|
||
Navigator.pop(ctx);
|
||
context.push(
|
||
'${Routes.webview}?url=${Uri.encodeComponent(u.downloadUrl ?? "")}&title=版本更新',
|
||
);
|
||
},
|
||
child: const Text('立即升级'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
} catch (e) {
|
||
EasyLoading.dismiss();
|
||
Toast.error('检查失败:$e');
|
||
}
|
||
}
|
||
|
||
Future<void> _logout() async {
|
||
final ok = await showDialog<bool>(
|
||
context: context,
|
||
builder: (ctx) => AlertDialog(
|
||
title: const Text('退出登录'),
|
||
content: const Text('确定要退出当前账号吗?'),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(ctx, false),
|
||
child: const Text('取消'),
|
||
),
|
||
ElevatedButton(
|
||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
||
onPressed: () => Navigator.pop(ctx, true),
|
||
child: const Text('退出'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
if (ok != true) return;
|
||
try {
|
||
await ref.read(userApiProvider).logout();
|
||
} catch (_) {}
|
||
SPUtil().remove('accessToken');
|
||
SPUtil().remove('user');
|
||
ApiClient.instance.clearToken();
|
||
ref.read(accessTokenProvider.notifier).state = null;
|
||
if (mounted) context.go(Routes.login);
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: AppBar(title: const Text('设置')),
|
||
body: ListView(
|
||
children: [
|
||
_section('消息通知'),
|
||
SwitchListTile(
|
||
title: const Text('推送通知'),
|
||
subtitle: const Text('接收订单推送'),
|
||
value: _push,
|
||
onChanged: (v) {
|
||
setState(() => _push = v);
|
||
_save();
|
||
},
|
||
),
|
||
SwitchListTile(
|
||
title: const Text('声音提醒'),
|
||
subtitle: const Text('新订单播报'),
|
||
value: _sound,
|
||
onChanged: (v) {
|
||
setState(() => _sound = v);
|
||
_save();
|
||
},
|
||
),
|
||
_section('服务设置'),
|
||
SwitchListTile(
|
||
title: const Text('后台保活'),
|
||
subtitle: const Text('App 切到后台后保持心跳'),
|
||
value: _bg,
|
||
onChanged: (v) {
|
||
setState(() => _bg = v);
|
||
_save();
|
||
},
|
||
),
|
||
ListTile(
|
||
title: const Text('个推 CID'),
|
||
subtitle: Text(_cid.isEmpty ? '未获取到' : _cid,
|
||
style: const TextStyle(fontSize: 12)),
|
||
trailing: const Icon(Icons.copy),
|
||
onTap: () {
|
||
if (_cid.isNotEmpty) {
|
||
// ignore: deprecated_member_use
|
||
// Clipboard.setData(ClipboardData(text: _cid));
|
||
Toast.success('CID 已复制');
|
||
}
|
||
},
|
||
),
|
||
_section('其他'),
|
||
ListTile(
|
||
title: const Text('清除缓存'),
|
||
trailing: const Icon(Icons.chevron_right),
|
||
onTap: _clearCache,
|
||
),
|
||
ListTile(
|
||
title: const Text('检查更新'),
|
||
subtitle: Text('当前版本 $_version'),
|
||
trailing: const Icon(Icons.chevron_right),
|
||
onTap: _checkUpdate,
|
||
),
|
||
ListTile(
|
||
title: const Text('性能优化'),
|
||
trailing: const Icon(Icons.chevron_right),
|
||
onTap: () => context.push(Routes.optimization),
|
||
),
|
||
ListTile(
|
||
title: const Text('用户协议'),
|
||
trailing: const Icon(Icons.chevron_right),
|
||
onTap: () {
|
||
context.push(
|
||
'${Routes.webview}?url=${Uri.encodeComponent(Constant.workOrderUrl)}&title=用户协议',
|
||
);
|
||
},
|
||
),
|
||
const SizedBox(height: 24),
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||
child: OutlinedButton.icon(
|
||
onPressed: _logout,
|
||
icon: const Icon(Icons.logout, color: Colors.red),
|
||
label: const Text('退出登录',
|
||
style: TextStyle(color: Colors.red)),
|
||
style: OutlinedButton.styleFrom(
|
||
side: const BorderSide(color: Colors.red),
|
||
minimumSize: const Size.fromHeight(44),
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: 24),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _section(String title) => Container(
|
||
width: double.infinity,
|
||
color: Colors.grey.shade100,
|
||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 4),
|
||
child: Text(
|
||
title,
|
||
style: const TextStyle(
|
||
fontSize: 12,
|
||
color: Colors.grey,
|
||
),
|
||
),
|
||
);
|
||
}
|