Flutter for OpenHarmony 看书管理记录App实战:搜索功能实现

搜索功能是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

相关推荐
zilikew2 小时前
Flutter框架跨平台鸿蒙开发——书籍推荐APP的开发流程
flutter·华为·harmonyos·鸿蒙
zilikew2 小时前
Flutter框架跨平台鸿蒙开发——桌面宠物APP的开发流程
学习·flutter·harmonyos·鸿蒙·宠物
yongui478342 小时前
基于MATLAB的NALM锁模光纤激光器仿真实现
开发语言·matlab
ITUnicorn3 小时前
Flutter调用HarmonyOS6原生功能:实现智感握持
flutter·华为·harmonyos·harmonyos6·智感握持
-To be number.wan3 小时前
Python数据分析:numpy数值计算基础
开发语言·python·数据分析
HIT_Weston3 小时前
107、【Ubuntu】【Hugo】搭建私人博客:模糊搜索 Fuse.js(三)
linux·javascript·ubuntu
2601_949575863 小时前
Flutter for OpenHarmony二手物品置换App实战 - 商品卡片实现
android·flutter
Cx330❀4 小时前
【优选算法必刷100题】第038题(位运算):消失的两个数字
开发语言·c++·算法·leetcode·面试
Loo国昌4 小时前
深入理解 FastAPI:Python高性能API框架的完整指南
开发语言·人工智能·后端·python·langchain·fastapi