Github|...

Flutter Guide

Spooky provides Dart code generation for Flutter integration, allowing for natively typed data models.

Note

The Flutter/Dart client is currently in development. This guide shows the generated Dart models and how to use them with SurrealDB’s Dart client.

Generate Dart Models

Use the Spooky CLI to generate Dart models from your SurrealDB schema:

Bash
spooky --input src/schema.surql --output lib/models.dart --format dart

This generates Dart classes with JSON serialization for your schema tables.

Generated Models Example

The CLI generates strongly-typed Dart classes for each table in your schema:

lib/models.dart
dart
// Auto-generated by Spooky CLI
import 'dart:convert';

class Comment {
/// Record ID of table: user
String author;

/// Assert: $value != NONE AND string::len($value) > 0
String content;

DateTime? createdAt;

/// Record ID
String id;

/// Record ID of table: thread
String threadId;

Comment({
  required this.author,
  required this.content,
  this.createdAt,
  required this.id,
  required this.threadId,
});

factory Comment.fromJson(Map<String, dynamic> json) => Comment(
  author: json["author"],
  content: json["content"],
  createdAt: json["created_at"] == null 
    ? null 
    : DateTime.parse(json["created_at"]),
  id: json["id"],
  threadId: json["thread_id"],
);

Map<String, dynamic> toJson() => {
  "author": author,
  "content": content,
  "created_at": createdAt?.toIso8601String(),
  "id": id,
  "thread_id": threadId,
};
}

class Thread {
String author;
String content;
DateTime? createdAt;
String id;
String title;

Thread({
  required this.author,
  required this.content,
  this.createdAt,
  required this.id,
  required this.title,
});

factory Thread.fromJson(Map<String, dynamic> json) => Thread(
  author: json["author"],
  content: json["content"],
  createdAt: json["created_at"] == null 
    ? null 
    : DateTime.parse(json["created_at"]),
  id: json["id"],
  title: json["title"],
);

Map<String, dynamic> toJson() => {
  "author": author,
  "content": content,
  "created_at": createdAt?.toIso8601String(),
  "id": id,
  "title": title,
};
}

// SURQL Schema constant
const String SURQL_SCHEMA = """
DEFINE TABLE thread SCHEMAFULL
PERMISSIONS
  FOR select WHERE true
  FOR update, delete, create WHERE $access = "account" AND author.id = $auth.id;
  
DEFINE FIELD title ON TABLE thread TYPE string
ASSERT $value != NONE AND string::len($value) > 0;
// ... etc
""";

Usage with SurrealDB Dart Client

Use the generated models with the official SurrealDB Dart client:

lib/database.dart
dart
import 'package:surrealdb/surrealdb.dart';
import 'models.dart';

class Database {
late Surreal db;

Future<void> init() async {
  db = Surreal();
  await db.connect('ws://localhost:8000/rpc');
  await db.use('main', 'main');
}

// Query threads
Future<List<Thread>> getThreads() async {
  final result = await db.query(
    'SELECT * FROM thread ORDER BY created_at DESC LIMIT 20'
  );
  
  final List<dynamic> data = result.first;
  return data.map((json) => Thread.fromJson(json)).toList();
}

// Create a thread
Future<Thread> createThread({
  required String title,
  required String content,
  required String authorId,
}) async {
  final result = await db.create('thread', {
    'title': title,
    'content': content,
    'author': authorId,
    'created_at': DateTime.now().toIso8601String(),
  });
  
  return Thread.fromJson(result.first);
}

// Update a thread
Future<void> updateThread(String id, Map<String, dynamic> updates) async {
  await db.merge(id, updates);
}

// Delete a thread
Future<void> deleteThread(String id) async {
  await db.delete(id);
}

// Authenticate
Future<void> signIn(String username, String password) async {
  await db.signin({
    'AC': 'account',
    'username': username,
    'password': password,
  });
}

Future<void> signUp(String username, String password) async {
  await db.signup({
    'AC': 'account',
    'username': username,
    'password': password,
  });
}
}

Flutter UI Example

Here’s how to use the database in a Flutter widget:

lib/screens/threads_screen.dart
dart
import 'package:flutter/material.dart';
import '../database.dart';
import '../models.dart';

class ThreadsScreen extends StatefulWidget {
@override
_ThreadsScreenState createState() => _ThreadsScreenState();
}

class _ThreadsScreenState extends State<ThreadsScreen> {
final Database _db = Database();
List<Thread> _threads = [];
bool _isLoading = true;

@override
void initState() {
  super.initState();
  _loadThreads();
}

Future<void> _loadThreads() async {
  try {
    await _db.init();
    final threads = await _db.getThreads();
    setState(() {
      _threads = threads;
      _isLoading = false;
    });
  } catch (e) {
    print('Error loading threads: $e');
    setState(() => _isLoading = false);
  }
}

@override
Widget build(BuildContext context) {
  if (_isLoading) {
    return Scaffold(
      body: Center(child: CircularProgressIndicator()),
    );
  }
  
  return Scaffold(
    appBar: AppBar(title: Text('Threads')),
    body: ListView.builder(
      itemCount: _threads.length,
      itemBuilder: (context, index) {
        final thread = _threads[index];
        return ListTile(
          title: Text(thread.title),
          subtitle: Text(thread.content),
          trailing: Text(
            thread.createdAt?.toString() ?? '',
            style: TextStyle(fontSize: 12),
          ),
        );
      },
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _createThread,
      child: Icon(Icons.add),
    ),
  );
}

Future<void> _createThread() async {
  // Show dialog and create thread
  // Implementation details omitted for brevity
}
}

Next Steps

  • Check out the SurrealDB Dart client documentation for more details
  • Explore state management solutions like Riverpod for reactive data binding
  • Consider implementing real-time updates using SurrealDB’s LIVE queries