当前位置: 首页 > news >正文

Flutter_学习记录_本地存储数据

背景

项目中经常会有本地存储数据的需求,例如,本地存储搜索页面“搜索数据”, 如下图:
在这里插入图片描述

思路

  1. 进入页面时,加载本地数据
  2. 点击搜索时,将搜索数据存储到本地:先读取本地数据,如果有数据,就追加数据,并保存;如果本地没数据,就直接保存。
  3. 点击“清空所有历史记录”时,删除本地的搜索记录的数据
  4. 长按某一条数据时,展示“确认删除”弹框,删除某条数据。

代码实现

  1. 用三方库shared_preferences来实现,在项目中引入shared_preferences库:
    在这里插入图片描述

  2. 创建一个StorageService类,来封装对应的方法,代码如下:

import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';

class StorageService {
  static final _searchKey = "SearchHistory";

  // 获取搜索的历史记录
  static Future<List<String>> getSearchHistoryString() async {
    final shareInstance = await  SharedPreferences.getInstance();
    try {
      final historyString = shareInstance.getString(_searchKey);
      print("搜索历史记录 = $historyString");
      if (historyString != null) {
        List historyList = json.decode(historyString);
        return historyList.map((value){
          return "$value";
        }).toList();
      } else {
        print("搜索历史记录 没有数据");
      return [];
      }
    } catch (e) {
      print("搜索历史记录 没有数据");
      return [];
    }
  }

  /* 
  存储搜索历史记录
    如果有数据,就追加数据,然后存储;
    如果没有数据,就新建List,添加数据,然后存储;
  */
  static Future<void> setSearchHistoryString(String keyword) async {
    final shareInstance = await SharedPreferences.getInstance();
    try {
      final historyString = shareInstance.getString(_searchKey);
      if (historyString != null) {
        List historyList = json.decode(historyString);
        historyList.add(keyword);

        final dataString = json.encode(historyList);
        print("历史记录 = $dataString");
        shareInstance.setString(_searchKey, dataString);
      } else {
        List historyList = [keyword];
        final dataString = json.encode(historyList);
        print("历史记录 = $dataString");
        shareInstance.setString(_searchKey, dataString);
      }
    } catch (e) {
      List historyList = [keyword];
      final dataString = json.encode(historyList);
      print("历史记录 = $dataString");
      shareInstance.setString(_searchKey, dataString);
    }
  }

  // 删除某条历史记录
  static Future<List<String>> removeSearchKeyword(String keyword) async {
    final shareInstance = await SharedPreferences.getInstance();
    try {
      final historyString = shareInstance.getString(_searchKey);
      if (historyString != null) {
        List historyList = json.decode(historyString);
        List<String> historyStringList = historyList.map((value){
          return "$value";
        }).toList();
        historyStringList.remove(keyword);

        final dataString = json.encode(historyStringList);
        print("历史记录 = $dataString");
        shareInstance.setString(_searchKey, dataString);

        return historyStringList;
      } else {
      	return [];
      }
    } catch (e) {
      return [];
    }
  }

  // 清空搜索历史记录
  static Future<void> clearSearchHistory() async {
    final shareInstance = await SharedPreferences.getInstance();
    try {
      shareInstance.remove(_searchKey);
    } catch (e) {
      print(e);
    }
  }
}
  1. 创建删除的提示框:
import 'dart:ffi';
import 'package:flutter/material.dart';

class ShowAlertWidget {
   /// 显示一个带有标题和内容的对话框
  /// 
  /// 参数:
  /// - `context`: 当前上下文
  /// - `title`: 对话框标题
  /// - `content`: 对话框内容
  /// 
  /// 返回值:
  /// - 0: 用户点击了 "取消"
  /// - 1: 用户点击了 "确定"
  static Future<int> showAlert(BuildContext context, String title, String content) async {
      final value = await showDialog<int>(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            title: Text(title),
            content: Text(content),
            actions: [
              TextButton(
                onPressed: () {
                  Navigator.pop(context, 0); // 返回值为 0,表示取消
                },
                child: Text("取消"),
              ),
              TextButton(
                onPressed: () {
                  Navigator.pop(context, 1); // 返回值为 1,表示确定
                },
                child: Text("确定"),
              ),
            ],
          );
        },
      ) ?? 0; // 如果用户直接关闭对话框,则默认返回 0(取消)
    print("value = $value");
    return value;
  }
}
  1. 使用的地方:

