
搜索功能是App的重要入口,用户可以通过搜索快速找到想要的书籍或笔记。这个页面支持搜索书籍和笔记两种类型,还有最近搜索记录方便用户快速访问。
做这个页面的时候,我把搜索框放在了 AppBar 里,这样用户一进入页面就可以直接输入,不需要额外点击。
状态管理
搜索页面需要管理搜索关键词、搜索类型、搜索结果等状态。
dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../../app/routes/app_routes.dart';
导入必要的依赖包。
状态变量定义
dart
class SearchPage extends StatefulWidget {
const SearchPage({super.key});
@override
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
final _searchController = TextEditingController();
String _searchType = '书籍';
List<Map<String, String>> _results = [];
_searchController 管理搜索框输入,_searchType 是当前搜索类型(书籍或笔记),_results 存储搜索结果。
模拟数据
dart
final List<Map<String, String>> _allBooks = [
{'title': '百年孤独', 'author': '加西亚·马尔克斯', 'type': '书籍'},
{'title': '人类简史', 'author': '尤瓦尔·赫拉利', 'type': '书籍'},
{'title': '三体', 'author': '刘慈欣', 'type': '书籍'},
{'title': '活着', 'author': '余华', 'type': '书籍'},
];
final List<Map<String, String>> _allNotes = [
{'title': '孤独是人类永恒的主题...', 'book': '百年孤独', 'type': '笔记'},
{'title': '认知革命让智人能够讨论虚构的事物...', 'book': '人类简史', 'type': '笔记'},
{'title': '黑暗森林法则...', 'book': '三体', 'type': '笔记'},
];
模拟的书籍和笔记数据,实际项目中应该从数据库读取。
资源释放
dart
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
在页面销毁时释放控制器。
搜索逻辑
dart
void _search(String query) {
if (query.isEmpty) {
setState(() => _results = []);
return;
}
final lowerQuery = query.toLowerCase();
if (_searchType == '书籍') {
setState(() => _results = _allBooks.where((b) =>
b['title']!.toLowerCase().contains(lowerQuery) ||
b['author']!.toLowerCase().contains(lowerQuery)).toList());
} else {
setState(() => _results = _allNotes.where((n) =>
n['title']!.toLowerCase().contains(lowerQuery) ||
n['book']!.toLowerCase().contains(lowerQuery)).toList());
}
}
搜索逻辑很简单,把关键词转小写后在标题和作者(或书名)中查找。实际项目中可能需要更复杂的搜索算法。
页面主体结构
dart
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFFDF8F3),
appBar: AppBar(
backgroundColor: const Color(0xFF5B4636),
foregroundColor: Colors.white,
title: TextField(
controller: _searchController,
autofocus: true,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
hintText: '搜索$_searchType...',
hintStyle: const TextStyle(color: Colors.white54),
border: InputBorder.none,
),
onChanged: _search,
),
搜索框放在 AppBar 的 title 位置,autofocus: true 让页面打开时自动聚焦。输入框文字用白色,提示文字用半透明白色。
清空按钮
dart
actions: [
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_searchController.clear();
_search('');
},
),
],
),
右上角有清空按钮,点击后清空搜索框和搜索结果。
页面内容布局
dart
body: Column(
children: [
_buildSearchTypeSelector(),
Expanded(child: _buildResults()),
],
),
);
}
页面分两部分:搜索类型选择器和搜索结果。
搜索类型选择器
dart
Widget _buildSearchTypeSelector() {
return Container(
padding: EdgeInsets.all(16.w),
child: Row(
children: ['书籍', '笔记'].map((t) => Expanded(
child: GestureDetector(
onTap: () {
setState(() => _searchType = t);
_search(_searchController.text);
},
child: Container(
margin: EdgeInsets.symmetric(horizontal: 4.w),
padding: EdgeInsets.symmetric(vertical: 10.h),
decoration: BoxDecoration(
color: _searchType == t ? const Color(0xFF5B4636) : Colors.white,
borderRadius: BorderRadius.circular(8.r),
),
child: Center(
child: Text(
t,
style: TextStyle(
color: _searchType == t ? Colors.white : Colors.grey[700],
fontWeight: FontWeight.w500,
),
),
),
),
),
)).toList(),
),
);
}
两个按钮切换搜索类型,选中的用主题色背景。切换类型后会重新搜索。
搜索结果展示
dart
Widget _buildResults() {
if (_searchController.text.isEmpty) {
return _buildRecentSearches();
}
if (_results.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.search_off, size: 64.sp, color: Colors.grey[300]),
SizedBox(height: 16.h),
Text('没有找到相关结果', style: TextStyle(color: Colors.grey[500], fontSize: 16.sp)),
],
),
);
}
如果搜索框为空,显示最近搜索;如果有关键词但没结果,显示空状态。
结果列表
dart
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16.w),
itemCount: _results.length,
itemBuilder: (context, index) {
final item = _results[index];
return GestureDetector(
onTap: () => Get.toNamed(
item['type'] == '书籍' ? AppRoutes.bookDetail : AppRoutes.noteDetail,
),
child: Container(
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.all(14.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
),
搜索结果用列表展示,点击跳转到对应的详情页面。
结果项内容
dart
child: Row(
children: [
Container(
width: 44.w,
height: 44.w,
decoration: BoxDecoration(
color: const Color(0xFF5B4636).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(
item['type'] == '书籍' ? Icons.menu_book : Icons.edit_note,
color: const Color(0xFF5B4636),
size: 24.sp,
),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item['title']!,
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14.sp),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 4.h),
Text(
item['type'] == '书籍' ? item['author']! : '来自《${item['book']}》',
style: TextStyle(color: Colors.grey[600], fontSize: 12.sp),
),
],
),
),
],
),
),
);
},
);
}
每个结果项显示图标、标题、副标题。书籍显示作者,笔记显示来源书籍。
最近搜索
dart
Widget _buildRecentSearches() {
final recent = ['百年孤独', '刘慈欣', '认知革命'];
return Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('最近搜索', style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: const Color(0xFF3D2914),
)),
TextButton(onPressed: () {}, child: const Text('清空')),
],
),
SizedBox(height: 12.h),
Wrap(
spacing: 10.w,
runSpacing: 10.h,
children: recent.map((r) => GestureDetector(
onTap: () {
_searchController.text = r;
_search(r);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 8.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.r),
),
child: Text(r, style: TextStyle(fontSize: 13.sp, color: Colors.grey[700])),
),
)).toList(),
),
],
),
);
}
}
最近搜索用胶囊形状的标签展示,点击可以快速搜索。
搜索优化建议
实际项目中可以做这些优化:
防抖:用户输入时不要每次都搜索,等停止输入后再搜索。
高亮:在搜索结果中高亮匹配的关键词。
排序:按相关度排序搜索结果。
小结
搜索功能页面通过简洁的界面让用户可以快速找到想要的内容。搜索框放在 AppBar 里,打开页面就能直接输入。
类型切换让用户可以在书籍和笔记之间切换搜索范围,最近搜索方便用户快速访问历史记录。
下一篇会讲收藏夹页面的实现,展示用户收藏的书籍和笔记。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net