Flutter 数据库drift(moor)的正确使用姿势

985 阅读1分钟

drift

由moor改名 Flutter 数据库moor的正确使用姿势

1. 添加依赖

    environment:
      sdk: '>=2.19.2 <3.0.0'

    dependencies:
      flutter:
        sdk: flutter
      cupertino_icons: ^1.0.2
      drift: ^2.8.0
      drift_db_viewer: ^2.0.0
      sqlite3_flutter_libs: ^0.5.18
      path_provider: ^2.1.1
      path: ^1.8.2

    dev_dependencies:
      flutter_test:
        sdk: flutter
      drift_dev: ^2.8.3
  

2. 编写数据库


    import 'dart:io';

    import 'package:drift/drift.dart';
    import 'package:drift/native.dart';
    import 'package:path_provider/path_provider.dart';
    import 'package:path/path.dart' as p;
    part 'my_database.g.dart';

    class Tasks extends Table {
      IntColumn get id => integer().autoIncrement()();

      TextColumn get name => text().withLength(min: 1, max: 50)();

      BoolColumn get completed => boolean().withDefault(const Constant(false))();
    }

    @DriftDatabase(tables: [Tasks])
    class MyDatabase extends _$MyDatabase {
      MyDatabase() : super(openConnection());

      @override
      int get schemaVersion => 1;

      ///插入
      Future<int> insertTask(Task task) async {
        return await into(tasks).insert(task);
      }

      ///查询数据
      Future<List<Task>> queryTasks() async {
        return await select(tasks).get();
      }

      /// 更新数据
      Future<bool> updateTask(Task task) async {
        return await update(tasks).replace(task);
      }

      ///删除数据
      Future<int> deleteTask(Task task) async {
        return await delete(tasks).delete(task);
      }
      ///删除数据
      Future<int> deleteAllTasks() => delete(tasks).go();


    }

    class DatabaseManager {
      MyDatabase myDatabase = MyDatabase();

      DatabaseManager._();

      static final DatabaseManager _instance = DatabaseManager._();

      static DatabaseManager get instance {
        return _instance;
      }
    }
    LazyDatabase openConnection() {
      return LazyDatabase(() async {
        final dbFolder = await getApplicationDocumentsDirectory();
        final file = File(p.join(dbFolder.path, 'test.db'));
        return NativeDatabase(file);
      });
    }

