Files
KiriMemo/lib/widgets/grouped_list_section.dart
T
pluv 22715f7247 feat: manage, account and help pages
- 管理页:单元集 / 计划 / 算法三个维度,卡片带元数据
- 账户页:资料、学习数据、竖直分组的四个功能入口
- 新增设置、文档与帮助(底部可上滑助手)、反馈、关于四个页面
- 抽出 StatBlock 与 GroupedListSection 共用组件
- 关闭 Android 预测式返回,转场改为 M3 水平滑入
2026-10-24 15:21:30 +08:00

118 lines
3.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';
/// 一组圆角卡片式列表项,行间自动加分隔线。
///
/// 容器用 [Material] 而不是 `Container`:`InkWell` 的水波纹画在最近的
/// `Material` 祖先上,普通容器会让墨水落到外层 Scaffold,圆角裁剪失效,
/// 按住时就变成直角矩形。
class GroupedListSection extends StatelessWidget {
const GroupedListSection({
required this.tiles,
this.dividerIndent = 58,
super.key,
});
final List<Widget> tiles;
/// 分隔线左侧缩进,默认与图标后的文字对齐。
final double dividerIndent;
@override
Widget build(BuildContext context) {
final ColorScheme colors = Theme.of(context).colorScheme;
return Material(
color: colors.surfaceContainerLow,
borderRadius: BorderRadius.circular(20),
clipBehavior: Clip.antiAlias,
child: Column(
children: <Widget>[
for (final (int index, Widget tile) in tiles.indexed) ...<Widget>[
tile,
if (index != tiles.length - 1)
Divider(
height: 1,
thickness: 1,
indent: dividerIndent,
color: colors.outlineVariant.withValues(alpha: 0.6),
),
],
],
),
);
}
}
/// 分组列表里的一行:图标 + 标题(+ 可选副标题)+ 右侧箭头。
class GroupedListTile extends StatelessWidget {
const GroupedListTile({
required this.icon,
required this.title,
this.subtitle,
this.onTap,
this.trailing,
super.key,
});
final IconData icon;
final String title;
final String? subtitle;
final VoidCallback? onTap;
/// 右侧自定义内容;不传且有 [onTap] 时显示箭头。
final Widget? trailing;
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final ColorScheme colors = theme.colorScheme;
final String? subtitle = this.subtitle;
final Widget? trailing = this.trailing;
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 18),
child: Row(
children: <Widget>[
Icon(icon, size: 22, color: colors.primary),
const SizedBox(width: 18),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: theme.textTheme.bodyLarge,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (subtitle != null) ...<Widget>[
const SizedBox(height: 2),
Text(
subtitle,
style: theme.textTheme.bodySmall?.copyWith(
color: colors.onSurfaceVariant,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
if (trailing != null)
trailing
else if (onTap != null)
Icon(
Icons.chevron_right_rounded,
size: 22,
color: colors.onSurfaceVariant,
),
],
),
),
);
}
}