## 新增功能 - 实时搜索:支持书名和作者的模糊搜索,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>
364 lines
8.8 KiB
Markdown
364 lines
8.8 KiB
Markdown
# Provider状态管理学习指南
|
||
|
||
## 📚 什么是Provider?
|
||
|
||
Provider是Flutter官方推荐的状态管理解决方案,它提供了一种简单、高效的方式来在应用中共享和传递数据。
|
||
|
||
### 核心概念
|
||
- **Provider**: 数据提供者,封装状态数据
|
||
- **Consumer**: 数据消费者,监听数据变化并重建UI
|
||
- **ChangeNotifier**: 状态变化通知器,当数据改变时通知监听者
|
||
|
||
## 🎯 为什么需要状态管理?
|
||
|
||
### 问题场景
|
||
```dart
|
||
// 问题:多个页面需要共享搜索状态
|
||
class HomePage extends StatelessWidget {
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
String searchText = ''; // 局部状态,无法跨页面共享
|
||
return AppHeader(onSearchPressed: () {
|
||
// 无法将搜索文本传递给其他页面
|
||
});
|
||
}
|
||
}
|
||
|
||
class SearchPage extends StatelessWidget {
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
// 无法获取首页的搜索状态
|
||
return Text('搜索结果');
|
||
}
|
||
}
|
||
```
|
||
|
||
### Provider解决方案
|
||
```dart
|
||
// 解决方案:使用Provider全局共享状态
|
||
class SearchProvider extends ChangeNotifier {
|
||
String _searchText = '';
|
||
List<Book> _searchResults = [];
|
||
|
||
String get searchText => _searchText;
|
||
List<Book> get searchResults => _searchResults;
|
||
|
||
void updateSearchText(String text) {
|
||
_searchText = text;
|
||
notifyListeners(); // 通知所有监听者重建
|
||
}
|
||
|
||
void performSearch() {
|
||
// 执行搜索逻辑
|
||
_searchResults = _searchBooks(_searchText);
|
||
notifyListeners();
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🔧 Provider使用步骤
|
||
|
||
### 步骤1: 添加依赖
|
||
```yaml
|
||
dependencies:
|
||
flutter:
|
||
sdk: flutter
|
||
provider: ^6.0.5 # 添加provider依赖
|
||
```
|
||
|
||
### 步骤2: 创建状态管理类
|
||
```dart
|
||
import 'package:flutter/foundation.dart';
|
||
|
||
class SearchProvider extends ChangeNotifier {
|
||
// 私有状态变量
|
||
String _searchQuery = '';
|
||
bool _isSearching = false;
|
||
List<Book> _results = [];
|
||
|
||
// 公共getter方法
|
||
String get searchQuery => _searchQuery;
|
||
bool get isSearching => _isSearching;
|
||
List<Book> get results => _results;
|
||
|
||
// 状态更新方法
|
||
void updateQuery(String query) {
|
||
_searchQuery = query;
|
||
notifyListeners(); // 通知UI更新
|
||
}
|
||
|
||
void searchBooks(String query) {
|
||
_isSearching = true;
|
||
notifyListeners();
|
||
|
||
// 模拟异步搜索
|
||
Future.delayed(const Duration(milliseconds: 500), () {
|
||
_results = _performSearch(query);
|
||
_isSearching = false;
|
||
notifyListeners();
|
||
});
|
||
}
|
||
|
||
void clearSearch() {
|
||
_searchQuery = '';
|
||
_results.clear();
|
||
notifyListeners();
|
||
}
|
||
|
||
// 私有搜索方法
|
||
List<Book> _performSearch(String query) {
|
||
if (query.isEmpty) return [];
|
||
// 实际搜索逻辑
|
||
return [];
|
||
}
|
||
}
|
||
```
|
||
|
||
### 步骤3: 在应用根部提供Provider
|
||
```dart
|
||
void main() {
|
||
runApp(
|
||
ChangeNotifierProvider(
|
||
create: (context) => SearchProvider(),
|
||
child: const ReadfulApp(),
|
||
),
|
||
);
|
||
}
|
||
```
|
||
|
||
### 步骤4: 在UI中消费Provider数据
|
||
```dart
|
||
// 方法1: 使用Consumer Widget
|
||
Consumer<SearchProvider>(
|
||
builder: (context, searchProvider, child) {
|
||
return Text('搜索词: ${searchProvider.searchQuery}');
|
||
},
|
||
)
|
||
|
||
// 方法2: 使用Provider.of
|
||
TextButton(
|
||
onPressed: () {
|
||
final searchProvider = Provider.of<SearchProvider>(context, listen: false);
|
||
searchProvider.updateQuery('Flutter');
|
||
},
|
||
child: Text('搜索'),
|
||
)
|
||
|
||
// 方法3: 使用Selector(性能优化)
|
||
Selector<SearchProvider, List<Book>>(
|
||
selector: (context, provider) => provider.results,
|
||
builder: (context, results, child) {
|
||
return ListView.builder(
|
||
itemCount: results.length,
|
||
itemBuilder: (context, index) {
|
||
return BookTile(book: results[index]);
|
||
},
|
||
);
|
||
},
|
||
)
|
||
```
|
||
|
||
## 🎨 实际应用:搜索功能实现
|
||
|
||
### 搜索状态管理
|
||
```dart
|
||
class SearchProvider extends ChangeNotifier {
|
||
String _query = '';
|
||
List<Book> _filteredBooks = [];
|
||
List<Book> _allBooks = []; // 数据源
|
||
|
||
// Getters
|
||
String get query => _query;
|
||
List<Book> get filteredBooks => _filteredBooks;
|
||
bool get hasResults => _filteredBooks.isNotEmpty;
|
||
bool get isEmpty => _query.isEmpty;
|
||
|
||
// 初始化数据
|
||
void loadBooks(List<Book> books) {
|
||
_allBooks = books;
|
||
_filteredBooks = books;
|
||
notifyListeners();
|
||
}
|
||
|
||
// 更新搜索查询
|
||
void updateQuery(String newQuery) {
|
||
_query = newQuery.toLowerCase();
|
||
_filterBooks();
|
||
notifyListeners();
|
||
}
|
||
|
||
// 过滤书籍
|
||
void _filterBooks() {
|
||
if (_query.isEmpty) {
|
||
_filteredBooks = List.from(_allBooks);
|
||
} else {
|
||
_filteredBooks = _allBooks.where((book) {
|
||
return book.title.toLowerCase().contains(_query) ||
|
||
book.author.toLowerCase().contains(_query) ||
|
||
book.description.toLowerCase().contains(_query);
|
||
}).toList();
|
||
}
|
||
}
|
||
|
||
// 清空搜索
|
||
void clearSearch() {
|
||
_query = '';
|
||
_filteredBooks = List.from(_allBooks);
|
||
notifyListeners();
|
||
}
|
||
}
|
||
```
|
||
|
||
### 搜索页面UI
|
||
```dart
|
||
class SearchPage extends StatelessWidget {
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
body: Column(
|
||
children: [
|
||
// 搜索输入框
|
||
_buildSearchBar(context),
|
||
|
||
// 搜索结果
|
||
Expanded(
|
||
child: Consumer<SearchProvider>(
|
||
builder: (context, provider, child) {
|
||
if (provider.isEmpty) {
|
||
return _buildEmptyState();
|
||
} else if (provider.hasResults) {
|
||
return _buildResultsList(provider.filteredBooks);
|
||
} else {
|
||
return _buildNoResults();
|
||
}
|
||
},
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildSearchBar(BuildContext context) {
|
||
return Container(
|
||
padding: const EdgeInsets.all(16),
|
||
child: TextField(
|
||
onChanged: (value) {
|
||
Provider.of<SearchProvider>(context, listen: false)
|
||
.updateQuery(value);
|
||
},
|
||
decoration: InputDecoration(
|
||
hintText: '搜索书名、作者或内容...',
|
||
prefixIcon: const Icon(Icons.search),
|
||
suffixIcon: Consumer<SearchProvider>(
|
||
builder: (context, provider, child) {
|
||
return provider.query.isNotEmpty
|
||
? IconButton(
|
||
icon: const Icon(Icons.clear),
|
||
onPressed: () {
|
||
Provider.of<SearchProvider>(context, listen: false)
|
||
.clearSearch();
|
||
},
|
||
)
|
||
: const SizedBox.shrink();
|
||
},
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
## 💡 Provider最佳实践
|
||
|
||
### 1. 性能优化
|
||
```dart
|
||
// ❌ 错误:每次重建都执行复杂计算
|
||
Consumer<MyProvider>(
|
||
builder: (context, provider, child) {
|
||
return HeavyComputationWidget(data: provider.complexData);
|
||
},
|
||
)
|
||
|
||
// ✅ 正确:使用Selector优化
|
||
Selector<MyProvider, ComputedData>(
|
||
selector: (context, provider) => provider.computedData,
|
||
builder: (context, data, child) {
|
||
return HeavyComputationWidget(data: data);
|
||
},
|
||
)
|
||
```
|
||
|
||
### 2. 合理拆分Provider
|
||
```dart
|
||
// ❌ 错误:一个Provider管理太多状态
|
||
class AppProvider extends ChangeNotifier {
|
||
// 搜索状态
|
||
String searchQuery = '';
|
||
List<Book> searchResults = [];
|
||
|
||
// 用户状态
|
||
User? currentUser;
|
||
bool isLoggedIn = false;
|
||
|
||
// 主题状态
|
||
bool isDarkMode = false;
|
||
Color accentColor = Colors.blue;
|
||
}
|
||
|
||
// ✅ 正确:按功能拆分Provider
|
||
class SearchProvider extends ChangeNotifier { /* 搜索相关 */ }
|
||
class UserProvider extends ChangeNotifier { /* 用户相关 */ }
|
||
class ThemeProvider extends ChangeNotifier { /* 主题相关 */ }
|
||
```
|
||
|
||
### 3. 错误处理
|
||
```dart
|
||
class SearchProvider extends ChangeNotifier {
|
||
bool _isLoading = false;
|
||
String? _errorMessage;
|
||
|
||
bool get isLoading => _isLoading;
|
||
String? get errorMessage => _errorMessage;
|
||
|
||
Future<void> searchAsync(String query) async {
|
||
_isLoading = true;
|
||
_errorMessage = null;
|
||
notifyListeners();
|
||
|
||
try {
|
||
final results = await _apiService.search(query);
|
||
_updateResults(results);
|
||
} catch (e) {
|
||
_errorMessage = e.toString();
|
||
} finally {
|
||
_isLoading = false;
|
||
notifyListeners();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🎯 学习总结
|
||
|
||
### 关键概念
|
||
- **ChangeNotifier**: 状态管理基类,提供notifyListeners()方法
|
||
- **Consumer**: 监听Provider变化并自动重建UI
|
||
- **Selector**: 性能优化的Consumer,只监听特定数据变化
|
||
- **Provider.of**: 手动获取Provider实例的方式
|
||
|
||
### 使用场景
|
||
- ✅ **全局状态**: 用户信息、主题设置
|
||
- ✅ **页面间共享**: 搜索状态、购物车数据
|
||
- ✅ **复杂数据**: 表单状态、异步加载状态
|
||
|
||
### 注意事项
|
||
- 避免在build方法中创建Provider实例
|
||
- 合理使用listen参数控制是否需要重建
|
||
- 及时dispose防止内存泄漏
|
||
- 使用Selector优化UI重建性能
|
||
|
||
---
|
||
|
||
*下一步:我们将使用Provider来实现Readful的搜索功能!* |