Flutter数据访问插件dart_data_source的使用

Flutter数据访问插件dart_data_source的使用

dart_data_source 是一个基于 Sqflite 的 Dart 数据访问层。它可以帮助你在 Flutter 应用中更方便地操作 SQLite 数据库。

安装插件

首先,在你的 pubspec.yaml 文件中添加 dart_data_source 依赖:

dependencies:
  dart_data_source: ^最新版本号

然后运行 flutter pub get 来安装依赖。

使用步骤

使用 dart_data_source 插件通常包括以下三个步骤:

  1. 扩展 DatabaseModel
  2. 初始化数据库
  3. 执行 SQL 操作

1. 扩展 DatabaseModel

创建一个继承自 DatabaseModel 的类,并在其中定义表结构、字段等。

import 'package:dart_data_source/dart_data_source.dart';

class DbModel extends DatabaseModel {
  // grade
  late StringColumn gradeName;
  late IntColumn studentsCount;
  late Table gradesTable;

  // student
  late DbColumn studentName;
  late IntColumn studentDegree;
  late Table studentsTable;

  late IntColumn studentGradeFk;

  // indices
  late Index gradeNameIndex;
  late Index studentNameIndex;

  // view
  late View studentGradeView;

  late DateColumn studentJoinDate;

  DbModel(Database db) : super(db);

  @override
  void defineDatabaseObjects(Database db) {
    // Grade table
    gradeName = db.stringColumn('gradeName', 50);
    studentsCount = db.intColumn('studentsCount', defaultValue: 0);

    gradesTable = db.newTable('Grades', [gradeName, studentsCount]);

    // student table
    studentName = db.stringColumn('studentName', 200);
    studentDegree = db.intColumn('studentDegree', defaultValue: 0);
    studentJoinDate = db.dateColumn('studentJoinDate', allowNull: true);

    studentsTable = db.newTable("Students", [studentName, studentDegree, studentJoinDate]);

    // foreign key
    studentGradeFk = studentsTable.addFKto(gradesTable, "studentGradeFk");

    // indices
    gradeNameIndex = db.newIndex('grade_name_idx', gradesTable, [gradeName], unique: true);
    studentNameIndex = db.newIndex('student_name_idx', studentsTable, [studentName]);

    // view
    studentGradeView = db.newView("student_grade_vw",
        db.Select().From(studentsTable.InnerJoin(gradesTable, studentGradeFk.Equal(gradesTable.Id))));
  }

  @override
  void defineSchemaUpdates(Database db) {
    // db migrations go here, it only supports adding new db objects, else you'll have to write raw sql
    db.addSchemaUpdate(SchemaUpdate(objects: [gradesTable, studentsTable]));
    //
    // you don't have to include table columns unless it's table is already created in older migration
    db.addSchemaUpdate(SchemaUpdate(objects: [studentJoinDate]));
    //
    db.addSchemaUpdate(SchemaUpdate(objects: [gradeNameIndex, studentNameIndex, studentGradeView]));
  }
}

2. 初始化数据库

初始化数据库可以使用文件存储或内存存储。这里以内存存储为例:

