Flutter中持久化存储数据有多种方案, 一般常用的有 shared_preferences 和 sqfite

  • shared_preferences: 包含NSUserDefaults(在iOS上)和SharedPreferences(在Android上),为简单数据提供持久存储。数据以异步方式持久保存到磁盘。

  • sqflite: 是一款轻量级的关系型数据库,类似SQLite. 支持iOS和Android。适用于存储数据库 , 表类型的数据.

sqflite的使用

添加依赖:

作者所用版本为1.1.3

dependencies:  ...  sqflite: ^1.1.3

为了方便,外面创建一个DatabaseHelper来封装一些数据库的相关操作:

先贴一下代码 ,然后我们逐行分析:

/* * author: Created by 李卓原 on 2019/3/12. * email: zhuoyuan93@gmail.com * */import 'dart:async';import 'package:path/path.dart';import 'package:sale_aggregator_app/models/video.dart';import 'package:sqflite/sqflite.dart';class DatabaseHelper {  static final DatabaseHelper _instance = new DatabaseHelper.internal();  factory DatabaseHelper() => _instance;  final String tableVideo = 'VideoTable';  final String columnId = 'id';  final String image = 'image';  final String url = 'url';  final String duration = 'duration';  final String title = 'title';  final String favoriteStatus = 'favorite_status';  static Database _db;  DatabaseHelper.internal();  Future<Database> get db async {    if (_db != null) {      return _db;    }    _db = await initDb();    return _db;  }  initDb() async {    String databasesPath = await getDatabasesPath();    String path = join(databasesPath, 'flashgo.db');    var db = await openDatabase(path, version: 1, onCreate: _onCreate);    return db;  }  void _onCreate(Database db, int newVersion) async {    await db.execute(        'CREATE TABLE $tableVideo($columnId INTEGER PRIMARY KEY, $image TEXT, $url TEXT, $duration INTEGER, $title TEXT, $favoriteStatus TEXT)');  }  Future<int> insertVideo(Video video) async {    var dbClient = await db;    var result = await dbClient.insert(tableVideo, video.toJson());    return result;  }  Future<List> selectVideos({int limit, int offset}) async {    var dbClient = await db;    var result = await dbClient.query(      tableVideo,      columns: [columnId, image, url, duration, title, favoriteStatus],      limit: limit,      offset: offset,    );    List<Video> videos = [];    result.forEach((item) => videos.add(Video.fromSql(item)));    return videos;  }  Future<int> getCount() async {    var dbClient = await db;    return Sqflite.firstIntValue(        await dbClient.rawQuery('SELECT COUNT(*) FROM $tableVideo'));  }  Future<Video> getVideo(int id) async {    var dbClient = await db;    List<Map> result = await dbClient.query(tableVideo,        columns: [columnId, image, url, duration, title, favoriteStatus],        where: '$id = ?',        whereArgs: [id]);    if (result.length > 0) {      return Video.fromSql(result.first);    }    return null;  }  Future<int> deleteNote(String images) async {    var dbClient = await db;    return await dbClient        .delete(tableVideo, where: '$image = ?', whereArgs: [images]);  }  Future<int> updateNote(Video video) async {    var dbClient = await db;    return await dbClient.update(tableVideo, video.toJson(),        where: "$columnId = ?", whereArgs: [video.id]);  }  Future close() async {    var dbClient = await db;    return dbClient.close();  }}

