- 添加导航条目和分类数据模型 - 实现导航卡片网格展示 - 支持点击卡片打开链接 - 添加主题样式(渐变色、卡片阴影) - 区分系统默认和自定义条目 - 添加分类统计信息"
98 lines
2.8 KiB
Dart
98 lines
2.8 KiB
Dart
import 'package:flutter/material.dart';
|
||
|
||
import '../../../../core/constants/app_sizes.dart';
|
||
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,
|
||
required this.controller,
|
||
required this.hintText,
|
||
});
|
||
|
||
/// 文本控制器。
|
||
final TextEditingController controller;
|
||
|
||
/// 占位文案。
|
||
final String hintText;
|
||
|
||
@override
|
||
State<LinkSearchField> createState() => _LinkSearchFieldState();
|
||
}
|
||
|
||
class _LinkSearchFieldState extends State<LinkSearchField> {
|
||
final FocusNode _focusNode = FocusNode();
|
||
bool _focused = false;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_focusNode.addListener(_handleFocusChange);
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_focusNode
|
||
..removeListener(_handleFocusChange)
|
||
..dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
void _handleFocusChange() {
|
||
if (_focusNode.hasFocus != _focused) {
|
||
setState(() => _focused = _focusNode.hasFocus);
|
||
}
|
||
}
|
||
|
||
@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,
|
||
),
|
||
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,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|