//

  • [ 进入页面的时候,加载数据]


  void initState() {
    super.initState();

	// 进入页面的时候,加载数据
    StorageService.getSearchHistoryString().then((list){
      setState(() {
        _historySearchData = list;
      });
    });
  }
  • 点击搜索时,将搜索数据存储到本地
      onTap: (){
        if (_searchTextValue.isEmpty) {
          return;
        }
        // 存储数据
        StorageService.setSearchHistoryString(_searchTextValue);
        // 进入商品分类后,点击返回,可以跳过搜索页,直接返回到根视图
        Navigator.pushReplacementNamed(context, "/productList", arguments: {
          "keyword": _searchTextValue
        });
      }
  • 点击“清空所有历史记录”时,删除本地的搜索记录的数据
		onTap: (){
          StorageService.clearSearchHistory();
          setState(() {
            _historySearchData = [];
          });
        }
  • 长按某一条数据时,展示“确认删除”弹框,删除某条数据
				onLongPress: (){
                  ShowAlertWidget.showAlert(context, "删除提醒", "确认要删除这条数据吗").then((value) {
                    if (value == 1) {
                      StorageService.removeSearchKeyword(keyword).then((list){
                        setState(() {
                          _historySearchData = list;
                        });
                      });
                    }
                  });
                }

完整代码案例

import 'package:fangjd/CommonWidget/ShowAlertWidget.dart';
import 'package:fangjd/Services/ScreenAdapter.dart';
import 'package:fangjd/Services/StorageService.dart';
import 'package:flutter/material.dart';


class SearchPage extends StatefulWidget {
  const SearchPage({super.key});

  
  State<SearchPage> createState() => _SearchPageState();
}

class _SearchPageState extends State<SearchPage> {

  final List<String> _hotSearchData = ["超级秒杀", "办公电脑", "儿童汽车", "唇彩唇蜜", "哪吒2"];
  List<String> _historySearchData = ["贝亲奶瓶", "纸尿裤", "婴儿衣架", "包被"];
  String _searchTextValue = "";

  // 设置搜索条
  Widget _searchInputWidget() {
    return Container(
      padding: EdgeInsets.only(left: Screenadapter.width(20)),
      height: Screenadapter.height(80),
      decoration: BoxDecoration(
        color: Color.fromRGBO(233, 233, 233, 0.9),
        borderRadius: BorderRadius.circular(Screenadapter.height(40)),
      ),
      child: TextField(
          autofocus: false,
          decoration: InputDecoration(
            // 设置内容的间距
            contentPadding: EdgeInsets.all(10),
            // 设置图标
            prefixIcon: Icon(Icons.search, size: 18, color: Colors.black45),
            // 设置前置图标的约束
            prefixIconConstraints: BoxConstraints(
                //添加内部图标之后,图标和文字会有间距,实现这个方法,不用写任何参数即可解决
                minWidth: 30.0
            ),
            // 设置提示文字
            hintText: "请输入你想搜索的商品",
            // 设置提示文字的样式
            hintStyle: TextStyle(fontSize: 16, color: Colors.black45),
            // 去掉输入框底部的线
            border: OutlineInputBorder(
              borderSide: BorderSide.none,
            ),
          ),
          onChanged: (value) {
            _searchTextValue = value;
          },
        ),
    );
  }

  // 设置搜索的按钮
  Widget _searchItemWiget() {
    return InkWell(
      onTap: (){
        if (_searchTextValue.isEmpty) {
          return;
        }
        // 存储数据
        StorageService.setSearchHistoryString(_searchTextValue);
        // 进入商品分类后,点击返回,可以跳过搜索页,直接返回到根视图
        Navigator.pushReplacementNamed(context, "/productList", arguments: {
          "keyword": _searchTextValue
        });
      },
      child: Container(
        padding: EdgeInsets.only(right: Screenadapter.width(30)),
        height: Screenadapter.height(80),
        width: Screenadapter.width(100),
        child: Center(
          child: Text("搜索", style: TextStyle(fontSize: 18)),
        ),
      ),
    );
  }

