Flutter 数据持久化存储之Hive库

2024-06-04 8628阅读

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,感兴趣的可以自行了解,本文运行效果图。

          Flutter 数据持久化存储之Hive库 第1张

          正文

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

          一、配置项目

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

          Flutter 数据持久化存储之Hive库 第2张

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

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

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

          Flutter 数据持久化存储之Hive库 第3张

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

          二、UI

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

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

          import 'package:flutter/material.dart';
          class HivePage extends StatelessWidget {
            @override
            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});
            @override
            Widget build(BuildContext context) {
              return MaterialApp(
                title: 'Hive Demo',
                theme: ThemeData(
                  primaryColor: Colors.blueAccent,
                  appBarTheme: const AppBarTheme(elevation: 0),
                ),
                home: HivePage(),
              );
            }
          }
          

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

          Flutter 数据持久化存储之Hive库 第4张

          ① 增加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;
            @override
            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;
            @override
            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],
                  ),
                ),
              );
          

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

          Flutter 数据持久化存储之Hive库 第5张

          运行一下:

          Flutter 数据持久化存储之Hive库 第6张

          这样增加的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],
                  ),
                ),
              );
          

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

          Flutter 数据持久化存储之Hive库 第7张

          三、使用Hive

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

          ① 初始化Hive

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

          import 'package:hive_flutter/hive_flutter.dart';
          

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

          void main() async {
            //初始化Hive
            await 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';
          @HiveType(typeId: 1)
          class Person {
            @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 之间,不可以重复。
            • HiveField:

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

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

          flutter packages pub run build_runner build
          

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

          Flutter 数据持久化存储之Hive库 第8张

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

          // GENERATED CODE - DO NOT MODIFY BY HAND
          part of 'person.dart';
          // **************************************************************************
          // TypeAdapterGenerator
          // **************************************************************************
          class PersonAdapter extends TypeAdapter {
            @override
            final int typeId = 1;
            @override
            Person read(BinaryReader reader) {
              final numOfFields = reader.readByte();
              final fields = {
                for (int i = 0; i  typeId.hashCode;
            @override
            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 {
            //初始化Hive
            await Hive.initFlutter();
            //注册TypeAdapter
            Hive.registerAdapter(PersonAdapter());
            //打开盒子
            await Hive.openBox('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('personBox');
            @override
            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();
            }
            @override
            void onClose() {
              nameEditController.dispose();
              ageEditController.dispose();
              super.onClose();
            }
          }
          

            上面的代码解释一下,首先我们获取personBox盒子对象,final personBox = Hive.box('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: [
                                          IconButton(
                                            icon: const Icon(Icons.edit),
                                            onPressed: () {
                                              showModifyDialog(index, personData);
                                            },
                                          ),
                                          IconButton(
                                            icon: const Icon(Icons.delete),
                                            onPressed: () {
                                              controller.delete(index);
                                            },
                                          ),
                                        ],
                                      ),
                                    );
                                  });
                            }
                          })));
          

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

          Flutter 数据持久化存储之Hive库 第9张

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

          Flutter 数据持久化存储之Hive库 第10张

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

              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),
                      )
                    ],
                  ));
          

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

          Flutter 数据持久化存储之Hive库 第1张

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

          四、源码

          源码地址:study_hive


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]