You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter应用中添加搜索历史功能?

Flutter搜索历史功能实现方案

步骤1:添加本地存储依赖

在pubspec.yaml中引入shared_preferences用于轻量存储搜索历史:

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.2.2

执行flutter pub get完成依赖安装。

步骤2:封装搜索历史操作逻辑

在页面类中添加以下方法,处理历史的增删查操作:

// 存储搜索历史的key
const String _searchHistoryKey = 'search_history';

// 获取本地存储的搜索历史
Future<List<String>> getSearchHistory() async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getStringList(_searchHistoryKey) ?? [];
}

// 添加搜索历史(自动去重,最多保留10条)
Future<void> addSearchHistory(String query) async {
  if (query.trim().isEmpty) return;
  final prefs = await SharedPreferences.getInstance();
  List<String> history = prefs.getStringList(_searchHistoryKey) ?? [];
  
  // 移除重复项,将新查询放在最顶部
  history.remove(query);
  history.insert(0, query);
  
  // 限制历史数量,避免存储过多
  if (history.length > 10) history.removeLast();
  await prefs.setStringList(_searchHistoryKey, history);
}

// 删除单条搜索历史
Future<void> deleteSingleHistory(String query) async {
  final prefs = await SharedPreferences.getInstance();
  List<String> history = prefs.getStringList(_searchHistoryKey) ?? [];
  history.remove(query);
  await prefs.setStringList(_searchHistoryKey, history);
}

// 清空所有搜索历史
Future<void> clearAllHistory() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.remove(_searchHistoryKey);
}

步骤3:改造搜索框与历史UI显示

修改原有搜索框,添加搜索提交逻辑,并在搜索框下方增加历史列表展示:

// 新增状态变量控制历史列表显示
bool _showHistory = true;

@override
Widget build(BuildContext context) {
  return Column(
    children: [
      TextFormField(
        keyboardType: TextInputType.text,
        textInputAction: TextInputAction.search,
        controller: searchcontroller,
        focusNode: fousnode,
        // 输入内容时隐藏历史,清空时显示
        onChanged: (query) {
          setState(() {
            _showHistory = query.isEmpty;
          });
        },
        // 用户提交搜索时添加历史
        onSubmitted: (query) async {
          await addSearchHistory(query);
          setState(() {
            _showHistory = false;
          });
          // 这里可触发搜索逻辑(比如调用下方的_performSearch)
        },
        decoration: InputDecoration(
          hintText: "Search Product",
          border: OutlineInputBorder(
            borderRadius: BorderRadius.all(Radius.circular(11)),
          ),
          // 输入内容时显示清除按钮
          suffixIcon: searchcontroller.text.isNotEmpty
              ? IconButton(
                  icon: Icon(Icons.clear),
                  onPressed: () {
                    searchcontroller.clear();
                    setState(() {
                      _showHistory = true;
                    });
                  },
                )
              : null,
        ),
      ),
      // 搜索历史列表区域
      if (_showHistory)
        FutureBuilder<List<String>>(
          future: getSearchHistory(),
          builder: (context, snapshot) {
            if (!snapshot.hasData || snapshot.data!.isEmpty) {
              return SizedBox(height: 0);
            }
            return Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(
                        '搜索历史',
                        style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
                      ),
                      TextButton(
                        onPressed: () async {
                          await clearAllHistory();
                          setState(() {});
                        },
                        child: Text('清空'),
                      ),
                    ],
                  ),
                ),
                ListView.builder(
                  shrinkWrap: true,
                  physics: NeverScrollableScrollPhysics(),
                  itemCount: snapshot.data!.length,
                  itemBuilder: (context, index) {
                    final history = snapshot.data![index];
                    return ListTile(
                      title: Text(history),
                      trailing: IconButton(
                        icon: Icon(Icons.close, size: 18),
                        onPressed: () async {
                          await deleteSingleHistory(history);
                          setState(() {});
                        },
                      ),
                      onTap: () {
                        // 点击历史填充搜索框并触发搜索
                        searchcontroller.text = history;
                        setState(() {
                          _showHistory = false;
                        });
                        _performSearch(history);
                      },
                    );
                  },
                ),
              ],
            );
          },
        ),
      // 原有搜索结果区域(修正了索引越界问题)
      Expanded(
        child: StreamBuilder<QuerySnapshot>(
          stream: _searchStream, // 替换为下方优化后的流
          builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
            if (!snapshot.hasData) {
              return Center(child: CircularProgressIndicator());
            }
            List<QueryDocumentSnapshot> data = snapshot.data!.docs;
            if (searchcontroller.text.isNotEmpty) {
              data = data.where((doc) => doc["product_name"]
                      .toLowerCase()
                      .contains(searchcontroller.text.toLowerCase()))
                  .toList();
            }
            return GridView.builder(
              itemCount: data.length,
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
              ),
              itemBuilder: (context, index) {
                final doc = data[index];
                return InkWell(
                  onTap: () {
                    Navigator.push(context, MaterialPageRoute(builder: (builder)=>detail(
                      url: doc["url"],
                      productName: doc["product_name"],
                      productPrice: doc["product_price"],
                    )));
                  },
                  child: Container(
                    padding: EdgeInsets.all(8),
                    child: Column(
                      children: [
                        CircleAvatar(
                          backgroundImage: NetworkImage(doc["url"]),
                          radius: 30,
                        ),
                        SizedBox(height: 8),
                        Text(doc["product_name"]),
                      ],
                    ),
                  ),
                );
              },
            );
          },
        ),
      ),
    ],
  );
}

步骤4:优化搜索逻辑(可选但推荐)

原来的onChanged中Firestore查询未实际使用,可改为动态更新查询流,提升大数据量下的性能:

Stream<QuerySnapshot>? _searchStream;

@override
void initState() {
  super.initState();
  // 初始加载全部商品
  _searchStream = FirebaseFirestore.instance.collectionGroup("Add_product").snapshots();
}

// 执行搜索的方法
void _performSearch(String query) {
  if (query.isEmpty) {
    _searchStream = FirebaseFirestore.instance.collectionGroup("Add_product").snapshots();
  } else {
    final lowerQuery = query.toLowerCase();
    _searchStream = FirebaseFirestore.instance
        .collectionGroup("Add_product")
        .where("product_name", isGreaterThanOrEqualTo: lowerQuery)
        .where("product_name", isLessThan: lowerQuery + 'z')
        .snapshots();
  }
  setState(() {});
}

内容的提问来源于stack exchange,提问作者Muhammad Zohaib

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 11:35:21