Flutter Supabase

Open-source Firebase alternative for Flutter apps

🚀 What is Supabase?

Supabase is an open-source backend platform providing authentication, database, storage, and real-time subscriptions. It's a complete Firebase alternative that gives you a PostgreSQL database, instant APIs, and powerful features for Flutter apps.


// Initialize Supabase
import 'package:supabase_flutter/supabase_flutter.dart';

await Supabase.initialize(
  url: 'YOUR_SUPABASE_URL',
  anonKey: 'YOUR_SUPABASE_ANON_KEY',
);

final supabase = Supabase.instance.client;
                                    

Key Supabase Features

🔐

Authentication

Built-in user authentication with email, social logins, and magic links. Secure user management with JWT tokens, password reset, and email verification out of the box.

await supabase.auth.signUp(
  email: '[email protected]',
  password: 'password123',
);
🗄️

Database

PostgreSQL database with instant REST API. Create tables, run queries, and manage data with powerful SQL features and automatic API generation for all tables.

final data = await supabase
  .from('users')
  .select()
  .eq('id', userId);
📁

Storage

File storage for images, videos, and documents. Upload, download, and manage files with built-in CDN, image transformations, and access control policies.

await supabase.storage
  .from('avatars')
  .upload('user1.jpg', file);

Real-time

Live database changes via WebSocket subscriptions. Listen to inserts, updates, and deletes in real-time, perfect for chat apps, collaborative tools, and live dashboards.

supabase
  .from('messages')
  .stream(primaryKey: ['id'])
  .listen((data) { });

🔹 Setting Up Supabase

Add Supabase to your Flutter project:

# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  supabase_flutter: ^2.0.0
// main.dart
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Supabase.initialize(
    url: 'https://your-project.supabase.co',
    anonKey: 'your-anon-key',
  );

  runApp(MyApp());
}

// Access Supabase client anywhere
final supabase = Supabase.instance.client;

Setup steps:

1. Create account at supabase.com

2. Create new project

3. Get URL and anon key from settings

4. Initialize in your Flutter app

🔹 User Authentication

Implement sign up, sign in, and sign out:

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

class AuthScreen extends StatefulWidget {
  @override
  _AuthScreenState createState() => _AuthScreenState();
}

class _AuthScreenState extends State<AuthScreen> {
  final emailController = TextEditingController();
  final passwordController = TextEditingController();
  final supabase = Supabase.instance.client;
  bool isLoading = false;

  Future<void> signUp() async {
    setState(() => isLoading = true);
    try {
      await supabase.auth.signUp(
        email: emailController.text,
        password: passwordController.text,
      );
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Check your email for confirmation!')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error: $e')),
      );
    } finally {
      setState(() => isLoading = false);
    }
  }

  Future<void> signIn() async {
    setState(() => isLoading = true);
    try {
      await supabase.auth.signInWithPassword(
        email: emailController.text,
        password: passwordController.text,
      );
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (context) => HomeScreen()),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error: $e')),
      );
    } finally {
      setState(() => isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Authentication')),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: emailController,
              decoration: InputDecoration(labelText: 'Email'),
            ),
            SizedBox(height: 16),
            TextField(
              controller: passwordController,
              decoration: InputDecoration(labelText: 'Password'),
              obscureText: true,
            ),
            SizedBox(height: 24),
            if (isLoading)
              CircularProgressIndicator()
            else
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  ElevatedButton(
                    onPressed: signUp,
                    child: Text('Sign Up'),
                  ),
                  ElevatedButton(
                    onPressed: signIn,
                    child: Text('Sign In'),
                  ),
                ],
              ),
          ],
        ),
      ),
    );
  }
}

Features:

✓ Email/password authentication

✓ Email verification

✓ Loading states

✓ Error handling

🔹 Database Operations

Perform CRUD operations on your database:

// Create (Insert)
Future<void> createNote(String title, String content) async {
  await supabase.from('notes').insert({
    'title': title,
    'content': content,
    'user_id': supabase.auth.currentUser!.id,
  });
}

