Flutter Drift database :
Flutter Drift database is a reactive library used to store data locally built on top of sqlite database in your mobile apps.
Flutter drift Database play a key role in mobile app implementation by saving the user information and providing when required. Having a flexible db is a boon.
Using flutter drift database we can write safe in dart and sql query’s so that there won’t be any error’s in table creation, updating and all the CRUD operations.
Drift has got all the required features for sqlite which is of course built on top of sqlite.It can filter data use joins and fetch data from multiple tables.
Flutter drift video tutorial :
Go through the below tutorial for more details on drift database.
pubspec.yaml :
Add the required libraries for the drift.Also make sure you provide latest versions so that there won’t be any conflicts.
dependencies: drift: ^1.7.1 sqlite3_flutter_libs: ^0.5.0 path_provider: ^2.0.0 path: ^1.8.0
dev_dependencies: drift_dev: ^1.7.0 build_runner: ^2.1.11
data.dart :
Let us create a data file where we provide the required fields to be used in the sqlite db i..e, flutter drift database. We need to provide fields required like id, title, description.
Then the required data.g.dart file i..e, generated file is created so that we can access the database to insert, retrieve and perform different operations on db.
You can go through the video tutorial for detailed explanation on how the file is generated.
import 'package:drift/drift.dart'; part 'data.g.dart'; class Products extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get title => text()(); TextColumn get description => text()(); } abstract class ProductsView extends View{ Products get products; @override Query as() => select([ products.title ]).from(products); } @DriftDatabase(tables:[ Products ], views:[ ProductsView ]) class Database extends _$Database { Database(QueryExecutor e): super(e); @override int get schemaVersion => 2; }
data.g.dart :
This is the generated file for data.dart file. As we discussed above this is used to perform database operations.
// GENERATED CODE - DO NOT MODIFY BY HAND part of 'data.dart'; // ************************************************************************** // MoorGenerator // ************************************************************************** // ignore_for_file: type=lint class Product extends DataClass implements Insertable<Product> { final int id; final String title; final String description; Product({required this.id, required this.title, required this.description}); factory Product.fromData(Map<String, dynamic> data, {String? prefix}) { final effectivePrefix = prefix ?? ''; return Product( id: const IntType() .mapFromDatabaseResponse(data['${effectivePrefix}id'])!, title: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}title'])!, description: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}description'])!, ); } @override Map<String, Expression> toColumns(bool nullToAbsent) { final map = <String, Expression>{}; map['id'] = Variable<int>(id); map['title'] = Variable<String>(title); map['description'] = Variable<String>(description); return map; } ProductsCompanion toCompanion(bool nullToAbsent) { return ProductsCompanion( id: Value(id), title: Value(title), description: Value(description), ); } factory Product.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return Product( id: serializer.fromJson<int>(json['id']), title: serializer.fromJson<String>(json['title']), description: serializer.fromJson<String>(json['description']), ); } @override Map<String, dynamic> toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return <String, dynamic>{ 'id': serializer.toJson<int>(id), 'title': serializer.toJson<String>(title), 'description': serializer.toJson<String>(description), }; } Product copyWith({int? id, String? title, String? description}) => Product( id: id ?? this.id, title: title ?? this.title, description: description ?? this.description, ); @override String toString() { return (StringBuffer('Product(') ..write('id: $id, ') ..write('title: $title, ') ..write('description: $description') ..write(')')) .toString(); } @override int get hashCode => Object.hash(id, title, description); @override bool operator ==(Object other) => identical(this, other) || (other is Product && other.id == this.id && other.title == this.title && other.description == this.description); } class ProductsCompanion extends UpdateCompanion<Product> { final Value<int> id; final Value<String> title; final Value<String> description; const ProductsCompanion({ this.id = const Value.absent(), this.title = const Value.absent(), this.description = const Value.absent(), }); ProductsCompanion.insert({ this.id = const Value.absent(), required String title, required String description, }) : title = Value(title), description = Value(description); static Insertable<Product> custom({ Expression<int>? id, Expression<String>? title, Expression<String>? description, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (title != null) 'title': title, if (description != null) 'description': description, }); } ProductsCompanion copyWith( {Value<int>? id, Value<String>? title, Value<String>? description}) { return ProductsCompanion( id: id ?? this.id, title: title ?? this.title, description: description ?? this.description, ); } @override Map<String, Expression> toColumns(bool nullToAbsent) { final map = <String, Expression>{}; if (id.present) { map['id'] = Variable<int>(id.value); } if (title.present) { map['title'] = Variable<String>(title.value); } if (description.present) { map['description'] = Variable<String>(description.value); } return map; } @override String toString() { return (StringBuffer('ProductsCompanion(') ..write('id: $id, ') ..write('title: $title, ') ..write('description: $description') ..write(')')) .toString(); } } class $ProductsTable extends Products with TableInfo<$ProductsTable, Product> { @override final GeneratedDatabase attachedDatabase; final String? _alias; $ProductsTable(this.attachedDatabase, [this._alias]); final VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn<int?> id = GeneratedColumn<int?>( 'id', aliasedName, false, type: const IntType(), requiredDuringInsert: false, defaultConstraints: 'PRIMARY KEY AUTOINCREMENT'); final VerificationMeta _titleMeta = const VerificationMeta('title'); @override late final GeneratedColumn<String?> title = GeneratedColumn<String?>( 'title', aliasedName, false, type: const StringType(), requiredDuringInsert: true); final VerificationMeta _descriptionMeta = const VerificationMeta('description'); @override late final GeneratedColumn<String?> description = GeneratedColumn<String?>( 'description', aliasedName, false, type: const StringType(), requiredDuringInsert: true); @override List<GeneratedColumn> get $columns => [id, title, description]; @override String get aliasedName => _alias ?? 'products'; @override String get actualTableName => 'products'; @override VerificationContext validateIntegrity(Insertable<Product> 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('title')) { context.handle( _titleMeta, title.isAcceptableOrUnknown(data['title']!, _titleMeta)); } else if (isInserting) { context.missing(_titleMeta); } if (data.containsKey('description')) { context.handle( _descriptionMeta, description.isAcceptableOrUnknown( data['description']!, _descriptionMeta)); } else if (isInserting) { context.missing(_descriptionMeta); } return context; } @override Set<GeneratedColumn> get $primaryKey => {id}; @override Product map(Map<String, dynamic> data, {String? tablePrefix}) { return Product.fromData(data, prefix: tablePrefix != null ? '$tablePrefix.' : null); } @override $ProductsTable createAlias(String alias) { return $ProductsTable(attachedDatabase, alias); } } class ProductsViewData extends DataClass { final String title; ProductsViewData({required this.title}); factory ProductsViewData.fromData(Map<String, dynamic> data, {String? prefix}) { final effectivePrefix = prefix ?? ''; return ProductsViewData( title: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}title'])!, ); } factory ProductsViewData.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return ProductsViewData( title: serializer.fromJson<String>(json['title']), ); } @override Map<String, dynamic> toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return <String, dynamic>{ 'title': serializer.toJson<String>(title), }; } ProductsViewData copyWith({String? title}) => ProductsViewData( title: title ?? this.title, ); @override String toString() { return (StringBuffer('ProductsViewData(') ..write('title: $title') ..write(')')) .toString(); } @override int get hashCode => title.hashCode; @override bool operator ==(Object other) => identical(this, other) || (other is ProductsViewData && other.title == this.title); } class $ProductsViewView extends ViewInfo<$ProductsViewView, ProductsViewData> implements HasResultSet { final String? _alias; @override final _$Database attachedDatabase; $ProductsViewView(this.attachedDatabase, [this._alias]); $ProductsTable get products => attachedDatabase.products.createAlias('t0'); @override List<GeneratedColumn> get $columns => [products.title]; @override String get aliasedName => _alias ?? entityName; @override String get entityName => 'products_view'; @override String? get createViewStmt => null; @override $ProductsViewView get asDslTable => this; @override ProductsViewData map(Map<String, dynamic> data, {String? tablePrefix}) { return ProductsViewData.fromData(data, prefix: tablePrefix != null ? '$tablePrefix.' : null); } late final GeneratedColumn<String?> title = GeneratedColumn<String?>( 'title', aliasedName, false, type: const StringType()); @override $ProductsViewView createAlias(String alias) { return $ProductsViewView(attachedDatabase, alias); } @override Query? get query => (attachedDatabase.selectOnly(products, includeJoinedTableColumns: false) ..addColumns($columns)); @override Set<String> get readTables => const {'products'}; } abstract class _$Database extends GeneratedDatabase { _$Database(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e); late final $ProductsTable products = $ProductsTable(this); late final $ProductsViewView productsView = $ProductsViewView(this); @override Iterable<TableInfo> get allTables => allSchemaEntities.whereType<TableInfo>(); @override List<DatabaseSchemaEntity> get allSchemaEntities => [products, productsView]; }
main.dart :
Providing the full code for implementation for drift database. Using this file we can access our database created above.
We have not created any user interface so this file will access the database but prints the data in logs.
You can go through our flutter tutorial collections so that you can create your own UI.
import 'package:drift/native.dart'; import 'data.dart'; Future<void> main() async { final db = Database(NativeDatabase.memory()); await db.into(db.products).insert(ProductsCompanion.insert(title: "flutter drift", description: "Drift database")); await db.into(db.products).insert(ProductsCompanion.insert(title: "tutorial on drift", description: "Drift database")); (await db.select(db.products).get()).forEach(print); }
Flutter drift output :
If you have any query’s in this tutorial on flutter drift implementation do let us know in the comment section below.If you like this tutorial do like and share us for more interesting updates.