feat: manage, account and help pages
- 管理页:单元集 / 计划 / 算法三个维度,卡片带元数据 - 账户页:资料、学习数据、竖直分组的四个功能入口 - 新增设置、文档与帮助(底部可上滑助手)、反馈、关于四个页面 - 抽出 StatBlock 与 GroupedListSection 共用组件 - 关闭 Android 预测式返回,转场改为 M3 水平滑入
This commit is contained in:
1 parent
2a307b2f48
commit
22715f7247
15 files changed
+1789
-72
No files matched your search
+152
-6
@@ -1,20 +1,166 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../widgets/empty_state.dart';
|
||||
import '../models/account_profile.dart';
|
||||
import '../widgets/grouped_list_section.dart';
|
||||
import '../widgets/stat_block.dart';
|
||||
import 'about_page.dart';
|
||||
import 'docs_page.dart';
|
||||
import 'feedback_page.dart';
|
||||
import 'settings_page.dart';
|
||||
|
||||
/// 账户:登录、同步与偏好设置。
|
||||
/// 账户页:资料、学习数据、功能入口。
|
||||
class AccountPage extends StatelessWidget {
|
||||
const AccountPage({super.key});
|
||||
|
||||
static const AccountProfile _profile = AccountProfile.sample;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('账户')),
|
||||
body: const EmptyState(
|
||||
icon: Icons.person_outline,
|
||||
title: '账户与同步',
|
||||
message: '登录后可以在多台设备之间同步学习记录。',
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
|
||||
children: const <Widget>[
|
||||
_ProfileCard(profile: _profile),
|
||||
SizedBox(height: 12),
|
||||
_StatsCard(profile: _profile),
|
||||
SizedBox(height: 22),
|
||||
_ActionList(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ProfileCard extends StatelessWidget {
|
||||
const _ProfileCard({required this.profile});
|
||||
|
||||
final AccountProfile profile;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final ColorScheme colors = theme.colorScheme;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: colors.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
CircleAvatar(
|
||||
radius: 30,
|
||||
backgroundColor: colors.primaryContainer,
|
||||
child: Text(
|
||||
profile.displayName.characters.first,
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
color: colors.onPrimaryContainer,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
profile.displayName,
|
||||
style: theme.textTheme.titleMedium,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'ID ${profile.userId} · ${profile.stage}',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: colors.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right_rounded,
|
||||
color: colors.onSurfaceVariant,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _StatsCard extends StatelessWidget {
|
||||
const _StatsCard({required this.profile});
|
||||
|
||||
final AccountProfile profile;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(18),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: StatBlock(
|
||||
label: '累计学习',
|
||||
value: '${profile.totalHours}',
|
||||
unit: '小时',
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: StatBlock(
|
||||
label: '坚持',
|
||||
value: '${profile.streakDays}',
|
||||
unit: '天',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 竖直排列的长按钮列表,样式参考原生 Android 设置页。
|
||||
class _ActionList extends StatelessWidget {
|
||||
const _ActionList();
|
||||
|
||||
static const List<(IconData, String)> _items = <(IconData, String)>[
|
||||
(Icons.settings_outlined, '设置'),
|
||||
(Icons.menu_book_outlined, '文档与帮助'),
|
||||
(Icons.feedback_outlined, '反馈问题'),
|
||||
(Icons.info_outline_rounded, '关于 KiriMemo'),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GroupedListSection(
|
||||
tiles: <Widget>[
|
||||
for (final (IconData icon, String label) in _items)
|
||||
GroupedListTile(
|
||||
icon: icon,
|
||||
title: label,
|
||||
onTap: () => _open(context, label),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _open(BuildContext context, String label) {
|
||||
final Widget page = switch (label) {
|
||||
'设置' => const SettingsPage(),
|
||||
'文档与帮助' => const DocsPage(),
|
||||
'反馈问题' => const FeedbackPage(),
|
||||
_ => const AboutPage(),
|
||||
};
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(builder: (BuildContext _) => page),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user