Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
// lib/widgets/search_box.dart | |
import 'package:flutter/material.dart'; | |
import '../theme/colors.dart'; | |
class SearchBox extends StatefulWidget { | |
final TextEditingController controller; | |
final bool isSearching; | |
final bool enabled; | |
final Function(String) onSearch; | |
final VoidCallback onCancel; | |
const SearchBox({ | |
super.key, | |
required this.controller, | |
required this.isSearching, | |
required this.enabled, | |
required this.onSearch, | |
required this.onCancel, | |
}); | |
State<SearchBox> createState() => _SearchBoxState(); | |
} | |
class _SearchBoxState extends State<SearchBox> { | |
final _focusNode = FocusNode(); | |
bool _isComposing = false; | |
void initState() { | |
super.initState(); | |
widget.controller.addListener(_onSearchTextChanged); | |
} | |
void _onSearchTextChanged() { | |
if (_focusNode.hasFocus) { | |
setState(() { | |
_isComposing = widget.controller.text.isNotEmpty; | |
}); | |
} | |
} | |
void _handleSubmitted(String value) { | |
final trimmedValue = value.trim(); | |
if (trimmedValue.isNotEmpty) { | |
FocusScope.of(context).unfocus(); | |
widget.onSearch(trimmedValue); | |
// Reset _isComposing to ensure the field can be edited again | |
setState(() { | |
_isComposing = false; | |
}); | |
} | |
} | |
Widget build(BuildContext context) { | |
return Material( | |
color: Colors.transparent, | |
child: TextFormField( | |
controller: widget.controller, | |
focusNode: _focusNode, | |
style: const TextStyle(color: AiTubeColors.onBackground), | |
enabled: widget.enabled, | |
textInputAction: TextInputAction.search, | |
onFieldSubmitted: _handleSubmitted, | |
onTapOutside: (_) { | |
FocusScope.of(context).unfocus(); | |
}, | |
decoration: InputDecoration( | |
hintText: 'Describe a video you want to generate...', | |
hintStyle: const TextStyle(color: AiTubeColors.onSurfaceVariant), | |
filled: true, | |
fillColor: AiTubeColors.surface, | |
border: OutlineInputBorder( | |
borderRadius: BorderRadius.circular(24), | |
borderSide: BorderSide.none, | |
), | |
contentPadding: const EdgeInsets.symmetric( | |
horizontal: 16, | |
vertical: 12, | |
), | |
suffixIcon: widget.isSearching | |
? IconButton( | |
icon: const SizedBox( | |
width: 20, | |
height: 20, | |
child: CircularProgressIndicator(strokeWidth: 2), | |
), | |
onPressed: widget.onCancel, | |
) | |
: IconButton( | |
icon: const Icon( | |
Icons.search, | |
color: AiTubeColors.onSurfaceVariant, | |
), | |
onPressed: () => _handleSubmitted(widget.controller.text), | |
), | |
), | |
), | |
); | |
} | |
void dispose() { | |
_focusNode.dispose(); | |
widget.controller.removeListener(_onSearchTextChanged); | |
super.dispose(); | |
} | |
} |