supa_helper 0.1.1 copy "supa_helper: ^0.1.1" to clipboard
supa_helper: ^0.1.1 copied to clipboard

A clean and simple wrapper around Supabase for Flutter projects.

supa_helper #

pub version pub points pub monthly downloads MIT license

Supabase is powerful. Using it shouldn't be painful.
supa_helper wraps every Supabase service into a clean, consistent API — so you stop fighting boilerplate and start shipping features.


The problem #

supabase_flutter is great — but using it directly in every project means:

  • Scattered try/catch blocks with no idea which service threw what
  • Rewriting the same CRUD calls across every screen
  • Manually wiring up auth listeners, storage uploads, and realtime subscriptions every single time
  • No consistency — every developer on the team writes it differently

supa_helper fixes all of that.


Before & After #

Authentication #

Before:

try {
  await Supabase.instance.client.auth.signInWithPassword(
    email: email,
    password: password,
  );
} on AuthException catch (e) {
  // generic error — now what?
  print(e.message);
} catch (e) {
  print(e);
}

After:

try {
  await SupaHelper.instance.auth.signInWithEmailAndPassword(
    email: email,
    password: password,
  );
} on SupaAuthException catch (e) {
  // typed, clear, always from auth
  print(e);
}

Database #

Before:

try {
  final data = await Supabase.instance.client
      .from('orders')
      .select()
      .eq('status', 'pending');

  final orders = List<Map<String, dynamic>>.from(data);
} on PostgrestException catch (e) {
  print(e.message);
} catch (e) {
  print(e);
}

After:

try {
  final orders = await SupaHelper.instance.database.GET(
    table: 'orders',
    filter: (q) => q.eq('status', 'pending'),
  );
} on SupaDatabaseException catch (e) {
  print(e);
}

Storage #

Before:

try {
  final path = '${DateTime.now().millisecondsSinceEpoch}.jpg';
  await Supabase.instance.client.storage
      .from('images')
      .upload('avatars/$path', file);

  final url = Supabase.instance.client.storage
      .from('images')
      .getPublicUrl('avatars/$path');
} on StorageException catch (e) {
  print(e.message);
}

After:

try {
  final url = await SupaHelper.instance.storage.uploadAndGetUrl(
    file,
    bucketName: 'images',
    folderName: 'avatars',
  );
} on SupaStorageException catch (e) {
  print(e);
}

Realtime #

Before:

final channel = Supabase.instance.client
    .channel('orders_channel')
    .onPostgresChanges(
      event: PostgresChangeEvent.all,
      schema: 'public',
      table: 'orders',
      callback: (payload) => print(payload),
    )
    .subscribe((status, error) {
      if (error != null) print(error);
    });

// later...
await Supabase.instance.client.removeChannel(channel);

After:

SupaHelper.instance.realtime.subscribeToTable(
  channelName: 'orders_channel',
  schema: 'public',
  table: 'orders',
  callback: (payload) => print(payload),
  onError: (e) => print(e),
);

// later...
SupaHelper.instance.realtime.unsubscribe('orders_channel');

Features #

Service What you get
🔐 Auth Email/password, OTP, social sign-in, auth state listener — all typed
🗄️ Database GET, GET_SINGLE, GET_PAGINATED, INSERT, INSERT_MANY, UPDATE, UPSERT, DELETE, DELETE_MANY, EXISTS, COUNT, RPC
📦 Storage Upload files or bytes, download, delete, signed URLs
📡 Realtime Subscribe/unsubscribe to Postgres changes with error handling built-in

Installation #

dependencies:
  supa_helper: ^latest
flutter pub get

Setup #

One call. Everything is ready.

import 'package:supa_helper/supa_helper.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await SupaHelper.instance.init(
    url: 'YOUR_SUPABASE_URL',
    anonKey: 'YOUR_ANON_KEY',
  );

  runApp(const MyApp());
}

Authentication #

// Sign up
await SupaHelper.instance.auth.createUser(
  email: 'user@example.com',
  password: 'password123',
  metaData: {'name': 'John'},
);

// Sign in
await SupaHelper.instance.auth.signInWithEmailAndPassword(
  email: 'user@example.com',
  password: 'password123',
);

// Sign out
await SupaHelper.instance.auth.signOut();

// Forgot password
await SupaHelper.instance.auth.sendForgetPasswordEmail(
  email: 'user@example.com',
);

// OTP
await SupaHelper.instance.auth.phoneProvider.sendOtp(phone: '+201234567890');
await SupaHelper.instance.auth.phoneProvider.verifyOtp(
  phone: '+201234567890',
  otp: '123456',
);

// Social sign-in
class GoogleAuth implements SupaSocialMediaAuth {
  @override
  OAuthProvider get oAuthProvider => OAuthProvider.google;

