In native Android, some lightweight data (such as user login information, APP configuration information, etc.) will be written into SharedPreferences for storage, while in iOS, NSUserDefaults will be used for storage. Sqlite will be used to realize the requirement of adding, deleting, modifying and searching large quantities of data. There are also officially maintained plug-ins for Flutter to implement these functions.

Simple data persistence

Saving data to local disks is a common function of application programs, such as saving user login information and user configuration information. This information is usually saved using shared_preferences, which stores data as key-value pairs for Android and iOS. Shared_preferences is a third-party plugin that uses NSUserDefaults in iOS and SharedPreferences in Android.

Add the dependent

Add dependencies to the project’s pubspec.yaml file:

Dependencies: shared_preferences: ^ 2.0.10Copy the code

Execute command:

flutter pub get
Copy the code

Shared_preferences Supports the data types int, Double, bool, String, and stringList.

The sample code

import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class SaveDataPage extends StatefulWidget { @override State<StatefulWidget> createState() => SaveDataState(); } class SaveDataState extends State { final _textFieldController = TextEditingController(); var _storageString = ''; final SAVE_KEY = 'storage_key'; Future saveString() Async {SharedPreferences SharedPreferences = await SharedPreferences.getInstance(); sharedPreferences.setString( SAVE_KEY, _textFieldController.value.text.toString()); Future getString() Async {SharedPreferences SharedPreferences = await SharedPreferences.getInstance(); setState(() { if(sharedPreferences.getString(SAVE_KEY) ! = null) { _storageString = sharedPreferences.getString(SAVE_KEY)! ; }}); } @override Widget build(BuildContext context) {return Scaffold(appBar: appBar (title: const Text(' data store '),), body: Column(children: <Widget>[const Text("shared_preferences store ", textAlign: textalign.center), TextField(Controller: Textfieldcontroller,), MaterialButton(onPressed: saveString, Child: const Text(" store "), color: Color.pink,), MaterialButton(onPressed: getString, Child: const Text(" fetch "), color: LightGreen,), Text('shared_preferences store = $_storageString'),],),); }}Copy the code

The actual effect

Persistence of large amounts of complex data

To have a large amount of data to add, delete, change, check the demand, we thought of the database Sqlite. The database in Flutter is called Sqflite, not Sqlite. Sqflite is an Android and iOS database.

Add the dependent

Sqflite: ^ 2.0.1Copy the code

Execute command:

flutter pub get
Copy the code

Introduction to Usage

Insert data

There are two ways to insert data:

Future<int> insert(String table, Map<String, Object? > values, {String? nullColumnHack, ConflictAlgorithm? conflictAlgorithm}); Future<int> rawInsert(String sql, [List<Object?>? arguments]);Copy the code
  • insertMethod the first parameter is the name of the table to be operated on, the second parameter map is the name and value of the field to be added, and the third parameter is the solution in case of a conflict. An example of an insert given by the official:
var value = {
  'age': 18,
  'name': 'Candy'
};
int id = await db.insert(
  'table',
  value,
  conflictAlgorithm: ConflictAlgorithm.replace,
);
Copy the code
  • rawInsertMethod takes an insert as the first argumentsqlStatement, the second parameter represents the fill data. An example of an insert given by the official:
Int id1 = await database.rawInsert('INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)');Copy the code

Query data

Querying data provides two methods:

Future<List<Map<String, Object? >>> query(String table, {bool? distinct, List<String>? columns, String? where, List<Object? >? whereArgs, String? groupBy, String? having, String? orderBy, int? limit, int? offset}); Future<List<Map<String, Object? >>> rawQuery(String sql, [List<Object?>? arguments]);Copy the code
  • queryThe first parameter of the method is the table name of the operation, followed by optional parameters indicating whether to de-duplicate, query field, WHERE clause, where clause placeholder parameter value, how to group, which row groups to contain, how to sort, the number of rows to query, and the offset bit to query. All but the table names and query fields are non-mandatory. Official query examples:
List<Map> maps = await db.query(tableTodo, columns: ['columnId', 'columnDone', 'columnTitle'], where: 'columnId = ? ', whereArgs: [id]);Copy the code
  • rawQueryThe first parameter to the method is a query SQL statement. Official query examples:
List<Map> list = await database.rawQuery('SELECT * FROM Test');
Copy the code

Update the data

Update the data in the database and return the number of changes. There are also two methods provided here.

Future<int> rawUpdate(String sql, [List<Object?>? arguments]); Future<int> update(String table, Map<String, Object? > values, {String? where, List<Object? >? whereArgs, ConflictAlgorithm? conflictAlgorithm});Copy the code
  • rawUpdateThe first parameter of the method is an update SQL statement and the second parameter represents the updated data. Official query examples:
int count = await database.rawUpdate( 'UPDATE Test SET name = ? , value = ? WHERE name = ? ', ['updated name', '9876', 'some name']);Copy the code
  • updateMethod the first parameter is the name of the operation table, the second parameter is the modified field and the corresponding value, followed by the WHERE statement, where clause placeholder parameter value, and the resolution of the conflict. Official query examples:
int count = await db.update(tableTodo, todo.toMap(), where: '$columnId = ? ', whereArgs: [todo.id]);Copy the code

Delete the data

There are also two ways to delete data, returning the number of deletions.

Future<int> rawDelete(String sql, [List<Object?>? arguments]); Future<int> delete(String table, {String? where, List<Object? >? whereArgs});Copy the code
  • rawDeleteThe first parameter of the method is a delete SQL statement and the second parameter represents the fill data. Official query examples:
int count = await database.rawDelete('DELETE FROM Test WHERE name = ? ', ['another name']);Copy the code
  • deleteThe first argument to the method is the table name of the operation, followed by optional arguments representing the WHERE clause and where clause placeholder parameter values. Official query examples:
int count = await db.delete(tableTodo, where: 'columnId = ?', whereArgs: [id]);
Copy the code

For example

Defines a Person object, through the encapsulation database definition of a class DBProvider to achieve the increase, delete, change, search.

Create SQLite in singleton mode

SQLite static final dbprovider_singleton = dbprovider._internal (); factory DBProvider() { return _singleton; } DBProvider._internal();Copy the code

Complete create database, including add, delete, change, check the code

import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; SQLite static final DBProvider _singleton = dbprovider._internal (); factory DBProvider() { return _singleton; } DBProvider._internal(); static Database? _db; Future<Database> get db async { if (_db ! = null) { return _db! ; } _db = await _initDB(); return _db! ; } Future<Database> _initDB() async {// Get Directory documentsDirectory = await for Database files getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, 'demo.db'); Return await openDatabase(path, version: 1, onCreate: _onCreate); } // create a Database table Future _onCreate(Database db, int version) async { return await db.execute(''' CREATE TABLE $tablePerson ( $columnId INTEGER PRIMARY KEY, $columnName TEXT, $columnSex TEXT, $columnAge INTEGER, '''); Future<Person> insert(Person Person) async {person.id = await _db! .insert(tablePerson, person.toMap()); return person; } Future<List<Person>? > queryAll() async { List<Map> maps = await _db! .query(tablePerson, columns: [ columnId, columnName, columnSex, columnAge ]); if (maps.isEmpty) { return null; } List<Person> books = []; for (int i = 0; i < maps.length; i++) { books.add(Person.fromMap(maps[i])); } return books; } Future<Person? > getBook(int id) async { List<Map> maps = await _db! .query(tablePerson, columns: [ columnId, columnName, columnSex, columnAge ], where: '$columnId = ? ', whereArgs: [id]); if (maps.isNotEmpty) { return Person.fromMap(maps.first); } return null; Future<int> delete(int ID) async {return await _db! .delete(tablePerson, where: '$columnId = ?', whereArgs: [id]); Future<int> Update (Person Person) async {return await _db! .update(tablePerson, person.toMap(), where: '$columnId = ? ', whereArgs: [person.id]); }}Copy the code

Code for the Person class

const String tablePerson = 'person'; const String columnId = '_id'; const String columnName = 'name'; const String columnSex = 'sex'; const String columnAge = 'age'; class Person { int? id; String? name; String? sex; int? age; Person({required this.id, required this.name, required this.sex, required this.age}); Map<String, dynamic> toMap() { var map = <String, dynamic>{ columnName: name, columnSex: sex, columnAge: age }; map[columnId] = id; return map; } Person.fromMap(Map<dynamic, dynamic> map) { id = map[columnId]; name = map[columnName]; sex = map[columnSex]; age = map[columnAge]; }}Copy the code

The basic usage of SQLite has been described above. Adding, deleting, modifying, and querying data is frequently used. SQLite also has some other uses that can be extended according to business requirements.