feat: 实现导航页基础功能
- 添加导航条目和分类数据模型 - 实现导航卡片网格展示 - 支持点击卡片打开链接 - 添加主题样式(渐变色、卡片阴影) - 区分系统默认和自定义条目 - 添加分类统计信息"
This commit is contained in:
@@ -5,12 +5,6 @@ import '../../../../core/theme/app_colors.dart';
|
||||
import '../../../../core/theme/app_text_styles.dart';
|
||||
|
||||
/// 搜索框。
|
||||
///
|
||||
/// 对应 `index3.html` 的 `.search-box`:固定 44px 高、白色底、1px 描边、
|
||||
/// 12px 圆角,聚焦时边框变主色并外扩 3px 的浅色光晕。
|
||||
///
|
||||
/// 用 [FocusNode] 而不是 `InputDecoration` 的 `focusedBorder`:
|
||||
/// 那条光晕是「向外扩散」的矩形阴影,靠边框宽度画不出来。
|
||||
class LinkSearchField extends StatefulWidget {
|
||||
const LinkSearchField({
|
||||
super.key,
|
||||
@@ -18,10 +12,7 @@ class LinkSearchField extends StatefulWidget {
|
||||
required this.hintText,
|
||||
});
|
||||
|
||||
/// 文本控制器。
|
||||
final TextEditingController controller;
|
||||
|
||||
/// 占位文案。
|
||||
final String hintText;
|
||||
|
||||
@override
|
||||
@@ -54,42 +45,52 @@ class _LinkSearchFieldState extends State<LinkSearchField> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedContainer(
|
||||
duration: AppSizes.transition,
|
||||
// `.search-box { transition: border-color .2s, box-shadow .2s }`。
|
||||
curve: Curves.ease,
|
||||
height: AppSizes.searchHeight,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSizes.searchPaddingHorizontal,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surface,
|
||||
border: Border.all(
|
||||
color: _focused ? AppColors.primary : AppColors.border,
|
||||
return GestureDetector(
|
||||
// 输入框只占中间一行的高度,点上下留白也让它聚焦——
|
||||
// 原型里整个 44px 高的盒子就是一个 <input>。
|
||||
onTap: _focusNode.requestFocus,
|
||||
child: AnimatedContainer(
|
||||
duration: AppSizes.transition,
|
||||
curve: Curves.ease,
|
||||
height: AppSizes.searchHeight,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSizes.searchPaddingHorizontal,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppSizes.searchRadius),
|
||||
// 原型 `.search-box:focus` 的 box-shadow 会整体覆盖掉默认阴影,
|
||||
// 聚焦时只剩这一圈光晕,不是「默认阴影 + 光晕」。
|
||||
boxShadow: <BoxShadow>[
|
||||
if (_focused)
|
||||
const BoxShadow(color: AppColors.focusRing, spreadRadius: 3)
|
||||
else
|
||||
...AppColors.cardShadow,
|
||||
],
|
||||
),
|
||||
child: TextField(
|
||||
controller: widget.controller,
|
||||
focusNode: _focusNode,
|
||||
style: AppTextStyles.searchInput,
|
||||
cursorColor: AppColors.primary,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
textInputAction: TextInputAction.search,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintText: widget.hintText,
|
||||
hintStyle: AppTextStyles.searchHint,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surface,
|
||||
border: Border.all(
|
||||
color: _focused ? AppColors.primary : AppColors.border,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppSizes.searchRadius),
|
||||
boxShadow: <BoxShadow>[
|
||||
if (_focused)
|
||||
const BoxShadow(color: AppColors.focusRing, spreadRadius: 3)
|
||||
else
|
||||
...AppColors.cardShadow,
|
||||
],
|
||||
),
|
||||
// 垂直居中不要靠固定的 contentPadding。
|
||||
// 那个写法只在「字体行高 + 上下 padding 正好等于内容区高度」时成立,
|
||||
// 换一个字体(浏览器里的系统字体行高通常更高)就会被挤偏。
|
||||
// 这里让 TextField 按自身行高确定高度,再用 Align 把这一行放进
|
||||
// 44px 的内容区里居中——和浏览器渲染单行 <input> 的方式一致,
|
||||
// 与字体行高无关。
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TextField(
|
||||
controller: widget.controller,
|
||||
focusNode: _focusNode,
|
||||
style: AppTextStyles.searchInput,
|
||||
cursorColor: AppColors.primary,
|
||||
textInputAction: TextInputAction.search,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintText: widget.hintText,
|
||||
hintStyle: AppTextStyles.searchHint,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user