Files
Navius/frontend/lib/features/home/presentation/widgets/link_section_view.dart
T
CNWei 039ea70edf feat: 实现导航页基础功能
- 添加导航条目和分类数据模型
- 实现导航卡片网格展示
- 支持点击卡片打开链接
- 添加主题样式(渐变色、卡片阴影)
- 区分系统默认和自定义条目
- 添加分类统计信息"
2026-09-19 23:17:36 +08:00

94 lines
2.8 KiB
Dart

import 'package:flutter/material.dart';
import '../../../../core/constants/app_breakpoints.dart';
import '../../../../core/constants/app_sizes.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../core/theme/app_text_styles.dart';
import '../../domain/models/link_item.dart';
import '../../domain/models/link_section.dart';
import 'link_card.dart';
/// 一个分组:标题 + 自适应网格。
class LinkSectionView extends StatelessWidget {
const LinkSectionView({
super.key,
required this.section,
required this.onOpen,
});
/// 分组数据。
final LinkSection section;
/// 打开某个站点。
final ValueChanged<LinkItem> onOpen;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_SectionTitle(title: section.title),
const SizedBox(height: AppSizes.sectionTitleGap),
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final int columns = AppBreakpoints.gridColumnsFor(
constraints.maxWidth,
minItemWidth: AppSizes.gridMinItemWidth,
gap: AppSizes.gridGap,
);
final double itemWidth =
(constraints.maxWidth - AppSizes.gridGap * (columns - 1)) /
columns;
return Wrap(
spacing: AppSizes.gridGap,
runSpacing: AppSizes.gridGap,
children: <Widget>[
for (final LinkItem item in section.items)
SizedBox(
width: itemWidth,
child: LinkCard(item: item, onOpen: () => onOpen(item)),
),
],
);
},
),
],
);
}
}
/// 分组标题:左侧一条 4×16 的主色竖条 + 文字(`.section-title::before`)。
class _SectionTitle extends StatelessWidget {
const _SectionTitle({required this.title});
final String title;
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Container(
width: AppSizes.sectionAccentWidth,
height: AppSizes.sectionAccentHeight,
decoration: BoxDecoration(
color: AppColors.primary,
borderRadius: BorderRadius.circular(AppSizes.sectionAccentRadius),
),
),
const SizedBox(width: AppSizes.sectionAccentGap),
// 用 Flexible 包住:窄屏(侧栏展开后主内容只剩一百多像素)时
// 「素材资源 - UI 设计」这类长标题会溢出,这里退化成省略号。
Flexible(
child: Text(
title,
style: AppTextStyles.sectionTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
);
}
}