Flutter本地数据存储指南:使用Isar数据库实现CRUD操作

本文详细介绍了如何在Flutter应用中集成Isar高性能NoSQL数据库,包括环境配置、数据模型定义、CRUD操作实现以及与UI的集成,帮助开发者构建高效的本地数据存储解决方案。

如何在Flutter中使用Isar本地存储数据

在构建Flutter应用程序时,高效管理本地数据至关重要。您需要一个轻量级、快速且易于集成的数据库,特别是在应用需要离线工作时。Isar就是这样一个数据库。它是一个专为Flutter定制的高性能、易用的NoSQL嵌入式数据库。具有响应式查询、索引、关系、迁移和事务等特性,Isar使本地数据持久化既强大又对开发者友好。

在本文中,您将学习如何将Isar集成到Flutter项目中,设置数据模型,并执行完整的CRUD(创建、读取、更新、删除)操作。为了使其更实用,您将构建一个简单的待办事项应用,允许用户创建、查看、更新和删除任务。

目录

  • 先决条件
  • 我们将构建什么
  • 如何在Flutter项目中设置Isar
  • 如何创建任务模型
  • 如何构建CRUD操作的数据仓库
  • 如何将CRUD集成到Flutter UI中
  • 超越CRUD:Isar的高级特性
  • 结论

先决条件

开始之前,请确保您具备以下条件:

  • 已安装Flutter SDK(建议版本3.0或以上)。使用以下命令检查您的版本:

    1
    
    flutter --version
    
  • Dart知识:熟悉Dart语法、类和异步编程。

  • Flutter基础:您应该知道如何设置Flutter项目、构建小部件,以及使用FutureBuilder或setState进行状态管理。

  • 代码编辑器:推荐使用VS Code或Android Studio。

如果这些都准备好了,我们就可以开始了。

我们将构建什么

我们将创建一个任务管理器应用,让用户能够:

  • 添加新任务。
  • 在列表中查看所有任务。
  • 更新现有任务。
  • 删除任务。

到最后,您将拥有一个使用Flutter和Isar构建的完全功能的CRUD应用。

如何在Flutter项目中设置Isar

步骤1:添加依赖

打开您的pubspec.yaml文件并添加以下内容:

1
2
3
4
5
6
7
8
9
dependencies:
  flutter:
    sdk: flutter
  isar: ^3.1.0
  isar_flutter_libs: ^3.1.0

dev_dependencies:
  isar_generator: ^3.1.0
  build_runner: any
  • isar: 核心Isar包。
  • isar_flutter_libs: Flutter集成所需。
  • isar_generator: 用于为模型生成代码。
  • build_runner: 运行代码生成器。

运行:

1
flutter pub get

步骤2:创建并初始化Isar

创建一个名为isar_setup.dart的文件。这将处理Isar数据库的打开。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';
import 'task.dart'; // 我们很快将创建此模型

late final Isar isar;

Future<void> initializeIsar() async {
  final dir = await getApplicationDocumentsDirectory();
  isar = await Isar.open(
    [TaskSchema],
    directory: dir.path,
  );
}

解释:

  • getApplicationDocumentsDirectory() 提供数据库文件的存储位置。
  • Isar.open() 初始化数据库并注册我们的Task模式。
  • late final Isar isar; 确保我们在初始化后可以全局访问数据库实例。

如何创建任务模型

现在让我们为任务定义数据模型。创建一个名为task.dart的文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import 'package:isar/isar.dart';

part 'task.g.dart';

@Collection()
class Task {
  Id id = Isar.autoIncrement; // 自动递增的主键

  late String name;

  late DateTime createdAt;

  Task(this.name) : createdAt = DateTime.now();
}

解释:

  • @Collection() 告诉Isar这个类代表一个数据库集合。
  • Id id = Isar.autoIncrement; 自动创建一个唯一标识符。
  • late String name; 存储任务名称。
  • late DateTime createdAt; 存储创建时间戳。
  • part 'task.g.dart'; 链接到生成的代码,该代码将在运行代码生成器后创建。

使用以下命令生成代码:

1
flutter pub run build_runner build

这将生成task.g.dart,其中包含必要的模式代码。

如何构建CRUD操作的数据仓库

创建一个名为task_repository.dart的新文件。这将包含与数据库交互的方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import 'package:isar/isar.dart';
import 'task.dart';
import 'isar_setup.dart';