  // 热搜的视图
  Widget _hotSearchWidget() {
    return SizedBox(
        height: Screenadapter.height(180),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                SizedBox(width: 10),
                Text("热搜", style: TextStyle(fontSize: 18, fontWeight: FontWeight.w800))
              ],
            ),
            SizedBox(height: 10),
            SingleChildScrollView(
              padding: EdgeInsets.only(left: 10),
              scrollDirection: Axis.horizontal,
              child: Row(
                children: _hotSearchData.map((value){
                  return InkWell(
                    onTap: (){
                      // 进入商品分类后,点击返回,可以跳过搜索页,直接返回到根视图
                      Navigator.pushReplacementNamed(context, "/productList", arguments: {
                        "keyword": value
                      });
                    },
                    child: Container(
                      height: 30,
                      padding: EdgeInsets.only(left: 10, right: 10),
                      margin: EdgeInsets.only(right: 10),
                      color: Colors.grey[200],
                      child: Center(child: Text(value)),
                    ),
                  );
                }).toList(),
              ),
            ),
            SizedBox(height: 10),
            Container(
              height: 15,
              color: Colors.grey[200],
            )
          ],
        ),
    );
  }

  // 历史搜索
  Widget _historSearchTitleWidget() {
    return SizedBox(
      height: 50,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Expanded(
            child:  Container(
              padding: EdgeInsets.only(top: 14, bottom: 14, left: 10),
              child: Text("历史搜索", style: TextStyle(fontSize: 18, fontWeight: FontWeight.w800), textAlign: TextAlign.start),
            )),
          Divider(height: 1, color: Colors.grey[200])
        ],
      ),
    );
  }

  // 搜索页面的内容视图
  Widget _searchHotAndHistroyWidget() {
    return ListView.builder(
      itemBuilder: (context, index){
        if (index == 0) {
          return _hotSearchWidget();
        }
        if (index == 1) {
          return _historSearchTitleWidget();
        }
        final keyword = _historySearchData[index -2];
        return Container(
          padding: EdgeInsets.only(left: 10, right: 10),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              InkWell(
                onTap: (){
                  // 进入商品分类后,点击返回,可以跳过搜索页,直接返回到根视图
                  Navigator.pushReplacementNamed(context, "/productList", arguments: {
                    "keyword": keyword
                  });
                },
                onLongPress: (){
                  ShowAlertWidget.showAlert(context, "删除提醒", "确认要删除这条数据吗").then((value) {
                    if (value == 1) {
                      StorageService.removeSearchKeyword(keyword).then((list){
                        setState(() {
                          _historySearchData = list;
                        });
                      });
                    }
                  });
                },
                child: Container(
                  width: double.infinity,
                  padding: EdgeInsets.only(top: 14, bottom: 14),
                  child: Text(keyword, style: TextStyle(fontSize: 16), textAlign: TextAlign.start),
                ),
              ),
              Divider(height: 1, color: Colors.grey[200])
            ],
          ),
        );
      },
      itemCount: _historySearchData.isNotEmpty ? _historySearchData.length + 2 : 1,
    );
  }

  // 删除的按钮
  Widget _deleteHistoryWiget() {
    return Container(
      margin: EdgeInsets.only(left: Screenadapter.width(60), right: Screenadapter.width(60)),
      height: Screenadapter.height(100),
      decoration: BoxDecoration(
        border: Border.all(
          width: 1,
          color: Colors.black45
        ),
        borderRadius: BorderRadius.circular(5)
      ),
      child: InkWell(
        onTap: (){
          StorageService.clearSearchHistory();
          setState(() {
            _historySearchData = [];
          });
        },
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.delete),
            Text("清空所有历史记录")
          ],
        ),
      ),
    );
  }

  
  void initState() {
    super.initState();

    StorageService.getSearchHistoryString().then((list){
      setState(() {
        _historySearchData = list;
      });
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: _searchInputWidget(),
        actions: [
          _searchItemWiget()
        ],
      ),
      body: Container(
        padding: EdgeInsets.only(bottom: 30),
        child: Column(
          children: [
            Expanded(child: _searchHotAndHistroyWidget()),
            _deleteHistoryWiget()
          ],
        ),
      ),
    );
  }
}

相关文章:

  • 数据集笔记:新加坡traffic 照片
  • 虚拟化园区网络部署指南
  • 第十三届蓝桥杯大赛软件赛决赛C/C++ 大学 B 组
  • Linux下的网络通信编程
  • 2024年12月中国电子学会青少年软件编程(Python)等级考试试卷(四级)真题 + 答案
  • 手撸大模型-基础篇 简单线性回归模型预测房价
  • 一周一个Unity小游戏2D反弹球游戏 - 球板的发球
  • 【03】STM32F407 HAL 库框架设计学习
  • 介绍微信小程序中页面的生命周期函数和组件的生命周期函数
  • Python:列表的定义和增删改查,推导式与嵌套
  • 蓝桥杯单片机第16届4T模拟赛三思路讲解
  • 计算机毕业设计SpringBoot+Vue.js汽车资讯网站(源码+文档+PPT+讲解)
  • Java支持多继承么,为什么?
  • 4-3自定义加载器,并添加功能
  • 本地部署大模型
  • 《动手学习深度学习》的笔记,将会持续更新。
  • vue3:三项目增加404页面
  • Mysql 语法再巩固
  • 接口自动化测试框架(pytest+allure+aiohttp+用例自动生成)
  • 香港大学deep-research开源项目
  • 英文购物网站模板/自学seo大概需要多久
  • 赚钱的十大个人网站/属性词 关键词 核心词
  • 武汉手机网站开发/百度问一问付费咨询
  • 临沂做商城网站/视频seo优化教程
  • 深圳市专业制作网站公司/企业网站建设方案范文
  • 中企动力 网站建设 眼镜/简述seo的优化流程