每天一个Flutter开发小项目 (7) : 掌握Flutter数据持久化 - 构建你的简易笔记应用

引言

再次欢迎回到 每天一个Flutter开发小项目 系列博客!在之前的六篇博客中,我们逐步解锁了 Flutter 移动开发的各项核心技能,从 UI 构建、交互设计、状态管理到页面导航,您已然具备了构建功能丰富 Flutter 应用的基础。

然而,我们之前构建的应用大多还停留在“一次性”使用的阶段,应用数据无法在应用关闭后保存,这显然无法满足真实应用的需求。在实际开发中,数据持久化是至关重要的一环,它使得应用能够记住用户的操作、保存用户的创作,真正发挥应用的价值。今天,我们将聚焦 Flutter 应用的 “记忆力” —— 数据持久化,并构建一个每个人都需要的 简易笔记应用,让您掌握 Flutter 应用数据本地存储的专业技巧。

通过本篇博客,您将深入学习:

  • Flutter 数据持久化的核心概念: 理解数据持久化的重要性,掌握 Flutter 中实现数据持久化的各种方案。
  • sqflite 数据库的专业应用: 深入学习 sqflite 插件,掌握在 Flutter 应用中使用 SQLite 数据库进行数据持久化的全流程,包括数据库创建、表设计、CRUD 操作等。
  • 本地数据库CRUD操作: 熟练掌握使用 sqflite 进行 SQLite 数据库的 增 (Create)、删 (Delete)、改 (Update)、查 (Read) 操作,构建完整的数据管理功能。
  • 异步数据库操作的最佳实践: 理解 Flutter 中数据库操作的异步特性,掌握异步编程技巧,确保应用流畅性。
  • 简易笔记应用的功能实现: 构建一个功能完善的简易笔记应用,包括笔记创建、查看、编辑、删除、列表展示等核心功能。
  • 应用数据管理的专业技能: 从数据模型设计到数据库操作,全面提升 Flutter 应用数据管理的专业技能。

项目简介: 简易笔记应用

我们的简易笔记应用将围绕以下核心功能展开:

  • 创建新笔记: 用户可以创建新的笔记,并输入笔记标题和内容。
  • 查看笔记列表: 应用主页以列表形式展示所有已创建的笔记,方便用户浏览和查找。
  • 查看笔记详情: 点击笔记列表项,可以进入笔记详情页面,查看完整的笔记内容。
  • 编辑现有笔记: 在笔记详情页面,用户可以编辑笔记的标题和内容,并保存修改。
  • 删除笔记: 在笔记列表或详情页面,用户可以删除不再需要的笔记。
  • 数据持久化: 所有笔记数据都将持久化存储在本地数据库中,应用重启后数据不会丢失。

通过构建简易笔记应用,我们将重点实践:

  • sqflite 数据库集成: 在 Flutter 应用中集成 sqflite 插件,搭建本地数据库环境。
  • 数据库表设计: 设计合理的数据库表结构,存储笔记数据 (例如,笔记ID、标题、内容、创建时间等字段)。
  • CRUD 操作实现: 使用 sqflite 插件实现笔记数据的增删改查操作,包括数据库查询、数据插入、数据更新、数据删除等。
  • 异步数据库操作: 使用 async/await 关键字处理数据库操作的异步任务,确保 UI 线程不被阻塞。
  • 简易笔记应用完整功能实现: 从 UI 界面到数据逻辑,完整构建一个实用的简易笔记应用。

Flutter 数据持久化方案概览

