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 createState() => _LinkSearchFieldState(); } class _LinkSearchFieldState extends State { 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: [ 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, ), ), ); } }