Flutter 数据持久化存储之Hive库

Flutter 数据持久化存储之Hive库

  • 前言
  • 正文
    • 一、配置项目
    • 二、UI
      • ① 增加UI
      • ② 显示和删除UI
    • 三、使用Hive
      • ① 初始化Hive
      • ② TypeAdapter自定义对象
      • ③ 注册TypeAdapter
      • ③ CURD
    • 四、源码

前言

  在Flutter中,有多种方式可以进行数据持久化存储。以下是一些常见的方式:

  1. Shared Preferences:
    使用shared_preferences插件,可以将数据存储在设备的轻量级持久化存储中。这种方式适合存储少量简单的键值对数据,比如用户偏好设置等。

  2. 文件存储:
    使用dart:io库可以进行文件存储,可以将数据以文件的形式存储在设备上。这种方式适合存储结构化数据,可以使用JSON格式或者其他格式进行数据的读写。

  3. SQLite数据库:
    可以使用sqflite插件在Flutter应用中使用SQLite数据库。SQLite是一种轻量级的关系型数据库,适合于需要存储结构化数据,并进行高效查询的场景。

  4. NoSQL数据库:
    一些Flutter插件(如moor)也提供了对NoSQL数据库的支持,比如使用对象数据库(如Hive)来存储数据。

  5. 云存储:
    通过与云存储(如Firebase Firestore、AWS Amplify等)进行集成,可以将数据存储在云端,实现跨设备数据同步和备份。

以上的这些我们都不使用,这里要使用的是Hive库,地址是 Hive,感兴趣的可以自行了解,本文运行效果图。

在这里插入图片描述

正文

  Hive是一个轻量级、快速的本地数据库解决方案,适用于在移动应用程序中进行数据持久化存储。Hive采用高效的自定义序列化算法,能够在移动设备上快速读写数据,适用于处理结构化数据。并且Hive是用纯Dart编写的,这使得它比不支持Flutter网络的SQLite更有优势。

一、配置项目

  首先我们创建一个名为study_hive的项目。

在这里插入图片描述

  创建项目之后,我们配置一下依赖库,在项目的pubspec.yaml文件中,添加如下所示代码:

dependencies:get:hive:hive_flutter:dev_dependencies:hive_generator:build_runner:

  在dependencies中我添加了get和hive的库,在dev_dependencies中添加了一个构建对象的依赖库。冒号后面没有写版本号就是获取该库最新的版本。添加位置如下图所示:

在这里插入图片描述

然后点击Pub get获取对应的依赖库即可,到这里为止我们的配置工作就完成了。

二、UI

  在使用Hive库时我们需要想一下,用这个库去做什么?先设想一个应用场景,而不是写到哪里就是哪里,乱枪打鸟不可取。我们就写这样一个场景,对于人员信息的操作,可以增加、查询、修改、删除、删除所有。基于这个场景我们就可以去设计UI了,我们尽量在一个页面去解决,更直观一些(PS:我也是偷一个懒)。

  首先我们在lib目录下新建一个page包,page包下新建一个hive_page.dart,里面的代码如下:

import 'package:flutter/material.dart';class HivePage extends StatelessWidget {Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("Hive Demo"),),body: Container(color: Colors.blue,),);}
}

  当前页面很简单,就是一个标题和蓝色背景,当然你现在还看不到的,我们需要修改一下main.dart中的代码:

import 'package:flutter/material.dart';
import 'package:study_hive/page/hive_page.dart';void main() async {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Hive Demo',theme: ThemeData(primaryColor: Colors.blueAccent,appBarTheme: const AppBarTheme(elevation: 0),),home: HivePage(),);}
}

  这里的修改就是去掉了原来默认代码,并且加载我们刚写好的HivePage,下面我们可以运行一下,虚拟器或者真机都可以。

在这里插入图片描述

① 增加UI

