Flutter InstantDB
Getting Started

Quick Start

Build your first real-time app with InstantDB Flutter

This guide will help you build a simple todo app with real-time synchronization in just a few minutes.

1. Initialize InstantDB

First, initialize your InstantDB instance in your app:

import 'package:flutter/material.dart';
import 'package:flutter_instantdb/flutter_instantdb.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Initialize InstantDB
  final db = await InstantDB.init(
    appId: 'your-app-id-here', // Get this from instantdb.com
    config: const InstantConfig(
      syncEnabled: true,
    ),
  );
  
  runApp(MyApp(db: db));
}

2. Create Your App Widget

Wrap your app with InstantProvider to make the database available throughout your widget tree:

class MyApp extends StatelessWidget {
  final InstantDB db;
  
  const MyApp({super.key, required this.db});

  @override
  Widget build(BuildContext context) {
    return InstantProvider(
      db: db,
      child: MaterialApp(
        title: 'InstantDB Todo',
        home: const TodoPage(),
      ),
    );
  }
}

3. Build Reactive UI

Create a page that automatically updates when data changes:

class TodoPage extends StatefulWidget {
  const TodoPage({super.key});

  @override
  State<TodoPage> createState() => _TodoPageState();
}

class _TodoPageState extends State<TodoPage> {
  final _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Todos')),
      body: Column(
        children: [
          // Add todo input
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      hintText: 'Add a todo...',
                    ),
                    onSubmitted: _addTodo,
                  ),
                ),
                IconButton(
                  onPressed: () => _addTodo(_controller.text),
                  icon: const Icon(Icons.add),
                ),
              ],
            ),
          ),
          
          // Todo list - automatically updates in real-time!
          Expanded(
            child: InstantBuilderTyped<List<Map<String, dynamic>>>(
              query: {'todos': {}},
              transformer: (data) {
                final todos = (data['todos'] as List).cast<Map<String, dynamic>>();
                // Sort by creation time
                todos.sort((a, b) => (b['createdAt'] ?? 0).compareTo(a['createdAt'] ?? 0));
                return todos;
              },
              builder: (context, todos) {
                if (todos.isEmpty) {
                  return const Center(
                    child: Text('No todos yet. Add one above!'),
                  );
                }
                
                return ListView.builder(
                  itemCount: todos.length,
                  itemBuilder: (context, index) {
                    final todo = todos[index];
                    return ListTile(
                      title: Text(
                        todo['text'] ?? '',
                        style: TextStyle(
                          decoration: todo['completed'] == true
                              ? TextDecoration.lineThrough
                              : null,
                        ),
                      ),
                      leading: Checkbox(
                        value: todo['completed'] == true,
                        onChanged: (value) => _toggleTodo(todo),
                      ),
                      trailing: IconButton(
                        icon: const Icon(Icons.delete),
                        onPressed: () => _deleteTodo(todo['id']),
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  void _addTodo(String text) {
    if (text.trim().isEmpty) return;
    
    final db = InstantProvider.of(context);
    final todoId = db.id();
    
    db.transact([
      ...db.create('todos', {
        'id': todoId,
        'text': text.trim(),
        'completed': false,
        'createdAt': DateTime.now().millisecondsSinceEpoch,
      }),
    ]);
    
    _controller.clear();
  }

  void _toggleTodo(Map<String, dynamic> todo) {
    final db = InstantProvider.of(context);
    
    db.transact(
      db.tx['todos'][todo['id']].update({
        'completed': !(todo['completed'] == true),
      }),
    );
  }

  void _deleteTodo(String todoId) {
    final db = InstantProvider.of(context);
    
    db.transact(
      db.tx['todos'][todoId].delete(),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

4. Test Real-time Sync

That's it! Your app now has:

  • Real-time synchronization - Changes appear instantly across all connected clients
  • Offline support - Works offline and syncs when connection returns
  • Reactive UI - Widgets update automatically when data changes
  • Type-safe operations - Full Dart type safety

Try It Out

  1. Run your app on multiple devices or browser tabs
  2. Add, complete, or delete todos on one device
  3. Watch them sync in real-time on all other devices!

What's Next?

Now that you have a working real-time app, explore more features:

Common Issues

App ID Not Working?

Make sure you've created an app at instantdb.com and copied the correct App ID.

Not Syncing?

Check your internet connection and ensure syncEnabled: true in your config.

Build Errors?

Make sure you're using Flutter SDK >= 3.8.0 and have run flutter pub get.

Need help? Check out our troubleshooting guide or ask in our Discord community.

On this page