  @override
  Future<SocialAuthResult> signIn() async {
    return SocialAuthResult(
      idToken: 'ID_TOKEN',
      accessToken: 'ACCESS_TOKEN',
      email: 'user@gmail.com',
    );
  }
}

await SupaHelper.instance.auth.socialMediaSignIn(
  GoogleAuth(),
  (result) => print('Logged in: ${result.email}'),
);

// Auth state listener
final subscription = SupaHelper.instance.auth.setupAuthListener(
  onSignedIn: (id) => print('Signed in: $id'),
  onSignedOut: () => print('Signed out'),
  onUserUpdated: (id) => print('User updated: $id'),
  onInitialSession: () => print('Session restored'),
  onError: (e) => print('Error: $e'),
);

subscription.cancel(); // cancel when done

Database #

final db = SupaHelper.instance.database;

// GET all
final users = await db.GET(table: 'users');

// GET with filter
final admins = await db.GET(
  table: 'users',
  filter: (q) => q.eq('role', 'admin'),
);

// GET single
final user = await db.GET_SINGLE(
  table: 'users',
  filter: (q) => q.eq('id', '123'),
);

// GET paginated
final page = await db.GET_PAGINATED(
  table: 'orders',
  page: 1,
  perPage: 20,
);
print(page.data);        // rows
print(page.totalCount);  // total rows
print(page.hasMore);     // is there a next page?

// INSERT
await db.INSERT(
  table: 'users',
  data: {'name': 'John', 'email': 'john@example.com'},
);

// INSERT many
await db.INSERT_MANY(
  table: 'users',
  data: [
    {'name': 'Alice'},
    {'name': 'Bob'},
  ],
);

// UPDATE
await db.UPDATE(
  table: 'users',
  data: {'name': 'John Updated'},
  idValue: '123',
);

// UPSERT
await db.UPSERT(
  table: 'users',
  data: {'id': '123', 'name': 'John'},
  idValue: '123',
);

// DELETE one
await db.DELETE(
  table: 'users',
  filter: (q) => q.eq('id', '123'),
);

// DELETE many
await db.DELETE_MANY(
  table: 'orders',
  ids: ['1', '2', '3'],
);

// EXISTS
final exists = await db.EXISTS(
  table: 'users',
  filter: (q) => q.eq('email', 'test@example.com'),
);

// COUNT
final count = await db.COUNT(table: 'users');

// RPC
final result = await db.RPC(
  function: 'my_postgres_function',
  params: {'param1': 'value'},
);

Storage #

final storage = SupaHelper.instance.storage;

// Upload file → get public URL
final url = await storage.uploadAndGetUrl(
  file,
  bucketName: 'images',
  folderName: 'avatars',
);

// Upload bytes → get public URL
final url = await storage.uploadBytesAndGetUrl(
  bytes,
  bucketName: 'images',
  folderName: 'avatars',
  mimeType: 'image/jpeg',
);

// Download
final bytes = await storage.downloadFile(
  bucketName: 'images',
  filePath: 'avatars/IMG123',
);

// Delete
await storage.deleteFile(
  bucketName: 'images',
  filePath: 'avatars/IMG123',
);

// Signed URL
final signedUrl = await storage.createSignedUrl(
  bucketName: 'images',
  filePath: 'avatars/IMG123',
  expiresInSeconds: 3600,
);

Realtime #

final realtime = SupaHelper.instance.realtime;

// Subscribe
realtime.subscribeToTable(
  channelName: 'orders_channel',
  schema: 'public',
  table: 'orders',
  event: PostgresChangeEvent.all,
  callback: (payload) => print('Change: $payload'),
  onError: (e) => print('Error: $e'),
);

// Unsubscribe
realtime.unsubscribe('orders_channel');
realtime.unsubscribeAll();

// Check status
print(realtime.isSubscribed('orders_channel'));
print(realtime.activeChannelsCount);

Error Handling #

No more guessing. Every service throws its own typed exception.

Service Exception
Auth SupaAuthException
Database SupaDatabaseException
Storage SupaStorageException
Realtime SupaRealtimeException
try {
  await SupaHelper.instance.auth.signInWithEmailAndPassword(
    email: 'user@example.com',
    password: 'wrong_password',
  );
} on SupaAuthException catch (e) {
  print('Auth failed: $e');
}

Reset #

await SupaHelper.instance.reset();
// call init() again with new credentials

Contributing #

Found a bug? Have an idea? Open an issue or submit a PR on GitHub — contributions are welcome.


License #

MIT © Abdelrahman Yehia

7
likes
0
points
118
downloads

Publisher

unverified uploader

Weekly Downloads

A clean and simple wrapper around Supabase for Flutter projects.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, supabase_flutter

More

Packages that depend on supa_helper