在HivePage的build()中增加如下代码:

    ///通用输入框Widget baseEdit(String hintText, TextInputType type,TextEditingController textController) {return Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(10),border: Border.all(color: Colors.black87,width: 1.0,),),margin: const EdgeInsets.only(top: 6),padding: const EdgeInsets.all(0),height: 44,child: TextField(textInputAction: TextInputAction.none,keyboardType: type,cursorColor: Colors.black87,cursorWidth: 1,controller: textController,decoration: InputDecoration(contentPadding: const EdgeInsets.only(left: 10),filled: true,fillColor: Colors.white,hintText: hintText,hintStyle: const TextStyle(textBaseline: TextBaseline.alphabetic,color: Colors.grey,),border: OutlineInputBorder(borderRadius: BorderRadius.circular(10),borderSide: BorderSide.none,),),),);}

  这里的代码就是构建一个输入框的组件,将里面的提示文本、键盘类型和输入框控制器抽离了出来。控制器我们就放到GetX中使用,在page包下新建一个hive_controller.dart,代码如下所示:

import 'package:flutter/material.dart';
import 'package:get/get.dart';class HiveController extends GetxController {late TextEditingController nameEditController,ageEditController;void onInit() {super.onInit();nameEditController = TextEditingController();ageEditController = TextEditingController();}
}

  这里主要就是对于输入框控制器的初始化。回到HivePage的build中再写两个组件,代码如下:

    var size4 = const SizedBox(height: 4,width: 4,);///保存按钮var saveBtn = TextButton(onPressed: () {print('Save');},child: const Text('Save',style: TextStyle(color: Colors.blue),));

  一个是间隔,一个是保存按钮,然后我们可以再写一个组件用来包含刚才所写的内容。这里面就需要用到baseEdit去构建两个输入框,因此我们加上GetX,在page包下新建一个hive_controller.dart,代码如下所示:

import 'package:flutter/material.dart';
import 'package:get/get.dart';class HiveController extends GetxController {late TextEditingController nameEditController,ageEditController;void onInit() {super.onInit();nameEditController = TextEditingController();ageEditController = TextEditingController();}
}

回到HivePage中,在build中增加一个组件,代码如下:

	///保存组件var saveWidget = Container(width: MediaQuery.of(context).size.width,margin: const EdgeInsets.all(8),padding: const EdgeInsets.all(8),decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(12.0),),child: Column(mainAxisSize: MainAxisSize.min,children: [baseEdit('Name', TextInputType.name, controller.nameEditController),size4,baseEdit('Age', TextInputType.number, controller.ageEditController),saveBtn],),);

最后我们再修改一下返回的Scaffold中的代码,在这里我们加载刚才写好的保存组件,如下所示:

	return Scaffold(appBar: AppBar(title: const Text("Hive Demo"),),body: Container(color: Colors.blue,child: Column(children: [saveWidget],),),);

这里你需要注意的就是代码的顺序了,当前这个组件在最下边,通过一张图来说明。

在这里插入图片描述

运行一下:

在这里插入图片描述

这样增加的UI就写好了,下面我们构建显示和删除的。

② 显示和删除UI

在build中添加如下代码:

    ///列表组件var listWidget = Expanded(child: Container(width: MediaQuery.of(context).size.width,// 允许高度自适应margin: const EdgeInsets.only(left: 8, right: 8, bottom: 8),padding: const EdgeInsets.all(8),decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(12.0),),));var deleteAllBtn = ElevatedButton(onPressed: () {print('DeleteAll');},child: const Row(mainAxisSize: MainAxisSize.min,children: [Icon(Icons.delete, color: Colors.red),SizedBox(width: 4),Text('DeleteAll',style: TextStyle(color: Colors.red),)],));

再修改一下返回的Scaffold,将列表和按钮组件添加进去,代码如下所示:

    return Scaffold(appBar: AppBar(title: const Text("Hive Demo"),),body: Container(color: Colors.blue,child: Column(children: [saveWidget, listWidget, deleteAllBtn],),),);

再保存一下,热重载,效果如图所示:

在这里插入图片描述

三、使用Hive

下面我们就可以开始使用Hive了,之前我们已经添加过依赖了,下面我们首先进行初始化。

① 初始化Hive

  在Flutter中使用Hive,我们需要在main()函数中进行初始化,注意导包语句:

import 'package:hive_flutter/hive_flutter.dart';

main()函数代码如下所示:

