Get the FREE Ultimate OpenClaw Setup Guide →

flutter-drift

npx machina-cli add skill MADTeacher/mad-agents-skills/flutter-drift --openclaw
Files (1)
SKILL.md
6.3 KB

Flutter Drift

Comprehensive guide for using drift database library in Flutter applications.

Overview

Flutter Drift skill provides complete guidance for implementing persistent local storage in Flutter apps using the drift library. Drift is a reactive persistence library for Flutter built on SQLite, offering type-safe queries, auto-updating streams, schema migrations, and cross-platform support.

Quick Start

Add dependencies to pubspec.yaml:

dependencies:
  drift: ^2.30.0
  drift_flutter: ^0.2.8
  path_provider: ^2.1.5

dev_dependencies:
  drift_dev: ^2.30.0
  build_runner: ^2.10.4

Define database:

@DriftDatabase(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {
  AppDatabase([QueryExecutor? e])
      : super(
          e ??
              driftDatabase(
                name: 'app_db',
                native: const DriftNativeOptions(
                  databaseDirectory: getApplicationSupportDirectory,
                ),
                web: DriftWebOptions(
                  sqlite3Wasm: Uri.parse('sqlite3.wasm'),
                  driftWorker: Uri.parse('drift_worker.js'),
                ),
              ),
        );

  @override
  int get schemaVersion => 1;
}

Run code generator:

dart run build_runner build

Reference Files

See detailed documentation for each topic:

Common Patterns

Reactive Todo List with StreamBuilder

class TodoList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final database = Provider.of<AppDatabase>(context);

    return StreamBuilder<List<TodoItem>>(
      stream: select(database.todoItems).watch(),
      builder: (context, snapshot) {
        final todos = snapshot.data ?? [];
        return ListView.builder(
          itemCount: todos.length,
          itemBuilder: (context, index) {
            final todo = todos[index];
            return ListTile(
              title: Text(todo.title),
              trailing: Checkbox(
                value: todo.isCompleted,
                onChanged: (value) {
                  database.update(database.todoItems).replace(
                    TodoItem(
                      id: todo.id,
                      title: todo.title,
                      isCompleted: value ?? false,
                    ),
                  );
                },
              ),
            );
          },
        );
      },
    );
  }
}

Add Item with Form

Future<void> showAddTodoDialog(BuildContext context) async {
  final controller = TextEditingController();
  final database = Provider.of<AppDatabase>(context);

  await showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: const Text('Add Todo'),
        content: TextField(
          controller: controller,
          decoration: const InputDecoration(labelText: 'Title'),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Cancel'),
          ),
          TextButton(
            onPressed: () async {
              if (controller.text.isNotEmpty) {
                await database.into(database.todoItems).insert(
                  TodoItemsCompanion.insert(title: controller.text),
                );
                if (context.mounted) {
                  Navigator.pop(context);
                }
              }
            },
            child: const Text('Add'),
          ),
        ],
      );
    },
  );

  controller.dispose();
}

Provider Setup

final databaseProvider = Provider<AppDatabase>((ref) {
  final database = AppDatabase();
  ref.onDispose(database.close);
  return database;
});

Database Migration

@override
MigrationStrategy get migration {
  return MigrationStrategy(
      onUpgrade: stepByStep(
        from1To2: (m, schema) async {
          await m.addColumn(schema.todoItems, schema.todoItems.dueDate);
        },
      ),
    );
}

Platform-Specific Setup

Mobile (Android/iOS/macOS/Windows/Linux)

Uses drift_flutter with getApplicationSupportDirectory.

Web

Place sqlite3.wasm and drift_worker.js in web/ folder.

Isolate Sharing

AppDatabase.defaults(): super(
  driftDatabase(
    name: 'app_db',
    native: DriftNativeOptions(
      shareAcrossIsolates: true,
    ),
  ),
);

Testing

Use in-memory database for tests:

AppDatabase createTestDatabase() {
  return AppDatabase(NativeDatabase.memory());
}

Best Practices

  1. Use drift_flutter for easy database setup across platforms
  2. StreamBuilder for reactive UI updates
  3. Provider/Riverpod for database access management
  4. Close database on app/widget dispose
  5. Use migrations when schema changes
  6. Index columns used in WHERE clauses
  7. Limit stream query size for performance
  8. Use transactions for multi-step operations
  9. Debounce user input for search/filter
  10. Handle loading/error states in UI

Troubleshooting

Build Fails

dart run build_runner clean
dart run build_runner build --delete-conflicting-outputs

Migration Errors

dart run drift_dev schema validate
dart run drift_dev make-migrations

Stream Not Updating

Ensure operations go through drift APIs, not raw SQLite.

Source

git clone https://github.com/MADTeacher/mad-agents-skills/blob/main/flutter-drift/SKILL.mdView on GitHub

Overview

Flutter Drift provides a complete guide for implementing persistent local storage in Flutter apps using drift. Drift is a reactive persistence library built on SQLite, offering type-safe queries, auto-updating streams, and cross-platform support. The guide covers setup with drift_flutter, StreamBuilder integration, Provider/Riverpod patterns, migrations, and efficient CRUD operations for mobile, web, and desktop.

How This Skill Works

Install drift and drift_flutter, then define a DriftDatabase subclass annotated with @DriftDatabase and a database class that initializes the backend. Run code generation with build_runner to generate the _$AppDatabase class, then access a single AppDatabase instance and use generated APIs and streams such as select(...).watch() to drive reactive UI.

When to Use It

  • Need local persistent storage in a Flutter app using SQLite.
  • Want type-safe, compile-time checked queries and joins.
  • Build a UI that reacts in real time to data changes via streams.
  • Develop cross-platform apps (mobile, web, desktop) from a single codebase.
  • Manage schema migrations and upgrades across app versions.

Quick Start

  1. Step 1: Add drift and drift_flutter dependencies to pubspec.yaml along with path_provider; include drift_dev and build_runner in dev_dependencies.
  2. Step 2: Define AppDatabase extends _$AppDatabase with the DriftDatabase annotation and initialize using driftDatabase with native and web options as shown in the example.
  3. Step 3: Run code generation with dart run build_runner build

Best Practices

  • Increment schemaVersion with each change and document migrations.
  • Use drift_flutter for platform specific storage initialization.
  • Leverage streams via watch and UI patterns like StreamBuilder, Provider, or Riverpod.
  • Keep AppDatabase as a singleton or injected dependency to avoid multiple instances.
  • Test migrations and queries locally before deploying

Example Use Cases

  • Reactive Todo List with StreamBuilder showing real time updates.
  • Add Todo via a dialog form and save using drift insert or replace.
  • Define a TodoItems table and use generated queries for selects and updates.
  • Initialize AppDatabase using drift_flutter across mobile, web, and desktop.
  • Run build_runner to generate code and wire up with UI patterns

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers