[Flutter actual combat] a large amount of complex data persistence

Lao Meng's Guide : The previous article explained the file directory system of Android and iOS. This article explained how to use SQLite to save data. Contributions are welcome : http://laomengit.com/plan/Contribution.html

Saving data locally is one of the very important functions of the application, such as the following scenario: a news or blog application, and enter the homepage after opening it. If there is no data saved locally, you need to obtain the data through the network before returning the data , The user sees a blank page, and if part of the news is saved locally, this part of the data will be displayed, and it can be refreshed when the latest data is returned. For the user experience, the second experience is obviously better.

SQLite is currently one of the most popular local storage frameworks. This article will introduce how to use SQLite to save, query, and delete data.

SQLite pub address: https://pub.flutter-io.cn/packages/sqflite

SQLite Github:https://github.com/tekartik/sqflite

SQLite explanation: https://www.sqlitetutorial.net/

If you have a bit of knowledge about databases and SQL statements, the following explanations will be easier to understand. If you don’t know anything about databases and SQL statements, the following explanations may be difficult to understand.

Do you need to learn about databases and SQL statements first? I don’t think it is necessary. The database and SQL statement related knowledge used by the application is very basic, such as opening the database, creating a table, SQL query statement, update statement, delete statement, etc., these are all fixed formats, remember the fixed statements OK.

Add dependency

SQLite is not the system comes with Flutter, but a third-party plug-in project pubspec.yamlto add a dependency file:

dependencies:
  sqflite: ^1.3.1
  path_provider: ^1.6.11

Excuting an order:

flutter pub get

When using SQLite to create a database, the local path is required as a parameter, so add a path_providerplugin to get the local path.

Singleton mode to create SQLite access

The use of SQLite does not necessarily use the singleton mode, the singleton mode is to ensure that the entire application has only one database instance and global access.

class DBProvider{

  static final DBProvider _singleton = DBProvider._internal();

  factory DBProvider() {
    return _singleton;
  }

  DBProvider._internal();
}

Initialize the database

import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';

class DBProvider {
  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 {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path, 'dbName');
    return await openDatabase(path,
        version: 1, onCreate: _onCreate, onUpgrade: _onUpgrade);
  }

  ///
  /// 创建Table
  ///
  Future _onCreate(Database db, int version) async {}

  ///
  /// 更新Table
  ///
  Future _onUpgrade(Database db, int oldVersion, int newVersion) async {}
}

Create Table, Table represents a table, create a user table below, the table column has id (unique identifier), name (name), age (age), sex (gender).

///
/// 创建Table
///
Future _onCreate(Database db, int version) async {
  return await db.execute("CREATE TABLE User ("
      "id integer primary key AUTOINCREMENT,"
      "name TEXT,"
      "age TEXT,"
      "sex integer"
      ")");
}

save data

First create a User Model class for data storage:

class User {
  int id;
  String name;
  int age;
  int sex;

  User({this.id, this.name, this.age, this.sex});

  User.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    name = json['name'];
    age = json['age'];
    sex = json['sex'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['id'] = this.id;
    data['name'] = this.name;
    data['age'] = this.age;
    data['sex'] = this.sex;
    return data;
  }
}

save data:

Future saveData(User user) async {
  var _db = await db;
  return await _db.insert('User', user.toJson());
}

Case: Enter name, age, gender, click save

class _AddUser extends StatefulWidget {
  @override
  __AddUserState createState() => __AddUserState();
}

class __AddUserState extends State<_AddUser> {
  String _radioGroupValue = '0';
  TextEditingController _nameController;
  TextEditingController _ageController;

  @override
  void initState() {
    super.initState();
    _nameController = TextEditingController();
    _ageController = TextEditingController();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('保存数据'),
      ),
      body: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              Text('姓名:'),
              Flexible(
                child: TextField(
                  controller: _nameController,
                ),
              ),
            ],
          ),
          Row(
            children: <Widget>[
              Text('年龄:'),
              Flexible(
                child: TextField(
                  controller: _ageController,
                ),
              ),
            ],
          ),
          Row(
            children: <Widget>[
              Text('性别:'),
              Flexible(
                child: RadioListTile(
                  title: Text('男'),
                  value: '0',
                  groupValue: _radioGroupValue,
                  onChanged: (value) {
                    setState(() {
                      _radioGroupValue = value;
                    });
                  },
                ),
              ),
              Flexible(
                child: RadioListTile(
                  title: Text('女'),
                  value: '1',
                  groupValue: _radioGroupValue,
                  onChanged: (value) {
                    setState(() {
                      _radioGroupValue = value;
                    });
                  },
                ),
              ),
            ],
          ),
          Builder(
            builder: (context) {
              return RaisedButton(
                child: Text('保存'),
                onPressed: () async {
                  var user = User(
                      name: '${_nameController.text}',
                      age: int.parse('${_ageController.text}'),
                      sex: int.parse('$_radioGroupValue'));

                  int result = await DBProvider().saveData(user);
                  if (result > 0) {
                    Scaffold.of(context).showSnackBar(SnackBar(
                      content: Text('保存数据成功,result:$result'),
                    ));
                  } else {
                    Scaffold.of(context).showSnackBar(SnackBar(
                      content: Text('保存数据失败,result:$result'),
                    ));
                  }
                },
              );
            },
          )
        ],
      ),
    );
  }
}

