Files
xianyan/lib/features/mine/settings/presentation/theme/theme_shared_widgets.dart
Developer bd083976c6 补充
2026-06-01 08:17:08 +08:00

160 lines
4.5 KiB
Dart

/// ============================================================
/// 闲言APP — 主题设置共享组件
/// 创建时间: 2026-05-19
/// 更新时间: 2026-05-31
/// 作用: 主题设置页面中复用的 SectionHeader、OptionChip 和 ScrollableChipList 组件
/// 上次更新: 新增 ScrollableChipList 水平滑动列表指示器组件
/// ============================================================
import 'package:flutter/cupertino.dart';
import '../../../../../../core/theme/app_radius.dart';
import '../../../../../../core/theme/app_spacing.dart';
import '../../../../../../core/theme/app_theme.dart';
import '../../../../../../core/theme/app_typography.dart';
/// 主题设置分组标题
class ThemeSectionHeader extends StatelessWidget {
const ThemeSectionHeader({
super.key,
required this.icon,
required this.title,
});
final IconData icon;
final String title;
@override
Widget build(BuildContext context) {
final ext = AppTheme.ext(context);
return Row(
children: [
Icon(icon, size: 16, color: ext.accent),
const SizedBox(width: 6),
Text(
title,
style: AppTypography.subhead.copyWith(
color: ext.textPrimary,
fontWeight: FontWeight.w600,
),
),
],
);
}
}
/// 主题设置选项芯片
class ThemeOptionChip extends StatelessWidget {
const ThemeOptionChip({
super.key,
required this.label,
required this.isSelected,
required this.onTap,
this.icon,
});
final String label;
final bool isSelected;
final VoidCallback onTap;
final IconData? icon;
@override
Widget build(BuildContext context) {
final ext = AppTheme.ext(context);
return GestureDetector(
onTap: onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
curve: Curves.easeOutCubic,
margin: const EdgeInsets.symmetric(horizontal: 2),
padding: const EdgeInsets.symmetric(
vertical: AppSpacing.sm,
horizontal: AppSpacing.md,
),
decoration: BoxDecoration(
color: isSelected
? ext.accent.withValues(alpha: 0.15)
: ext.bgSecondary,
borderRadius: AppRadius.of(context).mdBorder,
border: isSelected ? Border.all(color: ext.accent, width: 1.5) : null,
),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null) ...[
Icon(
icon,
size: 14,
color: isSelected ? ext.accent : ext.textSecondary,
),
const SizedBox(width: 4),
],
Text(
label,
style: AppTypography.subhead.copyWith(
color: isSelected ? ext.accent : ext.textSecondary,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
),
),
],
),
),
),
);
}
}
/// 水平滑动列表指示器
/// 包裹水平 ListView 并在右侧添加渐变遮罩,提示用户可滑动
/// 遮罩使用双层渐变(半透明+背景色),确保深色/浅色模式下均可见
class ScrollableChipList extends StatelessWidget {
const ScrollableChipList({
super.key,
required this.itemCount,
required this.itemBuilder,
this.height = 40,
});
final int itemCount;
final Widget Function(BuildContext context, int index) itemBuilder;
final double height;
@override
Widget build(BuildContext context) {
final ext = AppTheme.ext(context);
return SizedBox(
height: height,
child: Stack(
children: [
ListView.separated(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.only(right: 32),
itemCount: itemCount,
separatorBuilder: (_, __) => const SizedBox(width: 6),
itemBuilder: itemBuilder,
),
Positioned(
right: 0,
top: 0,
bottom: 0,
width: 40,
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
ext.bgPrimary.withValues(alpha: 0),
ext.bgPrimary.withValues(alpha: 0.6),
ext.bgPrimary,
],
stops: const [0.0, 0.4, 1.0],
),
),
),
),
],
),
);
}
}