readful/lib/components/app_header.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

159 lines
4.5 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 '../pages/search_page.dart';
/// 应用顶部导航组件
///
/// 可复用的顶部导航栏组件,集成在搜索栏内的导入按钮。
/// 提供统一的搜索和文件导入功能,支持主题自适应。
class AppHeader extends StatelessWidget {
/// 标题文本(可选),某些页面可以显示标题而非搜索栏
final String? title;
/// 搜索按钮点击回调函数
final VoidCallback? onSearchPressed;
/// 导入按钮点击回调函数
final VoidCallback? onImportPressed;
/// 是否显示搜索栏默认值为true
final bool showSearchBar;
/// 搜索栏占位符文本
final String searchHint;
const AppHeader({
super.key,
this.title,
this.onSearchPressed,
this.onImportPressed,
this.showSearchBar = true,
this.searchHint = '搜索书名或内容...',
});
/// 构建组件的UI结构
@override
Widget build(BuildContext context) {
return Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border(
bottom: BorderSide(
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
width: 1,
),
),
),
child: Row(
children: [
// 标题区域(条件渲染)
if (title != null) ...[
Text(
title!,
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 16),
],
// 搜索栏区域(占据剩余空间)
if (showSearchBar) ...[
Expanded(
child: _buildSearchBar(context),
),
]
],
),
);
}
/// 构建搜索栏组件,包含搜索图标、占位符文本和导入按钮
Widget _buildSearchBar(BuildContext context) {
return Container(
height: 44,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceVariant,
borderRadius: BorderRadius.circular(22),
border: Border.all(
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
),
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(22),
onTap: onSearchPressed ?? () => _navigateToSearchPage(context),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Icon(
Icons.search,
size: 24,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
const SizedBox(width: 8),
Expanded(
child: Text(
searchHint,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context)
.colorScheme
.onSurfaceVariant
.withOpacity(0.6),
),
),
),
const SizedBox(width: 8),
_buildImportButton(context),
],
),
),
),
),
);
}
/// 构建导入按钮组件
Widget _buildImportButton(BuildContext context) {
return Container(
height: 32,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(16),
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: onImportPressed,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'导入',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
],
),
),
),
),
);
}
}
// 添加这个新方法
void _navigateToSearchPage(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const SearchPage(),
),
);
}