dynamic_backend_bridge 0.0.7 copy "dynamic_backend_bridge: ^0.0.7" to clipboard
dynamic_backend_bridge: ^0.0.7 copied to clipboard

A dynamic backend switcher for Flutter providing unified, generic Auth and Database bridges supporting Supabase Cloud and Self-Hosted Supabase.

dynamic_backend_bridge #

A Flutter package that provides a dynamic, decoupled interface for switching between Supabase backends (Managed Cloud, Custom Supabase Cloud, and Self-Hosted Docker instances) at runtime. It includes a built-in dark/light themed onboarding wizard UI, a unified Auth layer, a generic map-based Database layer with batch and query filtering support, Riverpod state providers, local & push notifications, and customizable in-app banner alerts.

Features #

  • Runtime Backend Switching: Swap between a Managed Supabase Cloud Backend, a Custom supabase.com project, or a self-hosted Docker VPS instance dynamically without rebuilds.
  • Riverpod State Management: Access authentication state, database repository, theme settings, and notification services via first-class Riverpod providers (core_providers.dart).
  • Unified Authentication: Perform sign-in, sign-up, sign-out, session restoration, and connection health checks via a single abstract interface (AuthRepository).
  • Generic Database Bridge: Read, write, batch upsert/delete, and stream records reactively using a generic, map-based repository interface (DatabaseRepository).
  • Type-Safe Collections (TypedCollection<T>): Wrap the database repository with models to serialize, deserialize, batch process, filter, order, and paginate data.
  • Push & Local Notifications: Built-in timezone-aware local notification scheduling (NotificationService) and Firebase Cloud Messaging integration (RemoteNotificationService).
  • In-App Banner Notifications: Floating overlay banner alerts (AppBannerService) with auto-dismiss timers, action callbacks, and seamless foreground FCM message display.
  • Theme & Appearance Service: Persistent theme mode management (ThemeService, themeModeProvider) and configurable design themes (AppTheme).
  • Ready-to-use UI Components: Clean, responsive UI components including HostingWizard, SignInPage, DynamicProfilePage, and AppBannerWidget.

Backend Modes #

dynamic_backend_bridge supports two runtime deployment modes:

  1. BackendType.managed (Our Managed Cloud): Connects to your organization's default managed Supabase instance. The app developer supplies defaultSupabaseUrl and defaultSupabaseAnonKey programmatically (or via AppEnvironment / --dart-define). End users do not need to configure anything.
  2. BackendType.customSupabase (Your Own Supabase Server): Allows end-users or administrators to enter their own Supabase Project URL & Anon Key via HostingWizard. Works seamlessly for both supabase.com Cloud projects and private self-hosted Docker VPS servers.

Getting Started #

Add the package to your pubspec.yaml dependencies:

dependencies:
  dynamic_backend_bridge: ^0.0.7

Run flutter pub get to install the dependencies.

Usage #

1. Initialize the Bridge #

In your application's main() or bootstrapping sequence, check for any saved configuration and initialize the dynamic backend bridge:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:dynamic_backend_bridge/dynamic_backend_bridge.dart';

// Centralize credentials in a single environment class or --dart-define
class AppEnvironment {
  static const String defaultSupabaseUrl = String.fromEnvironment(
    'SUPABASE_URL',
    defaultValue: 'https://xyzcompany.supabase.co',
  );
  static const String defaultSupabaseAnonKey = String.fromEnvironment(
    'SUPABASE_ANON_KEY',
    defaultValue: 'eyJhbGciOiJIUzI1NiIsInR...',
  );
}

// Helper function to initialize backend
Future<void> initializeBackend(AppConfig config, ProviderContainer container) async {
  await DynamicBackendBridge.initialize(
    config: config,
    container: container,
    defaultSupabaseUrl: AppEnvironment.defaultSupabaseUrl,
    defaultSupabaseAnonKey: AppEnvironment.defaultSupabaseAnonKey,
  );
}

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

  final prefs = await SharedPreferences.getInstance();
  final configService = ConfigService();
  final savedConfig = await configService.getSavedConfig();
  final container = ProviderContainer(
    overrides: [
      sharedPreferencesProvider.overrideWithValue(prefs),
    ],
  );

  if (savedConfig != null) {
    await initializeBackend(savedConfig, container);
  }

  runApp(UncontrolledProviderScope(
    container: container,
    child: MyApp(
      configService: configService,
      initialConfig: savedConfig,
    ),
  ));
}

2. Riverpod State Providers #

Access core backend services anywhere in your widget tree using Riverpod:

// Access database repository
final db = ref.watch(databaseRepositoryProvider);

// Access auth repository
final auth = ref.watch(authRepositoryProvider);

// Listen to auth state stream
final userAsync = ref.watch(currentUserProvider);