import 'dart:io' show Directory;
import 'package:path_provider/path_provider.dart';
import 'package:dart_data_source/dart_data_source.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<String> logList = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(10),
          child: Column(
            children: [
              Expanded(
                child: ListView(
                  children: logList.map((e) => Text(e)).toList(),
                ),
              ),
              TextButton(
                child: Text('press'),
                onPressed: () => _onBtnPress(),
              ),
            ],
          ),
        ),
      ),
    );
  }

  _onBtnPress() async {
    var db = new InMemoryDatabase();
    var dbm = new DbModel(db);

    var dbc = await db.newContext()
      ..setLogger(resultLogger: (log) => print(log), sqlLogger: (log) => print(log));

    // 新建模式
    await dbm.init(dbc);
    var version = await db.currentVersion(dbc);

    log('数据库版本 => $version');
    expect(version, 3);

    // 插入数据
    await db.InsertInto(dbm.gradesTable)
        .Values([dbm.gradeName.Assign('Grade1'), dbm.studentsCount.Assign(0)]).execute(dbc);

    await db.InsertInto(dbm.gradesTable).ValuesMap({'gradeName': 'Grade2', 'studentsCount': 0}).execute(dbc);

    await dbc.rawInsert("insert into Grades (gradeName, studentsCount) values ('Grade3', 0)");

    var gradeCount = await db.Select()
        .Fields([Expr.Count(dbm.gradesTable.Id)])
        .From(dbm.gradesTable)
        .executeScalar(dbc);

    log('年级数量 => $gradeCount');
    expect(gradeCount, 3);

    var gradesData = await db.Select().From(dbm.gradesTable).execute(dbc);
    log('年级数据 => $gradesData');

    // 在事务中添加 30 名学生,并更新年级表中的学生数量
    await dbc.executeTransaction((transactionContext) async {
      for (int i = 1; i <= 10; i++) {
        await db.InsertInto(dbm.studentsTable).ValuesMap({
          'studentName': 'ahmed$i-1',
          'studentDegree': i * 10,
          'studentJoinDate': DateTime(2020, 1, i),
          'studentSuccess': false,
          'studentGradeFk': 1
        }).execute(transactionContext);
      }
      //
      for (int i = 1; i <= 10; i++) {
        await db.InsertInto(dbm.studentsTable).ValuesMap({
          'studentName': 'ahmed$i-2',
          'studentDegree': i * 10,
          'studentJoinDate': DateTime(2020, 1, i),
          'studentSuccess': 1, // int value for bool
          'studentGradeFk': 2
        }).execute(transactionContext);
      }
      //
      for (int i = 1; i <= 10; i++) {
        db.InsertInto(dbm.studentsTable).ValuesMap({
          'test': null, // this field shall be ignored
          'studentName': 'ahmed$i-3',
          'studentDegree': i * 10,
          'studentJoinDate': DateTime(2020, 1, i),
          'studentGradeFk': 3
        }).execute(transactionContext);
      }
      //
      await db.Update(dbm.gradesTable)
          .Set([dbm.studentsCount.Assign(10)])
          .Where(dbm.gradesTable.Id.InValues([1, 2, 3]))
          .execute(transactionContext);
    });

    gradesData = await db.Select().From(dbm.gradesTable).execute(dbc);
    log('年级数据 => $gradesData');

    var studentsData = await db.Select().From(dbm.studentsTable).execute(dbc);
    log('学生数据 => $studentsData');

    expect(30, studentsData.length);

    // 按日期查询
    var q1 = await db.Select()
        .From(dbm.studentsTable)
        .Where(dbm.studentJoinDate.BetweenValues(DateTime(2020, 1, 2), DateTime(2020, 1, 6)))
        .execute(dbc);

    expect(15, q1.length);
    log('学生日期从 2 到 6 => $q1');

    // 删除年级
    await db.DeleteFrom(dbm.gradesTable).Where(dbm.gradesTable.Id.Equal(3)).execute(dbc);

    gradesData = await db.Select().From(dbm.gradesTable).execute(dbc);
    expect(2, gradesData.length);
    expect(20, (await db.Select().From(dbm.studentsTable).execute(dbc)).length);

    // 联合查询
    var q2 = await db.Select()
        .From(dbm.studentsTable)
        .Where(dbm.studentsTable.Id.GreaterThan(1))
        .Intersect(db.Select().From(dbm.studentsTable).Where(dbm.studentsTable.Id.LessThan(3)))
        .execute(dbc);

    log('联合查询 => $q2');
    expect(1, q2.length);

    // 从视图查询
    var q3 = await db.Select()
        .From(dbm.studentGradeView)
        .Where(dbm.studentJoinDate.Equal(DateTime(2020, 1, 5)))
        .execute(dbc);

    log('从视图查询 => $q3');
    expect(2, q3.length);

    var q4 = await db.Select().From(dbm.studentGradeView).Page(0, 2).execute(dbc);

    log('从视图查询前两行 => $q4');
    expect(2, q4.length);

    var q5 = await db.Select().From(dbm.studentGradeView).Where(dbm.studentSuccess.Equal(true)).Page(0, 2).execute(dbc);

    log('根据布尔字段查询前两行 => $q5');
    expect(2, q5.length);

    var q6 = await db.Select()
        .From(dbm.studentsTable)
        .Where(Expr.DateFormat(dbm.studentJoinDate, '%d').Equal('01'))
        .execute(dbc);

    log('使用日期格式查询 => $q6');
    expect(2, q6.length);

    dbc.close();
  }

  void expect(Object? a, Object? b) {
    log('断言: $a = $b');
    assert(a == b);
  }

  void log(String line) {
    logList.add(line + '\n------');
    setState(() {});
  }
}

3. 执行 SQL 操作

插入数据
await db.InsertInto(dbm.gradesTable)
    .Values([dbm.gradeName.Assign('Grade1'), dbm.studentsCount.Assign(0)]).execute(dbc);

await db.InsertInto(dbm.gradesTable).ValuesMap({'gradeName': 'Grade2', 'studentsCount': 0}).execute(dbc);

await dbc.rawInsert("insert into Grades (gradeName, studentsCount) values ('Grade3', 0)");
查询数据
var gradeCount = await db.Select()
    .Fields([Expr.Count(dbm.gradesTable.Id)])
    .From(dbm.gradesTable)
    .executeScalar(dbc);

