Files
ai_english/client/lib/features/reading/widgets/reading_search_bar.dart
2025-11-17 14:09:17 +08:00

303 lines
8.5 KiB
Dart

import 'package:flutter/material.dart';
/// 阅读搜索栏组件
class ReadingSearchBar extends StatefulWidget {
final Function(String) onSearch;
final String? initialQuery;
final String hintText;
const ReadingSearchBar({
super.key,
required this.onSearch,
this.initialQuery,
this.hintText = '搜索文章标题、内容或标签...',
});
@override
State<ReadingSearchBar> createState() => _ReadingSearchBarState();
}
class _ReadingSearchBarState extends State<ReadingSearchBar> {
late TextEditingController _controller;
final FocusNode _focusNode = FocusNode();
@override
void initState() {
super.initState();
_controller = TextEditingController(text: widget.initialQuery);
// 自动聚焦
WidgetsBinding.instance.addPostFrameCallback((_) {
_focusNode.requestFocus();
});
}
@override
void dispose() {
_controller.dispose();
_focusNode.dispose();
super.dispose();
}
void _performSearch() {
final query = _controller.text.trim();
if (query.isNotEmpty) {
widget.onSearch(query);
}
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
const Text(
'搜索文章',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
const SizedBox(height: 16),
// 搜索输入框
TextField(
controller: _controller,
focusNode: _focusNode,
decoration: InputDecoration(
hintText: widget.hintText,
hintStyle: TextStyle(color: Colors.grey[500]),
prefixIcon: const Icon(
Icons.search,
color: Color(0xFF2196F3),
),
suffixIcon: _controller.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_controller.clear();
setState(() {});
},
)
: null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[300]!),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(
color: Color(0xFF2196F3),
width: 2,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[300]!),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
),
onChanged: (value) {
setState(() {});
},
onSubmitted: (_) => _performSearch(),
textInputAction: TextInputAction.search,
),
const SizedBox(height: 16),
// 搜索建议
_buildSearchSuggestions(),
const SizedBox(height: 20),
// 操作按钮
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
'取消',
style: TextStyle(color: Colors.grey[600]),
),
),
const SizedBox(width: 12),
ElevatedButton(
onPressed: _controller.text.trim().isNotEmpty
? _performSearch
: null,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2196F3),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
),
child: const Text('搜索'),
),
],
),
],
),
),
);
}
/// 构建搜索建议
Widget _buildSearchSuggestions() {
final suggestions = [
'四级阅读',
'六级阅读',
'托福阅读',
'雅思阅读',
'商务英语',
'日常对话',
'科技文章',
'新闻报道',
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'热门搜索',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: suggestions.map((suggestion) {
return GestureDetector(
onTap: () {
_controller.text = suggestion;
setState(() {});
_performSearch();
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.grey[300]!),
),
child: Text(
suggestion,
style: TextStyle(
fontSize: 12,
color: Colors.grey[700],
),
),
),
);
}).toList(),
),
],
);
}
}
/// 简化的搜索栏组件(用于嵌入页面)
class ReadingSearchField extends StatefulWidget {
final Function(String) onSearch;
final Function()? onTap;
final String? initialQuery;
final bool enabled;
const ReadingSearchField({
super.key,
required this.onSearch,
this.onTap,
this.initialQuery,
this.enabled = true,
});
@override
State<ReadingSearchField> createState() => _ReadingSearchFieldState();
}
class _ReadingSearchFieldState extends State<ReadingSearchField> {
late TextEditingController _controller;
@override
void initState() {
super.initState();
_controller = TextEditingController(text: widget.initialQuery);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(16),
child: TextField(
controller: _controller,
enabled: widget.enabled,
onTap: widget.onTap,
decoration: InputDecoration(
hintText: '搜索文章...',
hintStyle: TextStyle(color: Colors.grey[500]),
prefixIcon: const Icon(
Icons.search,
color: Color(0xFF2196F3),
),
suffixIcon: _controller.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_controller.clear();
widget.onSearch('');
setState(() {});
},
)
: null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
onChanged: (value) {
setState(() {});
},
onSubmitted: widget.onSearch,
textInputAction: TextInputAction.search,
),
);
}
}