feat: 实现导航页基础功能
- 添加导航条目和分类数据模型 - 实现导航卡片网格展示 - 支持点击卡片打开链接 - 添加主题样式(渐变色、卡片阴影) - 区分系统默认和自定义条目 - 添加分类统计信息"
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../core/constants/app_sizes.dart';
|
||||
import '../../../../core/theme/app_colors.dart';
|
||||
import '../../../../core/theme/app_text_styles.dart';
|
||||
|
||||
/// 对话框里的主按钮:实底、8px 圆角(和侧栏「提交网站」一致)。
|
||||
///
|
||||
/// [danger] 为 true 时用错误色,用在「删除」这类不可逆操作上。
|
||||
class DialogPrimaryButton extends StatefulWidget {
|
||||
const DialogPrimaryButton({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.onTap,
|
||||
this.danger = false,
|
||||
});
|
||||
|
||||
/// 按钮文字。
|
||||
final String label;
|
||||
|
||||
/// 点击回调;为 null 表示禁用。
|
||||
final VoidCallback? onTap;
|
||||
|
||||
/// 是否用错误色(删除类操作)。
|
||||
final bool danger;
|
||||
|
||||
@override
|
||||
State<DialogPrimaryButton> createState() => _DialogPrimaryButtonState();
|
||||
}
|
||||
|
||||
class _DialogPrimaryButtonState extends State<DialogPrimaryButton> {
|
||||
bool _hovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool enabled = widget.onTap != null;
|
||||
final Color background = widget.danger
|
||||
? AppColors.danger
|
||||
: AppColors.primary;
|
||||
|
||||
return MouseRegion(
|
||||
cursor: enabled ? SystemMouseCursors.click : SystemMouseCursors.basic,
|
||||
onEnter: (_) => setState(() => _hovered = true),
|
||||
onExit: (_) => setState(() => _hovered = false),
|
||||
child: GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
child: AnimatedContainer(
|
||||
duration: AppSizes.transition,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSizes.cardPaddingHorizontal,
|
||||
vertical: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: enabled ? background : AppColors.border,
|
||||
borderRadius: BorderRadius.circular(AppSizes.navItemRadius),
|
||||
),
|
||||
child: Text(
|
||||
widget.label,
|
||||
style: AppTextStyles.submitButton.copyWith(
|
||||
color: enabled ? AppColors.surface : AppColors.textSecondary,
|
||||
fontWeight: _hovered ? FontWeight.w600 : FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 对话框里的次级按钮:描边、透明底。
|
||||
class DialogGhostButton extends StatefulWidget {
|
||||
const DialogGhostButton({super.key, required this.label, required this.onTap});
|
||||
|
||||
/// 按钮文字。
|
||||
final String label;
|
||||
|
||||
/// 点击回调;为 null 表示禁用。
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
State<DialogGhostButton> createState() => _DialogGhostButtonState();
|
||||
}
|
||||
|
||||
class _DialogGhostButtonState extends State<DialogGhostButton> {
|
||||
bool _hovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool enabled = widget.onTap != null;
|
||||
return MouseRegion(
|
||||
cursor: enabled ? SystemMouseCursors.click : SystemMouseCursors.basic,
|
||||
onEnter: (_) => setState(() => _hovered = true),
|
||||
onExit: (_) => setState(() => _hovered = false),
|
||||
child: GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
child: AnimatedContainer(
|
||||
duration: AppSizes.transition,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSizes.cardPaddingHorizontal,
|
||||
vertical: 9,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: _hovered ? AppColors.primaryFaint : Colors.transparent,
|
||||
border: Border.all(color: AppColors.border),
|
||||
borderRadius: BorderRadius.circular(AppSizes.navItemRadius),
|
||||
),
|
||||
child: Text(
|
||||
widget.label,
|
||||
style: AppTextStyles.submitButton.copyWith(
|
||||
color: enabled ? AppColors.textSecondary : AppColors.navArrow,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user