void main() async {//初始化Hiveawait Hive.initFlutter();runApp(const MyApp());
}

  初始化之后我们就可以去使用了,在此之前我们需要明确使用的方式,因为我们操作的是对象,包含常规的数据类型,因此我们就需要自定义对象。

② TypeAdapter自定义对象

  在lib下创建一个models目录,该目录下创建person.dart文件,代码如下:

class Person {String name;int age;Person({required this.name,required this.age});
}

  这是标准的对象代码,然后我们可以使用Hive注释这个类和类里面的变量,然后快速生成一个TypeAdapter类代码,下面我们修改一下Person的代码如下:

import 'package:hive/hive.dart';part 'person.g.dart';(typeId: 1)
class Person {(0)String name;(1)int age;Person({required this.name, required this.age});
}

  首先注意导包的语句,这里的part 'person.g.dart';语句会标红,这是因为目前还没有这个文件,这个文件就是我们需要快捷生成的。HiveType 和 HiveField 是 Hive 数据库中用来定义对象映射和序列化的注解。

  1. HiveType:

    • HiveType 是一个标记注解,用于标识 Hive 中的自定义对象类。它告诉 Hive 数据库,被注解的类是一个 Hive 对象,需要进行序列化和反序列化。
    • 当你在定义自己的模型类时,可以使用 @HiveType() 注解来标记这个类,以便 Hive 可以识别并处理这个类。
    • 所有的 typeId 允许在 0 到 223 之间,不可以重复。
  2. HiveField:

    • HiveField 是用来标记类中的字段(成员变量)的注解,用于指定字段在 Hive 数据库中的位置和顺序。
    • 当你在定义自己的模型类时,可以使用 @HiveField() 注解来标记类中的字段,以便 Hive 可以按照指定的顺序进行序列化和反序列化。
    • 字段编号的范围可为 0~255,不可以重复。

  下面我们通过在Terminal中输入一行代码,生成对应的TypeAdapter对象类,代码如下所示:

flutter packages pub run build_runner build

输入后回车,如下图所示:

在这里插入图片描述

  你会看到对应的person.g.dart文件就已经生成在models文件夹中,里面的代码如下所示:

// GENERATED CODE - DO NOT MODIFY BY HANDpart of 'person.dart';// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************class PersonAdapter extends TypeAdapter<Person> {final int typeId = 1;Person read(BinaryReader reader) {final numOfFields = reader.readByte();final fields = <int, dynamic>{for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),};return Person(name: fields[0] as String,age: fields[1] as int,);}void write(BinaryWriter writer, Person obj) {writer..writeByte(2)..writeByte(0)..write(obj.name)..writeByte(1)..write(obj.age);}int get hashCode => typeId.hashCode;bool operator ==(Object other) =>identical(this, other) ||other is PersonAdapter &&runtimeType == other.runtimeType &&typeId == other.typeId;
}

下面我们注册TypeAdapter对象

③ 注册TypeAdapter

  依然是修改main()函数,注意一点,在打开使用Hive的盒子之前,需要先注册TypeAdapter,代码如下所示:

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:study_hive/models/person.dart';
import 'package:study_hive/page/hive_page.dart';void main() async {//初始化Hiveawait Hive.initFlutter();//注册TypeAdapterHive.registerAdapter(PersonAdapter());//打开盒子await Hive.openBox<Person>('personBox');runApp(const MyApp());
}

  注意导包语句,现在我们的盒子就打开了,盒子名称是personBox,这个可以自己去定义的,下面我们就可以正式去使用这个盒子来进行CURD了。

