readful/lib/components/app_header.dart
ddshi f62a43823c feat: 完成顶部导航组件开发和系统UI适配
- 创建可复用的AppHeader组件,集成搜索栏和导入按钮
- 实现SafeArea系统UI避让,解决状态栏重叠问题
- 优化代码注释,采用简洁专业的文档风格
- 完善Flutter布局系统学习文档,涵盖Container、Row、InkWell等组件
- 更新项目状态和开发进度文档

技术要点:
- Material Design主题系统集成
- 条件渲染和组件参数化设计
- InkWell水波纹点击效果
- Flutter代码质量优化(零警告)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 19:30:51 +08:00

157 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';
/// 应用顶部导航组件
///
/// 可复用的顶部导航栏组件,集成在搜索栏内的导入按钮。
/// 提供统一的搜索和文件导入功能,支持主题自适应。
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,
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(
width: 40,
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: 12),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.add,
size: 18,
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
const SizedBox(width: 4),
Text(
'导入',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
],
),
),
),
),
);
}
}