Use SQL statements to save data:

Future rawInsert(User user) async {
  var _db = await db;
  return await _db.rawInsert(
      'INSERT Into User (name,age,sex) VALUES (?,?,?)',[user.name,user.age,user.sex]);
}

Query data

Query all data:

Future<List<User>> findAll() async {
    var _db = await db;
    List<Map<String, dynamic>> result = await _db.query('User');

    return result.isNotEmpty ? result.map((e) {
      return User.fromJson(e);
    }).toList():[];
  }

Display the queried data on the table:

class DatabaseDemo extends StatefulWidget {
  @override
  _DatabaseDemoState createState() => _DatabaseDemoState();
}

class _DatabaseDemoState extends State<DatabaseDemo> {
  List<User> _list = [];

  @override
  void initState() {
    super.initState();
    _loadData();
  }

  _loadData() async {
    _list = await DBProvider().findAll();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Sqlite Demo'),
      ),
      body: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              RaisedButton(
                child: Text('查询数据'),
                onPressed: (){
                  _loadData();
                },
              ),
              RaisedButton(
                child: Text('添加数据'),
                onPressed: (){
                  Navigator.of(context).push(MaterialPageRoute(builder: (context){
                    return _AddUser();
                  }));
                },
              ),
            ],
          ),
          Table(
            children: [
              TableRow(children: [
                TableCell(child: Text('id')),
                TableCell(child: Text('姓名')),
                TableCell(child: Text('年龄')),
                TableCell(child: Text('性别')),
              ]),
              ..._list.map((user) {
                return TableRow(children: [
                  TableCell(child: Text('${user.id}')),
                  TableCell(child: Text('${user.name}')),
                  TableCell(child: Text('${user.age}')),
                  TableCell(child: Text(user.sex == 0 ? '男' : '女')),
                ]);
              }).toList()
            ],
          )
        ],
      ),
    );
  }
}

Query according to conditions, such as querying data with age 12:

Future<List<User>> find(int age) async {
    var _db = await db;
    List<Map<String, dynamic>> result =
        await _db.query('User', where: 'age = ?', whereArgs: [age]);

    return result.isNotEmpty ? result.map((e) {
      return User.fromJson(e);
    }).toList():[];
  }

use:

_loadData() async {
  _list = await DBProvider().find(12);
  setState(() {});
}

update data

Update data according to User id:

Future<int> update(User user) async {
  var _db = await db;
  return await _db
      .update('User', user.toJson(), where: 'id = ?', whereArgs: [user.id]);
}

Jump to the page to modify data:

RaisedButton(
  child: Text('修改第一行数据'),
  onPressed: () {
    if (_list.length > 1) {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (context) {
        return _AddUser(
          user: _list[0],
        );
      }));
    }
  },
),

Save the modified data:

RaisedButton(
  child: Text('保存'),
  onPressed: () async {
    var user = User(
        name: '${_nameController.text}',
        age: int.parse('${_ageController.text}'),
        sex: int.parse('$_radioGroupValue'));
    if (widget.user == null) {
      _saveData(context,user);
    } else {
      _updateData(context,user);
    }
  },
)

_updateData(BuildContext context,User user) async {
    user.id = widget.user.id;
    int result = await DBProvider().update(user);
    if (result > 0) {
      Scaffold.of(context).showSnackBar(SnackBar(
        content: Text('修改数据成功,result:$result'),
      ));
    } else {
      Scaffold.of(context).showSnackBar(SnackBar(
        content: Text('修改数据失败,result:$result'),
      ));
    }
  }

delete data

Delete eligible data based on id:

Future<int> delete(int id) async {
  var _db = await db;
  return await _db.delete('User', where: 'id = ?', whereArgs: [id]);
}

Delete the first row of data, refresh the data after successful deletion:

RaisedButton(
  child: Text('删除第一行数据'),
  onPressed: () async {
    if (_list.length > 0) {
      await DBProvider().delete(_list[0].id);
      _loadData();
    }
  },
),

Delete all data:

Future<int> deleteAll() async {
  var _db = await db;
  return await _db.delete('User');
}

to sum up

We introduced the basic usage of SQLite. Data addition, deletion, modification, and query are the most frequently used. SQLite also has some advanced query statements, such as grouping and joint query, which are not frequently used.

After SQLite is successfully created, a db_name.db file will be created locally . The file directory is the directory set when the database is initialized.

communicate with

communicate with

Laomeng Flutter blog (330 control usage + actual combat introduction series): http://laomengit.com

Welcome to join the Flutter exchange group (WeChat: laomengit) and follow the public account [Lao Meng Flutter]:

Guess you like

Origin blog.csdn.net/mengks1987/article/details/108782226