print('年级数量 => $gradeCount');
更新数据
await db.Update(dbm.gradesTable)
  .Set([dbm.gradeName.Assign('First Grade')])
  .Where(dbm.gradeName.Equal('Grade1'))
  .execute(dbc);
删除数据
await db.DeleteFrom(dbm.gradesTable).Where(dbm.gradesTable.Id.Equal(1)).execute(dbc);
事务处理
await dbc.executeTransaction((transactionContext) async {
  for (int i = 1; i <= 10; i++) {
    await db.InsertInto(dbm.studentsTable).ValuesMap({
      'studentName': 'student$i-grade1',
      'studentDegree': i * 10,
      'studentJoinDate': DateTime(2020, 1, i),
      'studentGradeFk': 1
    }).execute(transactionContext);
  }

  for (int i = 1; i <= 10; i++) {
    await db.InsertInto(dbm.studentsTable).ValuesMap({
      'studentName': 'student$i-grade2',
      'studentDegree': i * 10,
      'studentJoinDate': DateTime(2020, 1, i),
      'studentGradeFk': 2
    }).execute(transactionContext);
  }

  await db.Update(dbm.gradesTable)
      .Set([dbm.studentsCount.Assign(dbm.studentsCount.PlusValue(10))])
      .Where(dbm.gradesTable.Id.InValues([1, 2]))
      .execute(transactionContext);
});

更多关于Flutter数据访问插件dart_data_source的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据访问插件dart_data_source的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


dart_data_source 是一个用于 Flutter 应用中的数据访问插件,它允许开发者方便地处理不同数据源(如 SQLite、网络请求等)的数据。它提供了一套统一的 API 来操作数据,并且支持类型安全的数据访问。

主要特点

  1. 统一的数据访问接口:无论数据源是 SQLite、REST API 还是其他,dart_data_source 提供了一套统一的接口来访问和操作数据。
  2. 类型安全:通过泛型和强类型支持,减少了运行时错误。
  3. 数据绑定:支持将数据直接绑定到 Flutter 的 UI 组件上,简化了数据驱动的 UI 开发。
  4. 支持多种数据源:包括 SQLite、REST API 等。

基本使用

1. 安装

首先,在 pubspec.yaml 中添加 dart_data_source 依赖:

dependencies:
  flutter:
    sdk: flutter
  dart_data_source: ^1.0.0

然后运行 flutter pub get 来安装依赖。

2. 配置数据源

假设你使用的是 SQLite 数据库,首先需要配置数据源:

import 'package:dart_data_source/dart_data_source.dart';

void main() {
  var dataSource = SQLiteDataSource('my_database.db');
  dataSource.open().then((_) {
    // 数据源已打开,可以执行查询等操作
  }).catchError((error) {
    print('Failed to open data source: $error');
  });
}

3. 定义数据模型

定义一个数据模型类来表示数据库中的表:

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

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

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name,
      'age': age,
    };
  }

  factory User.fromMap(Map<String, dynamic> map) {
    return User(
      id: map['id'],
      name: map['name'],
      age: map['age'],
    );
  }
}

4. 查询数据

使用 dart_data_source 查询数据:

Future<List<User>> getUsers(SQLiteDataSource dataSource) async {
  var result = await dataSource.query('SELECT * FROM users');
  return result.map((map) => User.fromMap(map)).toList();
}

5. 插入数据

插入新数据:

Future<void> insertUser(SQLiteDataSource dataSource, User user) async {
  await dataSource.insert('users', user.toMap());
}

6. 更新数据

更新现有数据:

Future<void> updateUser(SQLiteDataSource dataSource, User user) async {
  await dataSource.update('users', user.toMap(), where: 'id = ?', whereArgs: [user.id]);
}

7. 删除数据

删除数据:

Future<void> deleteUser(SQLiteDataSource dataSource, int userId) async {
  await dataSource.delete('users', where: 'id = ?', whereArgs: [userId]);
}

数据绑定

dart_data_source 支持将数据绑定到 Flutter 的 UI 组件上。例如,你可以使用 FutureBuilderStreamBuilder 来显示从数据源中获取的数据:

class UserList extends StatelessWidget {
  final SQLiteDataSource dataSource;

  UserList({this.dataSource});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return FutureBuilder<List<User>>(
      future: getUsers(dataSource),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator();
        } else if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        } else {
          var users = snapshot.data;
          return ListView.builder(
            itemCount: users.length,
            itemBuilder: (context, index) {
              var user = users[index];
              return ListTile(
                title: Text(user.name),
                subtitle: Text('Age: ${user.age}'),
              );
            },
          );
        }
      },
    );
  }
}
回到顶部