// Read (Select)
Future<List<Map<String, dynamic>>> getNotes() async {
  final response = await supabase
      .from('notes')
      .select()
      .eq('user_id', supabase.auth.currentUser!.id)
      .order('created_at', ascending: false);
  return response as List<Map<String, dynamic>>;
}

// Update
Future<void> updateNote(int id, String title, String content) async {
  await supabase.from('notes').update({
    'title': title,
    'content': content,
  }).eq('id', id);
}

// Delete
Future<void> deleteNote(int id) async {
  await supabase.from('notes').delete().eq('id', id);
}

// Usage in a widget
class NotesScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('My Notes')),
      body: FutureBuilder<List<Map<String, dynamic>>>(
        future: getNotes(),
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Center(child: CircularProgressIndicator());
          }

          final notes = snapshot.data!;

          return ListView.builder(
            itemCount: notes.length,
            itemBuilder: (context, index) {
              final note = notes[index];
              return ListTile(
                title: Text(note['title']),
                subtitle: Text(note['content']),
                trailing: IconButton(
                  icon: Icon(Icons.delete),
                  onPressed: () => deleteNote(note['id']),
                ),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => createNote('New Note', 'Content here'),
        child: Icon(Icons.add),
      ),
    );
  }
}

🔹 Real-time Subscriptions

Listen to database changes in real-time:

class RealtimeNotesScreen extends StatefulWidget {
  @override
  _RealtimeNotesScreenState createState() => _RealtimeNotesScreenState();
}

class _RealtimeNotesScreenState extends State<RealtimeNotesScreen> {
  final supabase = Supabase.instance.client;
  List<Map<String, dynamic>> notes = [];

  @override
  void initState() {
    super.initState();
    _loadNotes();
    _subscribeToNotes();
  }

  Future<void> _loadNotes() async {
    final data = await supabase
        .from('notes')
        .select()
        .order('created_at', ascending: false);
    setState(() => notes = List<Map<String, dynamic>>.from(data));
  }

  void _subscribeToNotes() {
    supabase
        .from('notes')
        .stream(primaryKey: ['id'])
        .listen((data) {
          setState(() {
            notes = List<Map<String, dynamic>>.from(data);
          });
        });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Real-time Notes')),
      body: ListView.builder(
        itemCount: notes.length,
        itemBuilder: (context, index) {
          final note = notes[index];
          return Card(
            child: ListTile(
              title: Text(note['title']),
              subtitle: Text(note['content']),
            ),
          );
        },
      ),
    );
  }
}

Real-time updates:

✓ Automatic UI updates on data changes

✓ No manual refresh needed

✓ Perfect for collaborative apps

🔹 File Storage

Upload and download files:

import 'dart:io';
import 'package:image_picker/image_picker.dart';

class StorageExample extends StatelessWidget {
  final supabase = Supabase.instance.client;

  Future<void> uploadAvatar() async {
    final picker = ImagePicker();
    final image = await picker.pickImage(source: ImageSource.gallery);
    
    if (image == null) return;

    final file = File(image.path);
    final userId = supabase.auth.currentUser!.id;
    final fileName = '$userId.jpg';

    await supabase.storage
        .from('avatars')
        .upload(fileName, file, fileOptions: FileOptions(upsert: true));

    // Get public URL
    final imageUrl = supabase.storage
        .from('avatars')
        .getPublicUrl(fileName);

    print('Image uploaded: $imageUrl');
  }

  Future<void> downloadFile(String fileName) async {
    final data = await supabase.storage
        .from('avatars')
        .download(fileName);
    
    // Save to device or display
    print('File downloaded: ${data.length} bytes');
  }

  Future<void> deleteFile(String fileName) async {
    await supabase.storage
        .from('avatars')
        .remove([fileName]);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('File Storage')),
      body: Center(
        child: ElevatedButton(
          onPressed: uploadAvatar,
          child: Text('Upload Avatar'),
        ),
      ),
    );
  }
}

🧠 Test Your Knowledge

What database does Supabase use?