在深入 sqflite 之前,我们先来概览 Flutter 中常用的数据持久化方案,以便您对 Flutter 数据存储有更全面的了解。Flutter 提供了多种数据持久化方案,适用于不同的数据存储需求:

  • shared_preferences: 轻量级的键值对存储方案,适用于存储少量的应用配置数据、用户偏好设置等简单数据。shared_preferences 以键值对的形式将数据存储在设备的本地存储中,例如,存储用户的登录状态、主题设置、应用语言等。 优点: 简单易用,快速上手。 缺点: 只适合存储少量简单数据,不支持复杂数据结构和查询操作。

  • sqflite 数据库: SQLite 数据库的 Flutter 插件,适用于存储结构化数据,支持关系型数据库的各种操作,例如,表创建、数据查询、事务处理等。sqflite 将数据存储在设备本地的 SQLite 数据库文件中。优点: 功能强大,支持复杂数据结构和查询操作,性能较好,适用于存储中等规模的结构化数据。 缺点: 相对于 shared_preferences 学习曲线稍陡峭,需要了解 SQL 数据库的基本知识。

  • path_provider: 文件路径提供器插件,用于获取设备文件系统中常用目录的路径,例如,应用文档目录、临时目录等。path_provider 本身不提供数据存储功能,但可以与其他数据存储方案 (例如,文件读写、JSON 文件存储、数据库存储等) 结合使用,指定数据存储的路径。优点: 方便获取设备文件系统路径,与各种数据存储方案灵活搭配。 缺点: 本身不提供数据存储功能,需要与其他方案结合使用。

  • 文件读写: Flutter 提供了 dart:io 库,可以进行文件读写操作,将数据存储到本地文件中,例如,文本文件、JSON 文件、图片文件等。优点: 灵活性高,可以存储各种类型的文件数据。 缺点: 需要手动处理文件读写操作,较为繁琐,不适合存储结构化数据。

  • NoSQL 数据库 (例如,Firebase Firestore, MongoDB Realm): NoSQL 数据库的 Flutter SDK,适用于存储非结构化数据文档型数据,支持云端同步实时数据更新 等高级功能。NoSQL 数据库通常将数据存储在云端数据库服务中。 优点: 功能强大,支持复杂数据结构和查询,支持云端同步和实时更新,适用于构建需要云端数据同步的应用。 缺点: 需要依赖云服务,学习曲线较陡峭,通常用于构建更大型、复杂的应用。

  • 对象关系映射 (ORM) 库 (例如,drift): ORM 库 可以简化数据库操作,将数据库表映射为 Dart 对象,通过操作 Dart 对象来操作数据库,提高开发效率。优点: 简化数据库操作,提高开发效率,代码更简洁易读。 缺点: 学习曲线较陡峭,可能会引入额外的性能开销。

在本篇博客中,我们选择 sqflite 数据库 来构建简易笔记应用的数据持久化方案。sqflite 数据库功能强大,性能良好,能够满足简易笔记应用的数据存储需求,同时也是 Flutter 应用开发中常用的数据持久化方案。

实战步骤: 构建简易笔记应用

接下来,我们将一步步使用 sqflite 数据库构建我们的简易笔记应用。

步骤 1: 创建新的 Flutter 项目并添加 sqflite 依赖

首先,创建一个新的 Flutter 项目,命名为 simple_notes_app

然后在 pubspec.yaml 文件中添加 sqflitepath_provider 依赖:

dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.0.0 #  使用最新版本,请查阅 pub.dev 获取最新版本号
  path_provider: ^2.0.0 # 使用最新版本,请查阅 pub.dev 获取最新版本号

运行 flutter pub get 命令获取依赖。

步骤 2: 定义笔记数据模型 (Note)

我们需要定义一个 Note 类来表示笔记数据,包含笔记ID、标题、内容、创建时间等信息。

创建 lib/models/note.dart 文件,定义 Note 类:

class Note {
   
  final int? id; //  笔记ID,自增长,可为空,数据库生成
  final String title; //  笔记标题
  final String content; //  笔记内容
  final DateTime createdAt; //  创建时间

  const Note({
    //  使用 const 构造函数
    this.id,
    required this.title,
    required this.content,
    required this.createdAt,
  });

  Map<String, dynamic> toMap() {
    //  将 Note 对象转换为 Map<String, dynamic>,方便数据库操作
    return {
   
      'id': id,
      'title': title,
      'content': content,
      'createdAt': createdAt.millisecondsSinceEpoch, //  将 DateTime 转换为毫秒时间戳存储
    };
  }

  static Note fromMap(Map<String, dynamic> map) {
    //  从 Map<String, dynamic>  创建 Note 对象,方便从数据库读取数据
    return Note(
      id: map['id'],
      title: map['title'],
      content: map['content'],
      createdAt: DateTime.fromMillisecondsSinceEpoch(map['createdAt']), //  从毫秒时间戳创建 DateTime 对象
    );
  }
}