③ CURD

  在进行CURD时,我们将代码写在GetxController中,提供相关的函数进行操作,下面我们修改一下HiveController中的代码:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:study_hive/models/person.dart';class HiveController extends GetxController {late TextEditingController nameEditController, ageEditController;final personBox = Hive.box<Person>('personBox');void onInit() {super.onInit();nameEditController = TextEditingController();ageEditController = TextEditingController();}void save() {var person = Person(name: nameEditController.text, age: int.parse(ageEditController.text));personBox.add(person);nameEditController.clear();ageEditController.clear();}void modify(int index, Person person) {personBox.putAt(index, person);}void delete(int index) {personBox.deleteAt(index);}void deleteAll() {personBox.clear();}void onClose() {nameEditController.dispose();ageEditController.dispose();super.onClose();}
}

  上面的代码解释一下,首先我们获取personBox盒子对象,final personBox = Hive.box<Person>('personBox');,然后就是save()函数中获取输入框的值进行保存,保存之后再清空输入框,这里就没有对输入框的内容判空处理,需要注意一下。modify()函数中通过下标和person对象就可以完成,删除和删除所有就是可以直接处理的,就没有什么好说的。你会发现没有查询,这是因为Hive提供了一个名为ValueListenableBuilder 的小部件,它只在数据库内的任何数值被修改时才会刷新。下面我们就可以在HivePage中去使用刚才所写的函数。

首先我们修改一下listWidget组件的代码:

    var listWidget = Expanded(child: Container(width: MediaQuery.of(context).size.width,margin: const EdgeInsets.only(left: 8, right: 8, bottom: 8),padding: const EdgeInsets.all(8),decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(12.0),),child: ValueListenableBuilder(valueListenable: controller.personBox.listenable(),builder: (context, box, widget) {if (box.isEmpty) {return const Center(child: Text('Empty'),);} else {return ListView.builder(itemCount: box.length,itemBuilder: (context, index) {var personData = box.getAt(index)!;return ListTile(title: Text(personData.name),subtitle: Text(personData.age.toString()),trailing: Row(mainAxisSize: MainAxisSize.min,children: <Widget>[IconButton(icon: const Icon(Icons.edit),onPressed: () {showModifyDialog(index, personData);},),IconButton(icon: const Icon(Icons.delete),onPressed: () {controller.delete(index);},),],),);});}})));

  这里的核心代码就是ValueListenableBuilder 的使用,这里我们判断了box是否为空,空就显示文字提示一下,不为空就构建一个ListView显示Item数据。如下图所示:

在这里插入图片描述

  在列表的Item中我们除了显示用户的名称和年龄之外还有两个功能按钮,分别用于修改和删除,如下图所示:

在这里插入图片描述

  针对于删除很简单之后调用控制器里面写好的函数就可以了,删除之后列表会自动刷新的。而修改的话屏幕上没有空间了,因此我就写一个弹窗去显示需要修改的内容,代码如下所示:

    void showModifyDialog(int index, Person personData) => showDialog(context: context,builder: (BuildContext context) {TextEditingController nameController =TextEditingController(text: personData.name);TextEditingController ageController =TextEditingController(text: personData.age.toString());return AlertDialog(title: const Text('Modify Data'),content: Column(mainAxisSize: MainAxisSize.min,children: [TextField(controller: nameController),TextField(controller: ageController)],),actions: [ElevatedButton(child: const Text('Modify'),onPressed: () {var person = Person(name: nameController.text,age: int.parse(ageController.text));controller.modify(index, person);Navigator.of(context).pop(); // 关闭对话框})],);});

弹窗修改之后就关闭弹窗。最后我们再修改一下保存按钮和删除所有按钮组件的代码,如下所示:

var saveBtn = TextButton(onPressed: () {controller.save();},child: const Text('Save',style: TextStyle(color: Colors.blue),));var deleteAllBtn = ElevatedButton(onPressed: () {controller.deleteAll();},child: const Row(mainAxisSize: MainAxisSize.min,children: [Icon(Icons.delete, color: Colors.red),SizedBox(width: 4),Text('DeleteAll',style: TextStyle(color: Colors.red),)],));

那么基本上代码就写完了,下面我们整体看一下运行效果。

在这里插入图片描述

效果符合我的预期,文章到这里就结束了,元宵节快乐呀!

四、源码

源码地址:study_hive

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/498704.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TOMCAT的安装与基本信息

目录 引言 一、TOMCAT简介 二、TOMCAT安装 &#xff08;一&#xff09;JDK简介 &#xff08;二&#xff09;安装Oracle JDK 1.rpm安装 2.二进制安装 &#xff08;三&#xff09;安装TOMCAT 三、配置文件介绍及核心组件 &#xff08;一&#xff09;配置文件 1.目录文…

Java毕业设计 基于SSM SpringBoot vue购物比价网站

Java毕业设计 基于SSM SpringBoot vue购物比价网站 SSM vue 购物比价网站 功能介绍 首页 图片轮播 商品 商品分类 商品详情 评论 收藏 商品攻略 商品信息 公告栏 在线反馈 登录 注册 个人中心 我的收藏 后台管理 登录 注册商品户 个人中心 修改密码 个人信息 商品户管理 用户…

CCF-A类 IEEE VIS‘24 3月31日截稿!探索可视化技术的无限可能!

会议之眼 快讯 IEEE VIS (IEEE Visualization Conference )即可视化大会将于 2024 年 10月13日 -18日在美国佛罗里达州皮特海滩的信风岛大海滩度假举行&#xff01;圣彼得海滩&#xff0c;以其迷人的日落和和煦的微风&#xff0c;作为激发创造力和促进可视化社区内合作的完美背…

vue构建版本

完整版&#xff1a;同时包含编译器和运行时的版本。 编译器&#xff1a;用来将模板字符串编译成为 JavaScript 渲染函数的代码。 运行时runtime&#xff1a;用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。 UMD&#xff1a;UMD 版本可以…

【2024创业风向标】随身WiFi成财富新宠,格行带你解锁无限商机!

近日&#xff0c;一股“随身WiFi创业潮”悄然兴起&#xff0c;成为不少创业者和投资者关注的焦点。在这个5G时代&#xff0c;网络已成为生活不可或缺的一部分&#xff0c;而格行随身WiFi凭借其便捷、高效的特点&#xff0c;正成为市场新宠。 据了解&#xff0c;格行随身WiFi不仅…

MySQL里的两个“二次”

文章中所有图片均来自网络 一、double write 第一个二次是mysql一个崩溃恢复很重要的特性-重复写入。 doublewrite缓冲区是位于系统表空间中的存储区域&#xff0c;在该区域中&#xff0c;InnoDB会在将页面写入数据文件中的适当位置之前&#xff0c;从InnoDB缓冲池中刷新这些页…

【Redis | 第一篇】快速了解Redis

文章目录 1.快速了解Redis1.1简介1.2与其他key-value存储的不同处1.3Redis安装——Windows环境1.3.1下载redis1.3.2启动redis1.3.3进入redis客户端1.3.4修改配置 1.4Redis安装——Linux环境1.4.1安装命令1.4.2启动redis1.4.3进入redis客户端 1.5配置修改1.6小结 1.快速了解Redi…

巧妙解决接口测试产生脏数据问题

测试数据创建后需要对其删除&#xff0c;不然可能产生脏数据&#xff0c;对开发和测试、生产环境造成一定影响。 其接口框架是基于Python&#xff0c;API规范基于REST。 产生原因 改进前&#xff1a;清除资源的操作放在每个正向测试用例里&#xff0c;没有在setUp和tearDown…

【C++11】C++11新特性(下)

1、类的新功能 namespace flash {class string{public:// 构造函数string(const char* str ""):_size(strlen(str)), _capacity(_size){//cout << "string(char* str)" << endl;_str new char[_capacity 1];strcpy(_str, str);}// 交换两个…

【AI Agent系列】【MetaGPT多智能体学习】3. 开发一个简单的多智能体系统,兼看MetaGPT多智能体运行机制

本系列文章跟随《MetaGPT多智能体课程》&#xff08;https://github.com/datawhalechina/hugging-multi-agent&#xff09;&#xff0c;深入理解并实践多智能体系统的开发。 本文为该课程的第四章&#xff08;多智能体开发&#xff09;的第一篇笔记。主要记录下多智能体的运行…

java -进行堆转储文件分析

文章目录 前言java -进行堆转储文件分析1. 首先在window上主动生成堆转储文件2. 获取堆转储文件的方式3. 堆转储文件分析 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人…

【c语言】探索结构体---解锁更多选择

前言 结构体属于自定义类型的一种&#xff0c;这增加了我们写代码的选择&#xff0c;跟着我的脚步一起来探索结构体吧~ 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 认识结构体 结构是⼀些值的集合&#xff0c;这些值…