class TaskRepository {
  Future<void> addTask(String name) async {
    final task = Task(name);
    await isar.writeTxn(() async {
      await isar.tasks.put(task);
    });
  }

  Future<List<Task>> getAllTasks() async {
    return await isar.tasks.where().findAll();
  }

  Future<void> updateTask(Task task) async {
    await isar.writeTxn(() async {
      await isar.tasks.put(task);
    });
  }

  Future<void> deleteTask(Task task) async {
    await isar.writeTxn(() async {
      await isar.tasks.delete(task.id);
    });
  }
}

解释:

  • addTask:创建新任务并保存。
  • getAllTasks:从数据库中读取所有任务。
  • updateTask:通过再次调用.put()来更新现有任务。
  • deleteTask:通过其id删除任务。
  • isar.writeTxn:确保操作在事务内运行,以保证安全性和一致性。

如何将CRUD集成到Flutter UI中

现在,让我们在main.dart中连接所有内容。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import 'package:flutter/material.dart';
import 'isar_setup.dart';
import 'task_repository.dart';
import 'task.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initializeIsar(); // 在runApp之前初始化Isar
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: TaskListScreen(),
    );
  }
}

class TaskListScreen extends StatefulWidget {
  @override
  _TaskListScreenState createState() => _TaskListScreenState();
}

class _TaskListScreenState extends State<TaskListScreen> {
  final TaskRepository _taskRepository = TaskRepository();
  late Future<List<Task>> _tasksFuture;

  @override
  void initState() {
    super.initState();
    _tasksFuture = _taskRepository.getAllTasks();
  }

  Future<void> _addTask() async {
    await _taskRepository.addTask('New Task');
    setState(() {
      _tasksFuture = _taskRepository.getAllTasks();
    });
  }

  Future<void> _deleteTask(Task task) async {
    await _taskRepository.deleteTask(task);
    setState(() {
      _tasksFuture = _taskRepository.getAllTasks();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Isar CRUD Example')),
      body: FutureBuilder<List<Task>>(
        future: _tasksFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          } else if (snapshot.hasError) {
            return Center(child: Text('Error: ${snapshot.error}'));
          } else {
            final tasks = snapshot.data ?? [];
            if (tasks.isEmpty) {
              return Center(child: Text('No tasks yet.'));
            }
            return ListView.builder(
              itemCount: tasks.length,
              itemBuilder: (context, index) {
                final task = tasks[index];
                return ListTile(
                  title: Text(task.name),
                  subtitle: Text('Created at: ${task.createdAt}'),
                  trailing: IconButton(
                    icon: Icon(Icons.delete),
                    onPressed: () => _deleteTask(task),
                  ),
                );
              },
            );
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _addTask,
        child: Icon(Icons.add),
      ),
    );
  }
}

解释:

  • initializeIsar():确保在应用运行前数据库已准备就绪。
  • _tasksFuture:保存任务列表的Future。
  • _addTask:添加新任务并刷新列表。
  • _deleteTask:删除任务并刷新列表。
  • FutureBuilder:当future完成时自动重建UI。
  • ListView.builder:动态显示所有任务。

这为您提供了一个使用Isar的简单而完整的CRUD应用。

超越CRUD:Isar的高级特性

一旦您熟悉了CRUD,Isar提供了高级工具来优化和扩展您的应用:

  • 响应式查询:您可以直接监听更改,而不是使用FutureBuilder。

    1
    
    final stream = isar.tasks.where().watch(fireImmediately: true);
    
  • 索引:通过索引字段提高查询性能。

    1
    2
    3
    4
    5
    6
    7
    
    @Collection()
    class Task {
      Id id = Isar.autoIncrement;
    
      @Index()
      late String name;
    }
    
  • 关系:将一个集合链接到另一个集合(例如,一个项目有多个任务)。

  • 自定义查询:执行复杂的过滤、排序和分页。

  • 迁移:随着应用的增长,安全地演进您的模式。

  • 批量操作:在一个事务中插入或更新多条记录。

结论

我们使用Isar构建了一个简单的Flutter待办事项应用,支持创建、读取、更新和删除任务。在此过程中,我们学习了如何:

  • 添加Isar依赖。
  • 使用注解定义模型。
  • 生成模式代码。
  • 在仓库中实现CRUD操作。
  • 将Isar连接到Flutter UI。

凭借其性能、对开发者友好的API和高级特性,Isar是Flutter应用中本地持久化的绝佳选择。

如需进一步学习,请查阅官方文档:

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计