- 配置 edge-to-edge 沉浸式状态栏与刘海屏适配 - 新增百度地图权限、AK 配置及定位权限说明 - 接入 MethodChannel 实现返回键退后台热启动优化 - 配置自定义签名并更新构建逻辑 - 完善 token 刷新与鉴权失效统一处理 - 新增地图页与截图页路由 - 修复返回键拦截导致的 PopScope 失效问题
269 lines
11 KiB
Dart
269 lines
11 KiB
Dart
import 'package:cupertino_ui/cupertino_ui.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
|
||
import '../../../core/widgets/feature_ui.dart';
|
||
import '../../../features/device/data/device_providers.dart';
|
||
import '../../../features/device/domain/device_models.dart';
|
||
import '../../../l10n/app_localizations.dart';
|
||
import '../../auth/presentation/auth_controller.dart';
|
||
|
||
/// 我的 Tab 内容(特性模块:profile / presentation)。
|
||
///
|
||
/// 对应移动端设计稿的「我的」页:个人资料头部、绑定设备、
|
||
/// 个人资料、设置列表与退出登录。登出委托给 authController。
|
||
class ProfileTab extends ConsumerWidget {
|
||
const ProfileTab({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context, WidgetRef ref) {
|
||
final l10n = AppLocalizations.of(context);
|
||
|
||
return CupertinoPageScaffold(
|
||
child: SafeArea(
|
||
child: ListView(
|
||
padding: const EdgeInsets.only(bottom: 96),
|
||
children: [
|
||
// 个人资料头部
|
||
Container(
|
||
padding: const EdgeInsets.fromLTRB(18, 18, 18, 26),
|
||
decoration: const BoxDecoration(
|
||
gradient: LinearGradient(
|
||
colors: [AppColors.green, AppColors.blue],
|
||
begin: Alignment.topLeft,
|
||
end: Alignment.bottomRight,
|
||
),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
width: 62,
|
||
height: 62,
|
||
decoration: BoxDecoration(
|
||
color: CupertinoColors.white.withValues(alpha: 0.25),
|
||
shape: BoxShape.circle,
|
||
border: Border.all(color: CupertinoColors.white.withValues(alpha: 0.6), width: 2),
|
||
),
|
||
child: const Center(
|
||
child: Text('U', style: TextStyle(fontSize: 26, fontWeight: FontWeight.w700, color: CupertinoColors.white)),
|
||
),
|
||
),
|
||
const SizedBox(width: 14),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text('用户名',
|
||
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w700, color: CupertinoColors.white)),
|
||
const SizedBox(height: 4),
|
||
Text('ID: u_8821 · 平板管家 Pro',
|
||
style: TextStyle(fontSize: 12.5, color: CupertinoColors.white.withValues(alpha: 0.92))),
|
||
],
|
||
),
|
||
),
|
||
GestureDetector(
|
||
onTap: () {},
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 7),
|
||
decoration: BoxDecoration(
|
||
color: CupertinoColors.white.withValues(alpha: 0.2),
|
||
borderRadius: BorderRadius.circular(20),
|
||
),
|
||
child: Text(l10n.profileEdit,
|
||
style: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w600, color: CupertinoColors.white)),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
|
||
SectionTitle(l10n.deviceBound),
|
||
_deviceListCard(ref, l10n),
|
||
|
||
SectionTitle(l10n.profileInfo),
|
||
CardBox(
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
children: [
|
||
_infoRow('🖼️', AppColors.purple, l10n.piAvatar, null),
|
||
const AppDivider(),
|
||
_infoRow('📛', AppColors.blue, l10n.piName, '用户名'),
|
||
const AppDivider(),
|
||
_infoRow('✍️', AppColors.orange, l10n.piSign, '热爱生活'),
|
||
],
|
||
),
|
||
),
|
||
|
||
SectionTitle(l10n.settingsTitle),
|
||
CardBox(
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
children: [
|
||
_settingRow('🔔', AppColors.teal, l10n.setNotify, '消息、设备状态提醒'),
|
||
const AppDivider(),
|
||
_settingRow('🔒', const Color(0xFF22C55E), l10n.setPrivacy, '定位、相册、权限管理'),
|
||
const AppDivider(),
|
||
_settingRow('🛡️', AppColors.red, l10n.setSecurity, '登录设备、密码'),
|
||
const AppDivider(),
|
||
_settingRow('❓', AppColors.gray, l10n.setHelp, null),
|
||
const AppDivider(),
|
||
_settingRow('ℹ️', const Color(0xFF64748B), l10n.setAbout, '平板管家 v3.2.1'),
|
||
],
|
||
),
|
||
),
|
||
|
||
GestureDetector(
|
||
onTap: () {
|
||
ref.read(authControllerProvider.notifier).logout();
|
||
context.go('/login');
|
||
},
|
||
child: Container(
|
||
margin: const EdgeInsets.fromLTRB(16, 18, 16, 0),
|
||
height: 48,
|
||
decoration: BoxDecoration(
|
||
color: CupertinoColors.white,
|
||
borderRadius: BorderRadius.circular(14),
|
||
boxShadow: const [
|
||
BoxShadow(color: Color(0x14141E3C), blurRadius: 16, offset: Offset(0, 4)),
|
||
],
|
||
),
|
||
child: Center(
|
||
child: Text(l10n.logout,
|
||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700, color: AppColors.red)),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _deviceListCard(WidgetRef ref, AppLocalizations l10n) {
|
||
final devicesAsync = ref.watch(myDevicesProvider);
|
||
return CardBox(
|
||
padding: EdgeInsets.zero,
|
||
gradient: const LinearGradient(
|
||
colors: [Color(0xFFEEF6FF), Color(0xFFF3FBF6)],
|
||
begin: Alignment.topLeft,
|
||
end: Alignment.bottomRight,
|
||
),
|
||
child: devicesAsync.when(
|
||
loading: () => const Padding(
|
||
padding: EdgeInsets.symmetric(vertical: 24),
|
||
child: Center(child: CupertinoActivityIndicator()),
|
||
),
|
||
error: (e, _) => Padding(
|
||
padding: const EdgeInsets.all(16),
|
||
child: Text('${l10n.deviceLoadFailed}:$e',
|
||
style: const TextStyle(fontSize: 12, color: AppColors.sub)),
|
||
),
|
||
data: (devices) {
|
||
if (devices.isEmpty) {
|
||
return Padding(
|
||
padding: const EdgeInsets.all(16),
|
||
child: Text(l10n.deviceNone,
|
||
style: const TextStyle(fontSize: 13, color: AppColors.sub)),
|
||
);
|
||
}
|
||
return Column(
|
||
children: [
|
||
for (var i = 0; i < devices.length; i++) ...[
|
||
if (i > 0) const AppDivider(),
|
||
_deviceCard(devices[i], l10n),
|
||
],
|
||
],
|
||
);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _deviceCard(DeviceBrief device, AppLocalizations l10n) {
|
||
final sub = device.snModel?.isNotEmpty == true
|
||
? '${l10n.deviceOnline} · ${device.snModel}'
|
||
: l10n.deviceOnline;
|
||
return _deviceRow('📱', AppColors.green,
|
||
device.snName?.isNotEmpty == true ? device.snName! : device.serialno, sub,
|
||
l10n.deviceManage);
|
||
}
|
||
|
||
Widget _deviceRow(String emoji, Color color, String title, String sub, String? action) => Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
width: 36,
|
||
height: 36,
|
||
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(11)),
|
||
child: Center(child: Text(emoji, style: const TextStyle(fontSize: 17))),
|
||
),
|
||
const SizedBox(width: 12),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(title, style: const TextStyle(fontSize: 14.5, fontWeight: FontWeight.w600, color: AppColors.ink)),
|
||
const SizedBox(height: 2),
|
||
Text(sub, style: const TextStyle(fontSize: 12, color: AppColors.sub)),
|
||
],
|
||
),
|
||
),
|
||
if (action != null)
|
||
Text('$action ›', style: const TextStyle(fontSize: 13, color: AppColors.sub))
|
||
else
|
||
const Text('›', style: TextStyle(fontSize: 16, color: Color(0xFFCBD2DC))),
|
||
],
|
||
),
|
||
);
|
||
|
||
Widget _infoRow(String emoji, Color color, String label, String? value) => Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
width: 36,
|
||
height: 36,
|
||
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(11)),
|
||
child: Center(child: Text(emoji, style: const TextStyle(fontSize: 17))),
|
||
),
|
||
const SizedBox(width: 12),
|
||
Text(label, style: const TextStyle(fontSize: 14.5, fontWeight: FontWeight.w600, color: AppColors.ink)),
|
||
const Spacer(),
|
||
if (value != null)
|
||
Text(value, style: const TextStyle(fontSize: 13, color: AppColors.sub)),
|
||
const SizedBox(width: 6),
|
||
const Text('›', style: TextStyle(fontSize: 16, color: Color(0xFFCBD2DC))),
|
||
],
|
||
),
|
||
);
|
||
|
||
Widget _settingRow(String emoji, Color color, String label, String? sub) => Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 13),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
width: 36,
|
||
height: 36,
|
||
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(11)),
|
||
child: Center(child: Text(emoji, style: const TextStyle(fontSize: 17))),
|
||
),
|
||
const SizedBox(width: 12),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(label, style: const TextStyle(fontSize: 14.5, fontWeight: FontWeight.w600, color: AppColors.ink)),
|
||
if (sub != null) ...[
|
||
const SizedBox(height: 2),
|
||
Text(sub, style: const TextStyle(fontSize: 12, color: AppColors.sub)),
|
||
],
|
||
],
|
||
),
|
||
),
|
||
const Text('›', style: TextStyle(fontSize: 16, color: Color(0xFFCBD2DC))),
|
||
],
|
||
),
|
||
);
|
||
}
|