代码解释:

  • Note: 定义了 Note 类,包含 id (笔记ID), title (标题), content (内容), createdAt (创建时间) 等属性。
  • int? id: id 属性为 int? 类型,表示笔记 ID,允许为空。 数据库自增长的 ID 在插入数据时通常为空,由数据库自动生成。
  • DateTime createdAt: createdAt 属性为 DateTime 类型,表示笔记创建时间。
  • toMap() 方法: 将 Note 对象转换为 Map<String, dynamic> 类型,方便进行数据库插入、更新等操作。 createdAt.millisecondsSinceEpochDateTime 对象转换为毫秒时间戳,以便在数据库中存储。
  • fromMap(Map<String, dynamic> map) 方法: 静态方法,从 Map<String, dynamic> 类型的数据创建 Note 对象,方便从数据库读取数据后转换为 Note 对象。 DateTime.fromMillisecondsSinceEpoch(map['createdAt']) 从毫秒时间戳创建 DateTime 对象。

步骤 3: 创建数据库助手类 (DatabaseHelper)

我们需要创建一个数据库助手类 DatabaseHelper 来封装数据库操作,包括数据库初始化、表创建、CRUD 操作等。

创建 lib/helpers/database_helper.dart 文件,定义 DatabaseHelper 类:

import 'package:path/path.dart'; //  导入 path 插件
import 'package:sqflite/sqflite.dart'; //  导入 sqflite 插件
import 'package:path_provider/path_provider.dart'; // 导入 path_provider 插件

import '../models/note.dart'; // 导入 Note 类

class DatabaseHelper {
   
  static const _databaseName = "NotesDatabase.db"; //  数据库名称
  static const _databaseVersion = 1; //  数据库版本号
  static const tableNotes = 'notes'; //  笔记表名称

  DatabaseHelper._privateConstructor(); //  私有构造函数,单例模式
  static final DatabaseHelper instance = DatabaseHelper._privateConstructor(); //  单例模式实例

  static Database? _database; //  数据库实例

  Future<Database> get database async {
    //  获取数据库实例,单例模式
    if (_database != null) return _database!; //  如果数据库实例已存在,直接返回
    _database = await _initDatabase(); //  如果数据库实例不存在,初始化数据库
    return _database!;
  }

  _initDatabase() async {
    //  初始化数据库
    Directory documentsDirectory = await getApplicationDocumentsDirectory(); //  获取应用文档目录
    String path = join(documentsDirectory.path, _databaseName); //  拼接数据库文件路径
    return await openDatabase( //  打开数据库
      path,
      version: _databaseVersion,
      onCreate: _onCreate, //  数据库首次创建时回调
    );
  }

  Future _onCreate(Database db, int version) async {
    //  数据库首次创建时回调
    await db.execute(''' //  执行 SQL 创建表语句
      CREATE TABLE $tableNotes (
        id INTEGER PRIMARY KEY AUTOINCREMENT, //  ID,自增长主键
        title TEXT NOT NULL, //  标题,非空
        content TEXT NOT NULL, //  内容,非空
        createdAt INTEGER NOT NULL //  创建时间,非空,存储毫秒时间戳
      )
      ''');
  }

  Future<int> insertNote(Note note) async {
    //  插入笔记
    Database db = await instance.database; //  获取数据库实例
    return await db.insert(tableNotes, note.toMap()); //  插入数据,返回插入记录的 ID
  }

  Future<List<Note>> getAllNotes() async {
    //  查询所有笔记
    Database db = await instance.database; //  获取数据库实例
    final List<Map<String, dynamic>> maps = await db.query(tableNotes, orderBy: 'createdAt DESC'); //  查询所有数据,按创建时间倒序排列
    if (maps.isEmpty) {
    //  如果查询结果为空,返回空列表
      return [];
    }
    return List.generate(maps.length, (i) {
    //  将 List<Map<String, dynamic>>  转换为 List<Note>
      return Note.fromMap(maps[i]);
    });
  }

  Future<Note?> getNoteById(int id) async {
    //  根据 ID 查询笔记
    Database db = await instance.database; //  获取数据库实例
    List<Map<String, dynamic>> maps = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Neo Evolution

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值