import 'package:flutter/material.dart'; class VSearchInput extends StatelessWidget { /// 占位提示 final String? placeholder; /// 搜索回调 final ValueChanged? onSearch; /// 清空回调 final VoidCallback? onClear; /// 是否可一键清空 final bool? clearable; final TextEditingController? textEditingController; const VSearchInput({ super.key, this.placeholder, this.onSearch, this.clearable, this.onClear, this.textEditingController, }); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (_, c) { final controller = textEditingController ?? TextEditingController(); final radius = c.maxHeight / 2; final border = OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(radius)), borderSide: BorderSide.none, ); return TextField( style: const TextStyle(fontSize: 20), controller: controller, decoration: InputDecoration( enabledBorder: border, focusedBorder: border, fillColor: const Color.fromRGBO(238, 238, 238, 1), filled: true, hintText: placeholder, hintStyle: const TextStyle( fontSize: 20, color: Colors.black54, ), contentPadding: EdgeInsets.symmetric( horizontal: radius * 1.2, vertical: (c.maxHeight - 24) / 2, ), // isDense: true, isCollapsed: false, // prefixIcon: prefixIcon, // suffixIcon: suffixIcon, suffixIcon: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ if (clearable == true) _ClearableSuffixIcon( controller: controller, onTap: onClear, ), IconButton( icon: const Icon(Icons.search, size: 36), onPressed: () { onSearch?.call(controller.text); }, ), const SizedBox(width: 8), ], ), ), onSubmitted: (value) { onSearch?.call(value); }, ); }, ); } } class _ClearableSuffixIcon extends StatefulWidget { final TextEditingController controller; final VoidCallback? onTap; const _ClearableSuffixIcon({required this.controller, this.onTap}); @override State createState() => _ClearableSuffixIconState(); } class _ClearableSuffixIconState extends State<_ClearableSuffixIcon> { late TextEditingController controller; bool isShowIcon = false; @override void initState() { controller = widget.controller; WidgetsBinding.instance.addPostFrameCallback((timeStamp) { if (mounted) { controller.addListener(onTextUpdate); } }); super.initState(); } @override void dispose() { controller.removeListener(onTextUpdate); super.dispose(); } void onTextUpdate() { final isNeedShow = controller.text.isNotEmpty; if (isShowIcon != isNeedShow) { setState(() { isShowIcon = isNeedShow; }); } } @override Widget build(BuildContext context) { if (isShowIcon) { return IconButton( color: Colors.black, icon: Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Colors.black26, ), child: const Icon(Icons.close, size: 22, color: Colors.white), ), onPressed: () { controller.clear(); widget.onTap?.call(); }, ); } else { return const SizedBox(); } } }