0


Flutter 数据持久化存储之Hive库

Flutter 数据持久化存储之Hive库

前言

  在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';classHivePageextendsStatelessWidget{@overrideWidgetbuild(BuildContext context){returnScaffold(
      appBar:AppBar(
        title:constText("Hive Demo"),),
      body:Container(
        color:Colors.blue,),);}}

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

import'package:flutter/material.dart';import'package:study_hive/page/hive_page.dart';voidmain()async{runApp(constMyApp());}classMyAppextendsStatelessWidget{constMyApp({super.key});@overrideWidgetbuild(BuildContext context){returnMaterialApp(
      title:'Hive Demo',
      theme:ThemeData(
        primaryColor:Colors.blueAccent,
        appBarTheme:constAppBarTheme(elevation:0),),
      home:HivePage(),);}}

  这里的修改就是去掉了原来默认代码,并且加载我们刚写好的

HivePage

,下面我们可以运行一下,虚拟器或者真机都可以。

在这里插入图片描述

① 增加UI

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

///通用输入框WidgetbaseEdit(String hintText,TextInputType type,TextEditingController textController){returnContainer(
        decoration:BoxDecoration(
          borderRadius:BorderRadius.circular(10),
          border:Border.all(
            color:Colors.black87,
            width:1.0,),),
        margin:constEdgeInsets.only(top:6),
        padding:constEdgeInsets.all(0),
        height:44,
        child:TextField(
          textInputAction:TextInputAction.none,
          keyboardType: type,
          cursorColor:Colors.black87,
          cursorWidth:1,
          controller: textController,
          decoration:InputDecoration(
            contentPadding:constEdgeInsets.only(left:10),
            filled:true,
            fillColor:Colors.white,
            hintText: hintText,
            hintStyle:constTextStyle(
              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';classHiveControllerextendsGetxController{

  late TextEditingController nameEditController,ageEditController;@overridevoidonInit(){super.onInit();
    nameEditController =TextEditingController();
    ageEditController =TextEditingController();}}

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

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

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

hive_controller.dart

,代码如下所示:

import'package:flutter/material.dart';import'package:get/get.dart';classHiveControllerextendsGetxController{

  late TextEditingController nameEditController,ageEditController;@overridevoidonInit(){super.onInit();
    nameEditController =TextEditingController();
    ageEditController =TextEditingController();}}

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

///保存组件var saveWidget =Container(
      width:MediaQuery.of(context).size.width,
      margin:constEdgeInsets.all(8),
      padding:constEdgeInsets.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

中的代码,在这里我们加载刚才写好的保存组件,如下所示:

returnScaffold(
      appBar:AppBar(
        title:constText("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:constEdgeInsets.only(left:8, right:8, bottom:8),
      padding:constEdgeInsets.all(8),
      decoration:BoxDecoration(
        color:Colors.white,
        borderRadius:BorderRadius.circular(12.0),),));var deleteAllBtn =ElevatedButton(
        onPressed:(){print('DeleteAll');},
        child:constRow(
          mainAxisSize:MainAxisSize.min,
          children:[Icon(Icons.delete, color:Colors.red),SizedBox(width:4),Text('DeleteAll',
              style:TextStyle(color:Colors.red),)],));

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

returnScaffold(
      appBar:AppBar(
        title:constText("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()

函数代码如下所示:

voidmain()async{//初始化HiveawaitHive.initFlutter();runApp(constMyApp());}

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

② TypeAdapter自定义对象

  在

lib

下创建一个

models

目录,该目录下创建person.dart文件,代码如下:

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

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

import'package:hive/hive.dart';part'person.g.dart';@HiveType(typeId:1)classPerson{@HiveField(0)String name;@HiveField(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// **************************************************************************classPersonAdapterextendsTypeAdapter<Person>{@overridefinal int typeId =1;@overridePersonread(BinaryReader reader){final numOfFields = reader.readByte();final fields =<int,dynamic>{for(int i =0; i < numOfFields; i++) reader.readByte(): reader.read(),};returnPerson(
      name: fields[0]asString,
      age: fields[1]as int,);}@overridevoidwrite(BinaryWriter writer,Person obj){
    writer
      ..writeByte(2)..writeByte(0)..write(obj.name)..writeByte(1)..write(obj.age);}@override
  int get hashCode => typeId.hashCode;@override
  bool operator==(Object other)=>identical(this, other)||
      other isPersonAdapter&&
          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';voidmain()async{//初始化HiveawaitHive.initFlutter();//注册TypeAdapterHive.registerAdapter(PersonAdapter());//打开盒子awaitHive.openBox<Person>('personBox');runApp(constMyApp());}

  注意导包语句,现在我们的盒子就打开了,盒子名称是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';classHiveControllerextendsGetxController{
  late TextEditingController nameEditController, ageEditController;final personBox =Hive.box<Person>('personBox');@overridevoidonInit(){super.onInit();
    nameEditController =TextEditingController();
    ageEditController =TextEditingController();}voidsave(){var person =Person(
        name: nameEditController.text, age: int.parse(ageEditController.text));
    personBox.add(person);
    nameEditController.clear();
    ageEditController.clear();}voidmodify(int index,Person person){
    personBox.putAt(index, person);}voiddelete(int index){
    personBox.deleteAt(index);}voiddeleteAll(){
    personBox.clear();}@overridevoidonClose(){
    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:constEdgeInsets.only(left:8, right:8, bottom:8),
            padding:constEdgeInsets.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){returnconstCenter(
                      child:Text('Empty'),);}else{returnListView.builder(
                        itemCount: box.length,
                        itemBuilder:(context, index){var personData = box.getAt(index)!;returnListTile(
                            title:Text(personData.name),
                            subtitle:Text(personData.age.toString()),
                            trailing:Row(
                              mainAxisSize:MainAxisSize.min,
                              children:<Widget>[IconButton(
                                  icon:constIcon(Icons.edit),
                                  onPressed:(){showModifyDialog(index, personData);},),IconButton(
                                  icon:constIcon(Icons.delete),
                                  onPressed:(){
                                    controller.delete(index);},),],),);});}})));

  这里的核心代码就是

ValueListenableBuilder

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

在这里插入图片描述

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

在这里插入图片描述

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

voidshowModifyDialog(int index,Person personData)=>showDialog(
        context: context,
        builder:(BuildContext context){TextEditingController nameController =TextEditingController(text: personData.name);TextEditingController ageController =TextEditingController(text: personData.age.toString());returnAlertDialog(
            title:constText('Modify Data'),
            content:Column(
              mainAxisSize:MainAxisSize.min,
              children:[TextField(controller: nameController),TextField(controller: ageController)],),
            actions:[ElevatedButton(
                  child:constText('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:constText('Save',
          style:TextStyle(color:Colors.blue),));var deleteAllBtn =ElevatedButton(
        onPressed:(){
          controller.deleteAll();},
        child:constRow(
          mainAxisSize:MainAxisSize.min,
          children:[Icon(Icons.delete, color:Colors.red),SizedBox(width:4),Text('DeleteAll',
              style:TextStyle(color:Colors.red),)],));

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

在这里插入图片描述

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

四、源码

源码地址:study_hive

标签: flutter hive

本文转载自: https://blog.csdn.net/qq_38436214/article/details/136194583
版权归原作者 初学者-Study 所有, 如有侵权,请联系我们删除。

“Flutter 数据持久化存储之Hive库”的评论:

还没有评论