Files
ttstd_family_care/lib/features/profile/presentation/profile_tab.dart
TongTongStudio 06cdc77172 feat(android): 支持沉浸式状态栏、地图定位与自定义签名
- 配置 edge-to-edge 沉浸式状态栏与刘海屏适配
- 新增百度地图权限、AK 配置及定位权限说明
- 接入 MethodChannel 实现返回键退后台热启动优化
- 配置自定义签名并更新构建逻辑
- 完善 token 刷新与鉴权失效统一处理
- 新增地图页与截图页路由
- 修复返回键拦截导致的 PopScope 失效问题
2026-08-19 03:43:12 +08:00

269 lines
11 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: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))),
],
),
);
}