import 'package:flutter/material.dart'; import '../../../../core/constants/app_sizes.dart'; import '../../../../core/theme/app_colors.dart'; import '../../../../core/theme/app_text_styles.dart'; /// 搜索框。 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 GestureDetector( // 输入框只占中间一行的高度,点上下留白也让它聚焦—— // 原型里整个 44px 高的盒子就是一个 。 onTap: _focusNode.requestFocus, child: AnimatedContainer( duration: AppSizes.transition, 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), boxShadow: [ if (_focused) const BoxShadow(color: AppColors.focusRing, spreadRadius: 3) else ...AppColors.cardShadow, ], ), // 垂直居中不要靠固定的 contentPadding。 // 那个写法只在「字体行高 + 上下 padding 正好等于内容区高度」时成立, // 换一个字体(浏览器里的系统字体行高通常更高)就会被挤偏。 // 这里让 TextField 按自身行高确定高度,再用 Align 把这一行放进 // 44px 的内容区里居中——和浏览器渲染单行 的方式一致, // 与字体行高无关。 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, ), ), ), ), ); } }