hive 本地数据
One of the most important parts of app development is data storage and manipulation, and the same is true for flutter apps. There are many ways to store local data in flutter apps, but in this article, we will learn about just one — Hive.
应用程序开发中最重要的部分之一是数据存储和处理,对于Flutter应用程序也是如此。 有多种方法可以在flutter应用程序中存储本地数据,但是在本文中, 我们将仅介绍一种-Hive 。
Hive is a fast, lightweight, NoSQL database, for flutter and dart apps. Hive is really useful if you want a simple key-value database, without many relations, and really easy to use.
Hive是一个快速,轻量级的NoSQL数据库,适用于Flutter和Dart应用程序。 如果您想要一个简单的键值数据库,没有很多关系,并且确实易于使用,那么Hive真的很有用。
In this article, we’ll go over these steps to build a simple CRUD app with hive:
在本文中,我们将介绍这些步骤,以使用hive构建一个简单的CRUD应用程序:
- Setup Project 设置项目
- Setup hive 设置单元
- Type Adapters 类型适配器
- Storing data 储存资料
- Reading data 读取数据
- Update and Delete data 更新和删除数据
先决条件 (Prerequisites)
I won’t be talking much about provider setup or UIs in this article, so you should already have some idea about adding provider to your project.
在本文中,我不会过多地讨论提供程序设置或UI,因此您应该已经对将提供程序添加到项目中有了一些想法。
设置项目 (Setup Project)
To do the CRUD operations we’ll be using a simple inventory app, where you can create inventory items, update and delete them when you want to. For ease, I’ve set up the project and put it on Github in the starter folder, it contains the UI and the provider setup.
要执行CRUD操作,我们将使用一个简单的清单应用程序,您可以在其中创建清单项目,并在需要时更新和删除它们。 为方便起见,我已经设置了项目并将其放在Starter文件夹中的Github上,它包含UI和提供程序设置。
you can clone it and follow from there.
您可以克隆它并从那里开始。
添加依赖项 (Add Dependencies)
After creating your project (or cloning mine), as usual, you need to add some dependencies in order to continue building the app. the dependencies we’ll be adding are:
在创建项目(或克隆我的项目)之后,照常,您需要添加一些依赖项以继续构建应用程序。 我们将添加的依赖项是:
hive: ^1.4.1+1
provider: ^4.3.1
path_provider: ^1.6.5
we also need to add hive_generator and build_runner to the dev dependencies:
我们还需要将hive_generator和build_runner添加到dev依赖项中:
hive_generator: ^0.5.0
Hive generator helps us generate type adapters, you’ll see why later on in the article.
Hive generator可以帮助我们生成类型适配器,您将在本文后面看到原因。
along with the other dependencies already in the starter project.
以及入门项目中已经存在的其他依赖项。
设置单元 (Setup Hive)
Before you can use the hive, you need to initialize it. This is done in the main() function of your app before runApp().
在使用配置单元之前,需要对其进行初始化。 这在runApp()之前在应用程序的main()函数中完成。
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart' as pathProvider;
Future<void> main() async {
Directory directory = await pathProvider.getApplicationDocumentsDirectory();
Hive.init(directory.path);
runApp(MyApp());
}
First, import
path_provider, because Hive needs a home directory as it is initialized.首先,导入
path_provider,因为Hive在初始化时需要一个主目录。Next, get the directory from the
path_provider.接下来,从
path_provider获取目录。- Finally, initialize hive with the path of the directory. 最后,使用目录路径初始化配置单元。
蜂巢盒 (Hive Boxes)
Hive stores its data in boxes, containing key-value pairs. I like to see boxes as a folder with files listed and sorted by a numbering system, or as normal maps in dart. With hive, before you can read/write data a box must be opened. boxes can be opened with await Hive.openBox('name'), and you can get an instance of an opened box with Hive.box('name'), where 'name' is the name of the box (kind of saying the DB name).
Hive将其数据存储在包含键值对的boxes 。 我喜欢将框看作是一个文件夹,其中包含按编号系统列出和排序的文件,或者作为Dart中的法线贴图。 使用配置单元,必须先打开一个框,然后才能读取/写入数据。 可以使用await Hive.openBox('name')打开盒子,并且可以使用Hive.box('name')获得打开的盒子的实例,其中'name'是盒子的名字(说数据库名称)。
you can call
Hive.openBox('name')even if you already have the box opened somewhere else, sometimes it may be smart to do this.即使您已经在其他地方打开了该框,也 可以调用
Hive.openBox('name'),有时这样做很聪明。
类型适配器 (Type Adapters)
Hive allows you to store most standard types — String, int, Map, List, DateTime, but most times you want to have a model class for your data because this makes development much easier and faster. In order to use these model types, you need to register TypeAdapters which helps you encode/decode your object to binary form on disk. You can write your Type Adapters manually, but we’ll just be generating ours with the hive_generator and build_runner packages we added to our dev dependencies earlier.
Hive允许您存储大多数标准类型-String,int,Map,List,DateTime,但是大多数情况下,您希望为数据提供模型类,因为这使开发变得更加容易和快捷。 为了使用这些模型类型,您需要注册TypeAdapters ,它可以帮助您将对象编码/解码为磁盘上的二进制形式。 您可以手动编写类型适配器,但我们将使用hive_generator添加到我们的dev依赖项中的hive_generator和build_runner软件包来生成我们的类型适配器。
Somewhere in your lib/ folder, you can create your model file (mine is called inventory_model.dart).
您可以在lib/文件夹中的某个位置创建模型文件(我的模型称为inventory_model.dart )。
Now let’s create the class and add the right annotations so the TypeAdapters can be generated:
现在,让我们创建类并添加正确的注释,以便可以生成TypeAdapters:
import 'package:hive/hive.dart';
part 'inventory_model.g.dart';
@HiveType()
class Inventory {
@HiveField(0)
final String name;
@HiveField(1)
final String description;
Inventory({this.name, this.description});
}
- First import hive 首次导入配置单元
Add
'inventory_model.g.dart'as a part (this is where the typeadapter is generated)添加
'inventory_model.g.dart'作为一部分(这是生成typeadapter的位置)Annotate the model class with
@HiveType(), so the generator knows this should be a TypeAdapter.使用
@HiveType()注释模型类,因此生成器知道这应该是TypeAdapter。Annotate each field you want to save with
@HiveField(index), the index is an int and each index should appear once and you shouldn't change it after registering them.用
@HiveField(index)注释要保存的每个字段,索引是一个int值,每个索引应该出现一次,并且在注册它们后就不要更改它。
Run the generator by typing the following command in the terminal:
通过在终端中键入以下命令来运行生成器:
$ flutter packages pub run build_runner build
After generating the type adapters, you may get an error about some missing implementations: Missing concrete implementation of 'getter TypeAdapter.typeId'. you can fix this by overriding the getter - typeId and returning an integer (also unique like HiveField) I use 0 in my case because this is my first type adapter.
生成类型适配器后,可能会遇到一些缺少的实现的错误: Missing concrete implementation of 'getter TypeAdapter.typeId' 。 您可以通过覆盖getter-typeId并返回一个整数(也像HiveField这样唯一的整数)来解决此问题,我使用0是因为这是我的第一个类型适配器。
@override
int get typeId => 0;
Finally, register the type adapter in your main function, just add it beneath your hive initialization.
最后,在主函数中注册类型适配器,只需将其添加到配置单元初始化下面即可。
Hive.registerAdapter(InventoryAdapter());
runApp(MyApp());
储存资料 (Storing Data)
To store data we’ll create a method in our ChangeNotifier class:
为了存储数据,我们将在ChangeNotifier类中创建一个方法:
addItem(Inventory item) async {
var box = await Hive.openBox<Inventory>('inventory');
box.add(item);
notifyListeners();
}
Here, we open the box and then use the add() method to append the data to our box. you can also use the put() method, the difference is with put() you have to specify the key for each value - box.put('key', item), but add() just gives each value an index and auto increments it. There's also box.putAll({'key': value}) which takes in a map, and box.addAll( ) which takes in a list, they are useful for adding multiple values at once.
在这里,我们打开盒子,然后使用add()方法将数据追加到我们的盒子中。 您还可以使用put()方法,不同之处在于put()必须为每个值指定键box.put('key', item) ,但是add()只是为每个值提供一个索引并自动增加它。 还有box.putAll({'key': value})获取地图,以及box.addAll( )获取列表,它们对于一次添加多个值很有用。
To use this in the UI:
要在用户界面中使用此功能,请执行以下操作:
inputItemDialog(BuildContext context, String action, [int index]) {
var inventoryDb = Provider.of<HomeModel>(context, listen: false);
showDialog(
context: context,
builder: (context) {
return Dialog(
child: Container(
padding: EdgeInsets.only(
left: 15,
right: 15,
top: 40,
),
height: 45.height,
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Text(
action == 'add' ? 'Add Item' : 'Update Item',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
SizedBox(
height: 10,
),
TextFormField(
controller: nameController,
validator: (value) {
if (value.isEmpty) {
return 'Item name cannot be empty';
}
return null;
},
decoration: InputDecoration(
labelText: 'Item name',
),
),
SizedBox(
height: 40,
),
TextFormField(
controller: descriptionController,
validator: (value) {
if (value.isEmpty) {
return 'Item description cannot be empty';
}
return null;
},
decoration: InputDecoration(
labelText: 'Item description',
),
),
SizedBox(
height: 40,
),
RaisedButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
if (action == 'add') {
await inventoryDb.addItem(Inventory(
name: nameController.text,
description: descriptionController.text,
));
} else {
}
nameController.clear();
descriptionController.clear();
inventoryDb.getInventory();
Navigator.pop(context);
}
},
color: Colors.green[600],
child: Text(
action == 'add' ? 'Add' : 'update',
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
),
SizedBox(
height: 20,
)
],
),
),
),
),
);
},
);
}
just replace the RaisedButton to that in the gist above.
只需将RaisedButton替换为上面要点中的按钮即可。
读取数据 (Reading data)
There are different ways to read data from your boxes:
有多种方法可以从包装箱中读取数据:
box.get('key)- get the value from a key.box.get('key)-从键中获取值。box.getAt(index)- get the value from an index created withbox.add().box.getAt(index)-从使用box.add()创建的索引中获取值。box.values- this returns an iterable containing all the items in the box.box.values返回一个包含该框中所有项目的iterable。
get the values by adding this to the change notifier class:
通过将其添加到更改通知程序类中来获取值:
List _inventoryList = <Inventory>[];
List get inventoryList => _inventoryList;
getItem() async {
final box = await Hive.openBox<Inventory>('inventory');
_inventoryList = box.values.toList();
notifyListeners();
}
You should probably understand the code by now.
您现在可能应该已经了解了代码。
Add this to the top of the build() method:
将其添加到build()方法的顶部:
context.watch<HomeModel>().getItem();
Replace the ListView.builder() in the UI with:
将UI中的ListView.builder()替换为:
ListView.builder(
itemCount: model.inventoryList.length,
itemBuilder: (context, index) {
Inventory inv = model.inventoryList[index];
return Container(
margin: EdgeInsets.symmetric(
vertical: 2.height,
horizontal: 4.5.width,
),
height: 13.height,
decoration: BoxDecoration(
color: Colors.white,
),
child: Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 2.width),
width: 16.width,
decoration: BoxDecoration(
color: Colors.green[600],
shape: BoxShape.circle,
),
child: Center(
child: Icon(
Icons.home,
color: Colors.white,
),
),
),
Padding(
padding: EdgeInsets.symmetric(
vertical: 3.height,
horizontal: 3.width,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
inv.name,
style: TextStyle(
color: Colors.green[600],
fontWeight: FontWeight.w600,
fontSize: 4.5.text,
),
),
SizedBox(
height: 1.height,
),
Text(
inv.description,
style: TextStyle(
color: Colors.grey,
fontSize: 4.text,
),
),
],
),
),
Padding(
padding: EdgeInsets.only(left: 8.width),
child: PopupMenuButton(
onSelected: (item) {
switch (item) {
case 'update':
nameController.text = inv.name;
descriptionController.text =
inv.description;
inputItemDialog(context, 'update', index);
break;
case 'delete':
model.deleteAt(index);
}
},
itemBuilder: (context) {
return [
PopupMenuItem(
value: 'update',
child: Text('Update'),
),
PopupMenuItem(
value: 'delete',
child: Text('Delete'),
),
];
},
),
),
],
),
);
},
)
Updating Items
更新项目
To update, there’s a popup menu button on each item card.
要更新,每个项目卡上都有一个弹出菜单按钮。
To update you can use box.put('key', newValue) to update that value, or box.putAt(index, item) which works like getAt().
要进行更新,您可以使用box.put('key', newValue)来更新该值,或者使用box.putAt(index, item)来类似于getAt() 。
In our change notifier class we add:
在更改通知程序类中,添加:
updateItem(int index, Inventory inventory) {
final box = Hive.box<Inventory>('inventory');
box.putAt(index, inventory);
notifyListeners();
}
删除项目 (Delete Items)
Deleting items is similar to getting:
删除项目类似于获取:
box.delete('key)- get the value from a key.box.delete('key)-从键中获取值。box.deleteAt(index)- get the value from an index created withbox.add().box.deleteAt(index)-从使用box.add()创建的索引中获取值。box.deleteAll(keys)- accepts an iterable of keys, and deletes all the keys given.box.deleteAll(keys)-接受一个可迭代的键,并删除所有给定的键。
There’s also a deleteFromDisk() method - this deletes the file where the box was created on the device
还有一个 deleteFromDisk() 方法-该方法删除设备上创建盒子的文件
deleteItem(int index) {
final box = Hive.box<Inventory>(_inventoryBox);
box.deleteAt(index);
getInventory();
notifyListeners();
}
懒人箱 (Lazy Boxes)
Lazy Boxes — Lazy boxes are useful for large apps or boxes with a lot of data because they don’t load all the data from disk at once. to use a lazy box:
懒惰盒—懒惰盒对于大型应用程序或包含大量数据的盒子非常有用,因为它们不会一次从磁盘加载所有数据。 使用一个懒惰的盒子:
final lazyBox = await Hive.openLazyBox('name');
var value = await lazyBox.get('key');
note the await keyword on get().
注意 get()上 的 await 关键字。
Remember, though Hive is really cool and useful, I feel you should use it if there are few or no relations in your data. That’s all for this article, I hope it helps! Need help with something? You can reach me on twitter @txe_bert.
记住,尽管Hive确实很酷而且有用,但我认为如果数据中很少或没有关系,则应该使用它。 这就是本文的全部内容,希望对您有所帮助! 需要帮助吗? 您可以通过twitter @txe_bert与我联系 。
hive 本地数据
所有评论(0)