可以看到我们在执行相关方法的时候都会先获得db, db会执行一个initDb方法,用于创建数据库和表

  initDb() async {    String databasesPath = await getDatabasesPath();    String path = join(databasesPath, 'flashgo.db');    var db = await openDatabase(path, version: 1, onCreate: _onCreate);    return db;  }    void _onCreate(Database db, int newVersion) async {    await db.execute(        'CREATE TABLE $tableVideo($columnId INTEGER PRIMARY KEY, $image TEXT, $url TEXT, $duration INTEGER, $title TEXT, $favoriteStatus TEXT)');  }
  • getDatabasesPath() : 获取默认数据库位置(在Android上,它通常是data/data//databases,在iOS上,它是Documents目录)
  • join(databasesPath, ‘flashgo.db’): 相当于在上述方法获取到的位置创建了一个名为flashgo的数据库.
  • openDatabase: 按指定路径打开数据库 , 路径就是上面flash.db的路径,version为数据库版本号,onCreate是创建表的方法

然后是我定义的一个实体类,看一下代码:

class Video {  int id;  String image;  String url;  int duration;  String title;  bool favoriteStatus;  Video(      {this.id,      this.image,      this.url,      this.duration,      this.title,      this.favoriteStatus});  Video.fromJson(Map json) {    id = json['id'];    image = json['image'];    url = json['url'];    duration = json['duration'];    title = json['title'];    favoriteStatus = json['favorite_status'];  }  Video.fromSql(Map json) {    id = json['id'];    image = json['image'];    url = json['url'];    duration = json['duration'];    title = json['title'];    favoriteStatus = json['favorite_status'] == 'true';  }  Map toJson() {    final Map data = new Map();    data['id'] = this.id;    data['image'] = this.image;    data['url'] = this.url;    data['duration'] = this.duration;    data['title'] = this.title;    data['favorite_status'] = this.favoriteStatus;    return data;  }}

细心的同学可能看到了一个与众不同的方法: fromSql ,
其中的json['favorite_status'] 是字符串类型, 为什么不依然用bool型呢, 因为sqlite不支持bool型.

上文中,CREATE TABLE $tableVideo($columnId INTEGER PRIMARY KEY, $image TEXT, $url TEXT, $duration INTEGER, $title TEXT, $favoriteStatus TEXT) 这个建表方法可以看到
id用的是integer, 其他都是用的text.

我们看一下sqlite都支持哪些数据类型吧:


sql存储数据类型

每个存储在 SQLite 数据库中的值都具有以下存储类之一:

存储类 描述
NULL 值是一个 NULL 值。
INTEGER 值是一个带符号的整数,根据值的大小存储在 1、2、3、4、6 或 8 字节中。
REAL 值是一个浮点值,存储为 8 字节的 IEEE 浮点数字。
TEXT 值是一个文本字符串,使用数据库编码(UTF-8、UTF-16BE 或 UTF-16LE)存储。
BLOB 值是一个 blob 数据,完全根据它的输入存储。

数据库和表已经准备就绪了,那么该看一看它的增删改查了

插入数据
  Future insertVideo(Video video) async {    var dbClient = await db;    var result = await dbClient.insert(tableVideo, video.toJson());    return result;  }

这里是我封装的一个插入数据的方法,参数是video的对象, 但是可以看到insert方法第二个参数是一个json数据,所以其实也可以直接传递json数据.而不是传一个对象再转成json.

查询方法

Future selectVideos({int limit, int offset}) async {    var dbClient = await db;    var result = await dbClient.query(      tableVideo,      columns: [columnId, image, url, duration, title, favoriteStatus],      limit: limit,      offset: offset,    );    List

主要是调用了query方法,先看一下源码:

Future>> query(String table,      {bool distinct,      List columns,      String where,      List whereArgs,      String groupBy,      String having,      String orderBy,      int limit,      int offset});

有一个必传参数是表名,
然后有很多修饰,
比如
limit : 是要查询多少条数据,
offset :是从哪里开始查.
columns: 是要查询哪几列
where: 是查询条件,这里我是查询所有的所以没有设置.

limit 和offset 这两个也是最常用的属性,所以我封装方法的时候允许设置这两个参数.
如果你有多张表,多个列需要查询,我建议各自封装方法,不然的话,需要传入的参数过于复杂便失去了封装的意义.

这里的查询方法返回的是json数据,且要记住,是只有integer和text类型的,所以想要bool一定要自己处理

List

所以这里,我新建了一个fromSql的方法,把查询出来的json数据转成我想要的类型的对象.

查询单个
  Future

思路同上,只是要多了一个where,即查询条件,这里我是根据id来查 所以只传入了一个id参数.
返回查询到的结果(json类型) .
如果查询不到,则返回一个null.

更改数据

  Future updateVideo(Video video) async {    var dbClient = await db;    return await dbClient.update(tableVideo, video.toJson(),        where: "$columnId = ?", whereArgs: [video.id]);  }

这个代码的逻辑是

  1. 传入更改后的数据
  2. 根据传入的数据id找到对应数据
  3. 更新数据

删除数据

  Future deleteVideo(String images) async {    var dbClient = await db;    return await dbClient        .delete(tableVideo, where: '$image = ?', whereArgs: [images]);  }

其实逻辑和查询是一样的,我这是根据image来查找并删除.也可以用id或者其他数据.

获取数据的数量

  Future getCount() async {    var dbClient = await db;    return Sqflite.firstIntValue(        await dbClient.rawQuery('SELECT COUNT(*) FROM $tableVideo'));  }

此方法用来查询表中有多少条数据.
这我用了rawQuery方法, 它是支持直接使用sql语句进行查询的.
因为该结果返回一个列表,所以使用Sqflite.firstIntValue来获取其中的第一个值.

关闭方法

  Future close() async {    var dbClient = await db;    return dbClient.close();  }

在操作执行完毕后 , 记得关闭数据库.关闭之后无法再访问数据库.

以上是对代码的分析,下面看一下实际的使用:

  //把视频列表存到数据库以备用  void saveVideos(List

相关代码尽在github

更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. 一句话锁定MySQL数据占用元凶
  3. AndroidStudio 优秀的第三方数据库 GreenDao
  4. android在程序中打开另一个程序
  5. Android(安卓)百度地图使几点始终在合适的屏幕范围内显示
  6. Android(安卓)在PreferenceActivity 中移除一个Preference
  7. Android(安卓)5.0 如何正确启用isLoggable(二)__原理分析
  8. java做服务器,android做客户端,实现数据传输
  9. Android(安卓)基于DataBinding的通用RecyclerView Adapter

随机推荐

  1. Android官方资料--Adoptable Storage
  2. android dialog style属性设置
  3. 关于android的monkey测试
  4. Android Pid和Uid
  5. Android(安卓)ANR
  6. Android内存泄漏调试教程
  7. android stackView
  8. Android Hello World on IDEA
  9. Android TabHost风格
  10. Sending and receiving broadcast messag