Table of Contents
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.
Code Implementation :
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;
}
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.