Skip to content

A codegen script that grabs and generates classes and functions from Supabase tables, structs, enums, and utils, in Dart. Can be used to port from FlutterFlow to Flutter.

Notifications You must be signed in to change notification settings

Kemerd/supabase-flutter-codegen

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🚀 Supabase Dart Model Generator

Generate type-safe Dart models from your Supabase tables automatically! This tool helps you port projects to Flutter (from FlutterFlow) or create new Flutter projects with full type safety and Supabase integration.

✨ Features

  • Automatically generates Dart classes from Supabase tables
  • Creates type-safe models with full IDE support
  • Supports complex relationships and nested structures
  • Compatible with Flutter and Flutter Flow paradigms
  • Generates getters and setters for all fields

📋 Prerequisites

  • Supabase project with tables
  • Dart/Flutter development environment
  • Environment configuration file (env.dart)

🛠️ Setup

  1. Add the generator to your project's root directory
  2. Create an env.dart file with your Supabase credentials:
const supabaseUrl = 'YOUR_SUPABASE_URL';
const supabaseAnonKey = 'YOUR_SUPABASE_ANON_KEY';
  1. Run the generation script:
# On Unix-like systems
./generate_models.sh

# On Windows
generate_models.bat
  1. Setup your preferred auth in Supabase.dart, as well as your keys
    String _kSupabaseUrl = 'XXXXXXXXXXXXXX';
    String _kSupabaseAnonKey = 'XXXXXXXXXXXXXX';

    ...

    if (session != null) {
      print('[Supabase] User ID: ${session.user.id}');
      // Create auth user wrapper and update app state
      // Initialize your auth here
      //final authUser = FlutterAppSupabaseUser(session.user);
      //AppStateNotifier.instance.update(authUser);
    }

You can optionally use the auth classes I've provided in lib/auth but it is not required.

📦 Generated Types

The generator will create strongly-typed models like this:

class UsersTable extends SupabaseTable<UsersRow> {
  @override
  String get tableName => 'users';
  
  @override
  UsersRow createRow(Map<String, dynamic> data) => UsersRow(data);
}

class UsersRow extends SupabaseDataRow {
  UsersRow(super.data);
  
  @override
  SupabaseTable get table => UsersTable();
  
  String get id => getField<String>('id')!;
  set id(String value) => setField<String>('id', value);
  
  String? get name => getField<String>('name');
  set name(String? value) => setField<String>('name', value);
  
  DateTime get createdAt => getField<DateTime>('created_at')!;
  set createdAt(DateTime value) => setField<DateTime>('created_at', value);
}

🚀 Usage Examples

Reading Data

final userAccountsTable = UserAccountsTable();

// Fetch a single user
final users = await userAccountsTable.queryRows(
  queryFn: (q) => q.eq('id', 123),
  limit: 1,
);

if (users.isNotEmpty) {
  final user = users.first;
  // Access typed properties
  print(user.email);
  print(user.accName);
  print(user.phoneNumber);
  print(user.createdAt);
}

// Fetch multiple users
final activeUsers = await userAccountsTable.queryRows(
  queryFn: (q) => q
  .eq('is_active', true)
  .order('email'),
);

// Work with typed objects
for (final user in activeUsers) {
  print('User ${user.id}:');
  print('- Email: ${user.email}');
  print('- Name: ${user.accName ?? "No name set"}');
  print('- Phone: ${user.phoneNumber ?? "No phone set"}');
  print('- Created: ${user.createdAt}');
}

// Query with complex conditions
final recentUsers = await userAccountsTable.queryRows(
  queryFn: (q) => q
  .gte('created_at', DateTime.now().subtract(Duration(days: 7)))
  .ilike('email', '%@gmail.com')
  .order('created_at', ascending: false),
);

Creating Records

final userAccountsTable = UserAccountsTable();

// Create new record
final newUser = await userAccountsTable.insert({
  'email': '[email protected]',
  'acc_name': 'John Doe',
  'phone_number': '+1234567890',
});

// The returned object is already typed
print(newUser.email);
print(newUser.accName);

Updating Records

final userAccountsTable = UserAccountsTable();

// Update by query
await userAccountsTable.update(
  data: {'acc_name': 'Jane Doe'},
  matchingRows: (q) => q.eq('id', 123),
);

// Update with return value
final updatedUsers = await userAccountsTable.update(
  data: {'is_active': true},
  matchingRows: (q) => q.in_('id', [1, 2, 3]),
  returnRows: true,
);

Deleting Records

final userAccountsTable = UserAccountsTable();

// Delete single record
  await userAccountsTable.delete(
  matchingRows: (q) => q.eq('id', 123),
);

// Delete with return value
final deletedUsers = await userAccountsTable.delete(
  matchingRows: (q) => q.eq('is_active', false),
  returnRows: true,
);

Working with Related Data

// Get a pilot and their documents
final pilotsTable = PilotsTable();
final documentsTable = DocumentsTable();

// Get pilot
final pilots = await pilotsTable.queryRows(
  queryFn: (q) => q.eq('id', pilotId),
);
final pilot = pilots.firstOrNull;

// Get related documents
if (pilot != null) {
  final documents = await documentsTable.queryRows(
    queryFn: (q) => q.eq('pilot_id', pilot.id),
  );
}

📝 Notes

  • Ensure your Supabase tables have proper primary keys defined
  • All generated models are null-safe
  • Custom column types are supported through type converters

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A codegen script that grabs and generates classes and functions from Supabase tables, structs, enums, and utils, in Dart. Can be used to port from FlutterFlow to Flutter.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages