readful/lib/pages/search_page.dart
ddshi feb01c81ca feat: 完成搜索功能开发和Provider状态管理集成
## 新增功能
- 实时搜索:支持书名和作者的模糊搜索,300ms防抖优化
- Provider状态管理:使用ChangeNotifier模式管理搜索状态
- 搜索页面:完整的搜索UI,包括空状态、搜索中、无结果和结果列表
- 智能交互:一键清空搜索、焦点管理、状态同步

## 技术实现
- SearchProvider:防抖搜索、状态管理、多字段匹配
- SearchPage:StatefulWidget管理、控制器协调、生命周期优化
- 状态同步:TextEditingController与Provider协调,避免循环更新
- 用户体验:即时反馈、智能清空、页面状态重置

## 代码质量
- Flutter分析零警告
- 完整的代码注释和文档
- 内存安全:正确的资源清理
- 性能优化:防抖机制和精确UI重建

## 文档完善
- Provider状态管理学习指南
- 搜索功能开发实战总结
- 顶部导航组件开发总结

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 13:49:56 +08:00

210 lines
5.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/search_provider.dart';
import '../models/book.dart';
/// 搜索页面
///
/// 提供实时搜索功能,支持书名和作者的模糊搜索
/// 使用StatefulWidget管理输入框状态和焦点控制
class SearchPage extends StatefulWidget {
const SearchPage({super.key});
@override
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
late TextEditingController _textController;
late FocusNode _focusNode;
@override
void initState() {
super.initState();
_textController = TextEditingController();
_focusNode = FocusNode();
// 页面初始化时重置搜索状态
WidgetsBinding.instance.addPostFrameCallback((_) {
final searchProvider = Provider.of<SearchProvider>(context, listen: false);
searchProvider.clearSearch();
});
// 监听文本变化同步到Provider
_textController.addListener(_onTextChanged);
}
@override
void dispose() {
_textController.removeListener(_onTextChanged);
_textController.dispose();
_focusNode.dispose();
super.dispose();
}
/// 文本变化处理
///
/// 同步TextField文本到SearchProvider避免循环更新
void _onTextChanged() {
final currentQuery = Provider.of<SearchProvider>(context, listen: false).searchQuery;
if (_textController.text != currentQuery) {
Provider.of<SearchProvider>(context, listen: false).updateQuery(_textController.text);
}
}
/// 清空搜索
///
/// 同时清空Provider状态、TextField文本并重新获取焦点
void _clearSearch() {
final searchProvider = Provider.of<SearchProvider>(context, listen: false);
searchProvider.clearSearch();
_textController.clear();
_focusNode.requestFocus();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: _buildSearchInput(context),
),
),
Expanded(
child: _buildSearchResults(context),
),
],
),
);
}
/// 构建搜索输入框
///
/// 使用TextEditingController控制文本状态
/// 使用Consumer响应搜索状态变化动态显示清空按钮
Widget _buildSearchInput(BuildContext context) {
return Consumer<SearchProvider>(
builder: (context, searchProvider, child) {
return TextField(
controller: _textController,
focusNode: _focusNode,
autofocus: true,
decoration: InputDecoration(
hintText: '输入书名、作者或关键词...',
prefixIcon: const Icon(Icons.search),
suffixIcon: searchProvider.hasQuery
? IconButton(
icon: const Icon(Icons.clear),
onPressed: _clearSearch,
)
: null,
),
);
},
);
}
/// 构建搜索结果区域
///
/// 根据搜索状态显示不同的UI
/// - 搜索中:显示加载指示器
/// - 无搜索词:显示空状态提示
/// - 无结果:显示未找到提示
/// - 有结果:显示书籍列表
Widget _buildSearchResults(BuildContext context) {
return Consumer<SearchProvider>(
builder: (context, searchProvider, child) {
if (searchProvider.isSearching) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (!searchProvider.hasQuery) {
return _buildEmptyState();
}
if (!searchProvider.hasResults) {
return _buildNoResults();
}
return _buildBookList(searchProvider.searchResults);
},
);
}
/// 构建空状态界面
///
/// 当用户未输入搜索词时显示的引导界面
Widget _buildEmptyState() {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.search, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text(
'输入关键词开始搜索',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
);
}
/// 构建无结果界面
///
/// 当搜索没有找到匹配书籍时显示的提示界面
Widget _buildNoResults() {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.search_off, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text(
'没有找到相关书籍',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
);
}
/// 构建书籍列表
///
/// [books] 搜索结果书籍列表
/// 使用ListView.builder实现高效的滚动性能
Widget _buildBookList(List<Book> books) {
return ListView.builder(
itemCount: books.length,
cacheExtent: 500,
itemBuilder: (context, index) {
final book = books[index];
return ListTile(
leading: Container(
width: 50,
height: 70,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(8),
),
child: const Icon(Icons.book, color: Colors.grey),
),
title: Text(book.title),
subtitle: Text(book.author ?? '未知作者'),
trailing: const Icon(Icons.chevron_right),
onTap: () {
// TODO: 实现跳转到书籍详情页面的功能
print('点击了书籍: ${book.title}');
},
);
},
);
}
}