Flutter基础——数据库与本地存储

Posted by lingjye on July 18, 2019

Shared Preferences

在 Flutter 中,可以使用Shared Preferences plugin来达到与NSUserDefaults相似的功能。它包含了 NSUserDefaluts 以及 Android 上等价的 SharedPreferences 的功能。

使用它需要将shared_preferences引入pubspec.yaml文件。

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
class SharedPreferenceSampleAppPage extends StatelessWidget {

  Future _incrementCounter() async {
    SharedPreferences pref = await SharedPreferences.getInstance();
    // ?? 等价于 iOS中的 ?: 
    int counter = (pref.getInt('counter') ?? 0) + 1;
    print('Pressed $counter times');
    await pref.setInt('counter', counter);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
       appBar: AppBar(
         title: Text('Shared Preferences Demo'),
       ),
       body: Center(
         child: RaisedButton(
           child: Text('Increment Counter'),
           onPressed: _incrementCounter,
         ),
       ),
    );
  }
}

如果报以下错误,需要重新打开工程:

1
MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)

Sqflite

另外,iOS中的CoreData(实质上也是对SQLite的封装),在Flutter中使用SQFlite来代替。

  • sqflite支持事务和批次处理
  • 打开期间自动版本管理
  • 支持插入/查询/更新/删除查询的助手
  • 支持在iOS和Android上的后台线程中执行数据库操作

Sqflite用法

首先需要导入sqflite.dart

1
import 'package:sqflite/sqflite.dart';

打开数据库

需要现货区数据库路径,这是Android上的默认数据库目录和为iOS的Documents目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 打开数据库
open() async {
  var databasesPath = await getDatabasesPath();
  String path = join(databasesPath, 'demo.db');
  print('path: $path');

  // 删除数据库
  // await deleteDatabase(path);

  db = await openDatabase(
    path,
    version: 1,
    onCreate: (Database database, int version) async {
      await database.execute(
        // sql语句 创建表
        'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)'
      );
    }
  );
}

关闭数据库

许多应用程序使用数据库期间不需要关闭它(当应用程序终止时它将被关闭)。如果要释放资源,可以关闭数据库。

1
await db.close();

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
insert() async {
  // 使用事务添加两条记录
  await db.transaction((txn) async {
    int id1 = await txn.rawInsert(
      'INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)'
    );
    print('inserted1:$id1');
    
    int id2 = await txn.rawInsert(
      'INSERT INTO Test(name, value, num) VALUES(?, ?, ?)', ['another name', 5678, 3.14]
    );
    print('inserted2:$id2');
  });
}

1
2
3
4
5
6
delete() async {
  int result = await db.rawDelete(
    'DELETE FROM Test WHERE name = ?', ['some name',]
  );
  print('delete result:$result');
}

1
2
3
4
5
6
update() async {
  int result = await db.rawUpdate(
    'UPDATE Test SET name = ?, VALUE = ? WHERE name = ?', ['updated name', '7890', 'another name']
  );
  print('update result:$result');
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
lookup() async {
  List<Map> list = await db.rawQuery(
    'SELECT * FROM Test'
  );
  print('All Datas: $list');

  // 指定条件
  List<Map> expectedList = await db.rawQuery(
    'SELECT * FROM Test WHERE name = ? ORDER BY id DESC', ['updated name',]
  );
  print('ExpectedList : $expectedList');

  // 查询count
  int count = Sqflite.firstIntValue(await db.rawQuery(
    'SELECT COUNT(*) FROM Test'
  ));
  print('count = $count');
}

批量支持:

1
2
3
4
5
batch = db.batch();
batch.insert('Test', {'name': 'item'});
batch.update('Test', {'name': 'new_item'}, where: 'name = ?', whereArgs: ['item']);
batch.delete('Test', where: 'name = ?', whereArgs: ['item']);
results = await batch.commit();

如果不关心操作结果,可以使用下面方法来提高性能:

1
await batch.commit(noResult: true);

在事务中,事务提交之前不会提交批处理:

1
2
3
4
5
6
7
8
9
10
11
await database.transaction((txn) async {
  var batch = txn.batch();
  
  // ...
  
  // commit but the actual commit will happen when the transaction is committed
  // however the data is available in this transaction
  await batch.commit();
  
  //  ...
});

默认情况下,批处理在遇到错误时会立即停止(通常会还原未提交的更改)。也可以使用下面方法来忽略错误(即使一个操作失败也会运行并提交每个成功的操作):

1
await batch.commit(continueOnError: true);

本文Demo