3. 执行命令生成代码

    flutter pub run build_runner build --delete-conflicting-outputs


    // GENERATED CODE - DO NOT MODIFY BY HAND

    part of 'my_database.dart';

    // ignore_for_file: type=lint
    class $TasksTable extends Tasks with TableInfo<$TasksTable, Task> {
      @override
      final GeneratedDatabase attachedDatabase;
      final String? _alias;
      $TasksTable(this.attachedDatabase, [this._alias]);
      static const VerificationMeta _idMeta = const VerificationMeta('id');
      @override
      late final GeneratedColumn<int> id = GeneratedColumn<int>(
          'id', aliasedName, false,
          hasAutoIncrement: true,
          type: DriftSqlType.int,
          requiredDuringInsert: false,
          defaultConstraints:
              GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
      static const VerificationMeta _nameMeta = const VerificationMeta('name');
      @override
      late final GeneratedColumn<String> name = GeneratedColumn<String>(
          'name', aliasedName, false,
          additionalChecks:
              GeneratedColumn.checkTextLength(minTextLength: 1, maxTextLength: 50),
          type: DriftSqlType.string,
          requiredDuringInsert: true);
      static const VerificationMeta _completedMeta =
          const VerificationMeta('completed');
      @override
      late final GeneratedColumn<bool> completed =
          GeneratedColumn<bool>('completed', aliasedName, false,
              type: DriftSqlType.bool,
              requiredDuringInsert: false,
              defaultConstraints: GeneratedColumn.constraintsDependsOnDialect({
                SqlDialect.sqlite: 'CHECK ("completed" IN (0, 1))',
                SqlDialect.mysql: '',
                SqlDialect.postgres: '',
              }),
              defaultValue: const Constant(false));
      @override
      List<GeneratedColumn> get $columns => [id, name, completed];
      @override
      String get aliasedName => _alias ?? 'tasks';
      @override
      String get actualTableName => 'tasks';
      @override
      VerificationContext validateIntegrity(Insertable<Task> instance,
          {bool isInserting = false}) {
        final context = VerificationContext();
        final data = instance.toColumns(true);
        if (data.containsKey('id')) {
          context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
        }
        if (data.containsKey('name')) {
          context.handle(
              _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
        } else if (isInserting) {
          context.missing(_nameMeta);
        }
        if (data.containsKey('completed')) {
          context.handle(_completedMeta,
              completed.isAcceptableOrUnknown(data['completed']!, _completedMeta));
        }
        return context;
      }

      @override
      Set<GeneratedColumn> get $primaryKey => {id};
      @override
      Task map(Map<String, dynamic> data, {String? tablePrefix}) {
        final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
        return Task(
          id: attachedDatabase.typeMapping
              .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
          name: attachedDatabase.typeMapping
              .read(DriftSqlType.string, data['${effectivePrefix}name'])!,
          completed: attachedDatabase.typeMapping
              .read(DriftSqlType.bool, data['${effectivePrefix}completed'])!,
        );
      }

      @override
      $TasksTable createAlias(String alias) {
        return $TasksTable(attachedDatabase, alias);
      }
    }

    class Task extends DataClass implements Insertable<Task> {
      final int id;
      final String name;
      final bool completed;
      const Task({required this.id, required this.name, required this.completed});
      @override
      Map<String, Expression> toColumns(bool nullToAbsent) {
        final map = <String, Expression>{};
        map['id'] = Variable<int>(id);
        map['name'] = Variable<String>(name);
        map['completed'] = Variable<bool>(completed);
        return map;
      }

      TasksCompanion toCompanion(bool nullToAbsent) {
        return TasksCompanion(
          id: Value(id),
          name: Value(name),
          completed: Value(completed),
        );
      }

      factory Task.fromJson(Map<String, dynamic> json,
          {ValueSerializer? serializer}) {
        serializer ??= driftRuntimeOptions.defaultSerializer;
        return Task(
          id: serializer.fromJson<int>(json['id']),
          name: serializer.fromJson<String>(json['name']),
          completed: serializer.fromJson<bool>(json['completed']),
        );
      }
      @override
      Map<String, dynamic> toJson({ValueSerializer? serializer}) {
        serializer ??= driftRuntimeOptions.defaultSerializer;
        return <String, dynamic>{
          'id': serializer.toJson<int>(id),
          'name': serializer.toJson<String>(name),
          'completed': serializer.toJson<bool>(completed),
        };
      }

      Task copyWith({int? id, String? name, bool? completed}) => Task(
            id: id ?? this.id,
            name: name ?? this.name,
            completed: completed ?? this.completed,
          );
      @override
      String toString() {
        return (StringBuffer('Task(')
              ..write('id: $id, ')
              ..write('name: $name, ')
              ..write('completed: $completed')
              ..write(')'))
            .toString();
      }

      @override
      int get hashCode => Object.hash(id, name, completed);
      @override
      bool operator ==(Object other) =>
          identical(this, other) ||
          (other is Task &&
              other.id == this.id &&
              other.name == this.name &&
              other.completed == this.completed);
    }

    class TasksCompanion extends UpdateCompanion<Task> {
      final Value<int> id;
      final Value<String> name;
      final Value<bool> completed;
      const TasksCompanion({
        this.id = const Value.absent(),
        this.name = const Value.absent(),
        this.completed = const Value.absent(),
      });
      TasksCompanion.insert({
        this.id = const Value.absent(),
        required String name,
        this.completed = const Value.absent(),
      }) : name = Value(name);
      static Insertable<Task> custom({
        Expression<int>? id,
        Expression<String>? name,
        Expression<bool>? completed,
      }) {
        return RawValuesInsertable({
          if (id != null) 'id': id,
          if (name != null) 'name': name,
          if (completed != null) 'completed': completed,
        });
      }

      TasksCompanion copyWith(
          {Value<int>? id, Value<String>? name, Value<bool>? completed}) {
        return TasksCompanion(
          id: id ?? this.id,
          name: name ?? this.name,
          completed: completed ?? this.completed,
        );
      }

      @override
      Map<String, Expression> toColumns(bool nullToAbsent) {
        final map = <String, Expression>{};
        if (id.present) {
          map['id'] = Variable<int>(id.value);
        }
        if (name.present) {
          map['name'] = Variable<String>(name.value);
        }
        if (completed.present) {
          map['completed'] = Variable<bool>(completed.value);
        }
        return map;
      }

      @override
      String toString() {
        return (StringBuffer('TasksCompanion(')
              ..write('id: $id, ')
              ..write('name: $name, ')
              ..write('completed: $completed')
              ..write(')'))
            .toString();
      }
    }

    abstract class _$MyDatabase extends GeneratedDatabase {
      _$MyDatabase(QueryExecutor e) : super(e);
      late final $TasksTable tasks = $TasksTable(this);
      @override
      Iterable<TableInfo<Table, Object?>> get allTables =>
          allSchemaEntities.whereType<TableInfo<Table, Object?>>();
      @override
      List<DatabaseSchemaEntity> get allSchemaEntities => [tasks];
    }

4. 调用


 Future<void> add() async {
    try {
        await DatabaseManager.instance.myDatabase
            .into(DatabaseManager.instance.myDatabase.tasks)
            .insert(TasksCompanion.insert(name: "任务$current"));
        current = current + 1;
      var list = await DatabaseManager.instance.myDatabase.queryTasks();
      tasks = list;
      setState(() {});
    } catch (e) {
      debugPrint(e.toString());
    }
  }

  Future<void> delete(int i) async {
    await DatabaseManager.instance.myDatabase.deleteTask(tasks[i]);
    tasks.removeAt(i);
    current = current - 1;
    setState(() {});
  }
  Future<void> clear() async {
    await DatabaseManager.instance.myDatabase.deleteAllTasks();
    tasks.clear();
    current = 0;
    setState(() {});
  }

5.查看数据库


    Navigator.of(context).push(MaterialPageRoute(
                        builder: (context) =>
                            DriftDbViewer(DatabaseManager.instance.myDatabase)));

6. 代码仓库地址

drift_demo