// Manage theme mode (System / Light / Dark)
final themeMode = ref.watch(themeModeProvider);
ref.read(themeModeProvider.notifier).setThemeMode(ThemeMode.dark);

// Access notification services
final localNotif = ref.watch(notificationServiceProvider);
final remoteNotif = ref.watch(remoteNotificationServiceProvider);

3. Database Layer & Typed Collections #

Wrap domain models using TypedCollection<T> for type-safe database queries, batch operations, and realtime streaming:

class Task {
  final String id;
  final String title;
  final String userId;
  final bool isCompleted;

  Task({
    required this.id,
    required this.title,
    required this.userId,
    this.isCompleted = false,
  });

  Map<String, dynamic> toMap() => {
    'title': title,
    'userId': userId,
    'is_completed': isCompleted,
  };

  static Task fromMap(Map<String, dynamic> map, String id) => Task(
    id: id,
    title: map['title'] ?? '',
    userId: map['userId'] ?? '',
    isCompleted: map['is_completed'] ?? false,
  );
}

// Instantiate collection wrapper
final taskCollection = TypedCollection<Task>(
  repo: ref.read(databaseRepositoryProvider),
  collectionName: 'tasks',
  toMap: (task) => task.toMap(),
  fromMap: (map, id) => Task.fromMap(map, id),
);

// Save single item
await taskCollection.save(Task(id: '', title: 'Buy milk', userId: 'user123'), 'task-id-1');

// Batch save multiple items
await taskCollection.saveBatch([
  Task(id: '1', title: 'Task 1', userId: 'user123'),
  Task(id: '2', title: 'Task 2', userId: 'user123'),
]);

// Query with filtering, ordering, and pagination
final openTasks = await taskCollection.fetch(
  filters: [
    QueryFilter.eq('userId', 'user123'),
    QueryFilter.eq('is_completed', false),
  ],
  orderBy: 'title',
  ascending: true,
  limit: 20,
);

// Watch realtime updates
final taskStream = taskCollection.watch(
  filters: [QueryFilter.eq('userId', 'user123')],
);

// Batch delete
await taskCollection.deleteBatch(['1', '2']);

4. In-App Banner Notifications #

Display non-blocking in-app alert banners anywhere in your application:

// Success Banner
AppBannerService.showSuccess(context, 'Profile updated successfully!');

// Error Banner
AppBannerService.showError(context, 'Failed to save changes. Please try again.');

// Custom Informational Banner with Tap Handler
AppBannerService.showInfo(
  context,
  title: 'New Update Available',
  body: 'Tap to view release details.',
  duration: const Duration(seconds: 5),
  onTap: () {
    // Navigate to release page
  },
);

// Programmatically dismiss current banner
AppBannerService.hideCurrentBanner();

5. Local & Push Notifications #

Schedule local notifications or configure Firebase Cloud Messaging:

final notifService = ref.read(notificationServiceProvider);

// Schedule a notification in 10 minutes
await notifService.scheduleNotification(
  id: 101,
  title: 'Task Reminder',
  body: 'Time to complete your scheduled task!',
  duration: const Duration(minutes: 10),
);

// Show immediate notification
await notifService.showNotification(
  id: 102,
  title: 'Welcome!',
  body: 'Thank you for joining our platform.',
);

// Cancel notification
await notifService.cancelNotification(101);

6. Built-in UI Components #

Hosting Wizard (Onboarding & Endpoint Configuration)

HostingWizard(
  configService: configService,
  onValidate: (AppConfig config) async {
    try {
      final tempContainer = ProviderContainer();
      await initializeBackend(config, tempContainer);
      final auth = tempContainer.read(authRepositoryProvider);
      return await auth.validateConnection();
    } catch (e) {
      return e.toString();
    }
  },
  onComplete: (AppConfig config) async {
    await DynamicBackendBridge.initialize(
      config: config,
      ref: ref,
      defaultSupabaseUrl: AppEnvironment.defaultSupabaseUrl,
      defaultSupabaseAnonKey: AppEnvironment.defaultSupabaseAnonKey,
    );
  },
)

Dynamic Profile & Appearance Settings Page

DynamicProfilePage(
  configService: configService,
  onBackendConfigPressed: () {
    // Navigate to HostingWizard or config modal
  },
)

Additional Information #

For issues, contributions, or configuration details, please refer to the GitHub repository.

0
likes
150
points
112
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A dynamic backend switcher for Flutter providing unified, generic Auth and Database bridges supporting Supabase Cloud and Self-Hosted Supabase.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

firebase_messaging, flutter, flutter_local_notifications, flutter_riverpod, flutter_secure_storage, flutter_timezone, hooks_riverpod, shared_preferences, supabase_flutter, timezone

More

Packages that depend on dynamic_backend_bridge