In this blog we will learn about how to use Drift Local Database in Flutter.
Introduction:
Drift in flutter It allows you to write SQL queries in a type-safe manner and automatically handles database schema migrations, making it easier to work with local data.
Drift stands out due to its ability to write SQL queries in a type-safe manner and automatically handle database schema migrations.
This not only simplifies database operations but also reduces potential runtime errors that can arise from mismatched column names or data types.
Key Features of Drift:
- Type Safety: Compile-time checks to ensure your queries are correct.
- Reactive Data: Automatically update your UI with the data changes.
- Simple Query Language: SQL-Like syntax for querying data.
Implementation
Setting Up Your Flutter Project:
Open pubspec.yaml
and add the following dependencies under dependencies
and dev_dependencies
.
- Add Dependencies:
1 2 3 4 5 6 7 8 9 10 11 |
dependencies: flutter: sdk: flutter drift: ^2.0.0 drift_flutter: ^2.0.0 sqlite3_flutter_libs: ^0.6.0 dev_dependencies: build_runner: ^2.3.0 drift_dev: ^2.0.0 |
Run the following command to get the packages:
1 |
flutter pub get |
Creating Drift Database:
- Define Your Data Model:
Create a new file,lib/database.dart
, and define your database schema using Drift. For example, let’s create a simpleTask
table:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import 'package:drift/drift.dart'; import 'package:drift/native.dart'; part 'database.g.dart'; @DriftDatabase(tables: [Todos]) class AppDatabase extends _$AppDatabase { AppDatabase() : super(NativeDatabase.memory()); @override int get schemaVersion => 1; } class Todos extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get title => text().withLength(min: 1, max: 50)(); BoolColumn get isDone => boolean().withDefault(Constant(false))(); } |
- Generate the Database Code:
To generate the code needed for Drift, run:
1 2 |
flutter pub run build_runner build |
Using Drift in the Flutter Application:
Now that our database setup is ready, let’s integrate it into our Flutter app. In the main.dart
file, we’ll set up a basic UI to display, add, update, and delete tasks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
import 'package:drift/drift.dart'; import 'package:drift_database_flutter/database.dart'; import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Drift Example', home: TodoList(), ); } } class TodoList extends StatefulWidget { @override _TodoListState createState() => _TodoListState(); } class _TodoListState extends State<TodoList> { late AppDatabase db; List<Todo> todos = []; @override void initState() { super.initState(); db = AppDatabase(); _fetchTodos(); } Future<void> _fetchTodos() async { todos = await db.select(db.todos).get(); setState(() {}); } Future<void> _addTodo() async { await db.into(db.todos).insert( TodosCompanion( title: Value('New Task'), isDone: Value(false), ), ); _fetchTodos(); } Future<void> _updateTodo(Todo todo) async { final newTitle = await _showUpdateDialog(todo.title); if (newTitle != null && newTitle.isNotEmpty) { // Correctly update the title await db.update(db.todos).replace(todo.copyWith(title: "${newTitle}")); _fetchTodos(); } } Future<String?> _showUpdateDialog(String currentTitle) { TextEditingController controller = TextEditingController(text: currentTitle); return showDialog<String>( context: context, builder: (context) { return AlertDialog( title: Text('Update Todo'), content: TextField( controller: controller, decoration: InputDecoration(labelText: 'New Title'), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text('Cancel'), ), TextButton( onPressed: () => Navigator.of(context).pop(controller.text), child: Text('Update'), ), ], ); }, ); } Future<void> _deleteTodo(int id) async { final todoToDelete = await db.select(db.todos).get().then((value) => value.firstWhere((todo) => todo.id == id)); await db.delete(db.todos).delete(todoToDelete); _fetchTodos(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Todo List'), actions: [ IconButton( icon: Icon(Icons.add), onPressed: _addTodo, ), ], ), body: ListView.builder( itemCount: todos.length, itemBuilder: (context, index) { final todo = todos[index]; return ListTile( title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(todo.title,maxLines: 2,overflow: TextOverflow.ellipsis), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => _updateTodo(todo), child: Text('Update', style: TextStyle(color: Colors.blue)), ), SizedBox(width: 8), TextButton( onPressed: () => _deleteTodo(todo.id), child: Text('Delete', style: TextStyle(color: Colors.red)), ), ], ), ], ), ); }, ), ); } } |
Output
Drift Local Database in Flutter
Drift in flutter It allows you to write SQL queries in a type-safe manner and automatically handles database schema migrations, making it easier to work with local data.
Conclusion:
Drift makes managing local databases in Flutter a seamless experience.
With its type-safe queries, reactive streams, and migration support, it simplifies database operations and improves code maintainability.
And thanks for reading this blog for related data click here.
You can read more interesting blogs by mobikul .