contextless_ui 0.1.0 copy "contextless_ui: ^0.1.0" to clipboard
contextless_ui: ^0.1.0 copied to clipboard

A Flutter package for displaying UI components (dialogs, snackbars, toasts, bottom sheets) without BuildContext and managing them by handle or ID.

Contextless UI #

Pub Version License: MIT

A Flutter package for displaying UI components without requiring BuildContext. Show dialogs, snackbars, toasts, and bottom sheets from anywhere in your code.

Demo #

Contextless UI Demo

Features #

  • No BuildContext required - display UI from anywhere (services, controllers, etc.)
  • Unified API for dialogs, snackbars, toasts, and bottom sheets
  • Manage components by handle, ID, or tag
  • Async support with return values
  • Custom styling with decoration models
  • Built-in transitions and animations
  • Event streams for analytics
  • Cross-platform support

Installation #

dependencies:
  contextless_ui: ^0.1.0

Quick Start #

Initialize #

Add ContextlessObserver to your MaterialApp:

import 'package:contextless_ui/contextless_ui.dart';

class MyApp extends StatelessWidget {
  final navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      navigatorObservers: [ContextlessObserver()],
      home: const MyHomePage(),
    );
  }
}

Unified API #

All UI components can be accessed through the unified ContextlessUi class:

// Instead of using separate classes:
ContextlessDialogs.show(...)
ContextlessSnackbars.show(...)
ContextlessToasts.show(...)
ContextlessBottomSheets.show(...)

// Use the unified API:
ContextlessUi.showDialog(...)
ContextlessUi.showSnackbar(...)
ContextlessUi.showToast(...)
ContextlessUi.showBottomSheet(...)

Usage Examples #

Dialogs

void showLoadingDialog() {
  final handle = ContextlessUi.showDialog(
    const Dialog(
      child: Padding(
        padding: EdgeInsets.all(24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            CircularProgressIndicator(),
            SizedBox(height: 16),
            Text('Loading...'),
          ],
        ),
      ),
    ),
  );
  
  Future.delayed(const Duration(seconds: 3), () {
    ContextlessUi.closeDialog(handle);
  });
}

Snackbars

void showNotification() {
  ContextlessUi.showSnackbar(
    const Text('File uploaded successfully!'),
    action: TextButton(
      onPressed: () => openFile(),
      child: const Text('View'),
    ),
    decoration: const SnackbarDecoration(
      backgroundColor: Colors.green,
    ),
  );
}

Toasts

void showToast() {
  ContextlessUi.showToast(
    const Text('Operation completed'),
    iconLeft: const Icon(Icons.check_circle, color: Colors.white),
    decoration: const ToastDecoration(
      backgroundColor: Colors.black87,
      borderRadius: BorderRadius.all(Radius.circular(8)),
    ),
  );
}

Bottom Sheets

void showSettings() {
  ContextlessUi.showBottomSheet(
    Container(
      padding: const EdgeInsets.all(16),
      child: const Text('Settings'),
    ),
    decoration: const BottomSheetDecoration(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
    ),
  );
}

Async Results

Future<String?> pickColor() async {
  return await ContextlessUi.showDialogAsync<String>(
    Dialog(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          ElevatedButton(
            onPressed: () => ContextlessUi.closeAllDialogs('red'),
            child: const Text('Red'),
          ),
          ElevatedButton(
            onPressed: () => ContextlessUi.closeAllDialogs('blue'),
            child: const Text('Blue'),
          ),
        ],
      ),
    ),
  );
}

API Reference #

Dialog API #

// Show a dialog
DialogHandle show(
  Widget dialog, {
  String? id,
  String? tag,
  bool barrierDismissible = true,
  DialogDecoration? decoration,
});

// Async dialog with return value
Future<T?> showAsync<T>(Widget dialog, {...});

// Close methods
ContextlessDialogs.close(handle);
ContextlessDialogs.closeById(id);
ContextlessDialogs.closeByTag(tag);
ContextlessDialogs.closeAll();

Snackbar API #

// Show a snackbar
SnackbarHandle show(
  Widget content, {
  Widget? action,
  Widget? iconLeft,
  Widget? iconRight,
  String? id,
  String? tag,
  Duration duration,
  SnackbarDecoration? decoration,
});

// Decoration model
SnackbarDecoration({
  Color? backgroundColor,
  EdgeInsetsGeometry? margin,
  EdgeInsetsGeometry? padding,
  double? elevation,
  ShapeBorder? shape,
  SnackBarBehavior behavior,
  // ... more properties
});

Toast API #

// Show a toast
ToastHandle show(
  Widget content, {
  Widget? iconLeft,
  Widget? iconRight,
  String? id,
  String? tag,
  Duration duration,
  Alignment alignment,
  ToastDecoration? decoration,
});

// Decoration model
ToastDecoration({
  Color? backgroundColor,
  EdgeInsetsGeometry? padding,
  BorderRadius? borderRadius,
  double? elevation,
});

Bottom Sheet API #

// Show a bottom sheet
BottomSheetHandle show(
  Widget content, {
  String? id,
  String? tag,
  bool isDismissible,
  bool enableDrag,
  BottomSheetDecoration? decoration,
});

// Decoration model
BottomSheetDecoration({
  Color? backgroundColor,
  double? elevation,
  ShapeBorder? shape,
  BoxConstraints? constraints,
});

Advanced Features #

Tag-based Management #

// Group components with tags
ContextlessUi.showDialog(dialog, tag: 'loading');
ContextlessUi.showToast(toast, tag: 'loading');

// Close all components with the same tag
ContextlessUi.closeDialogsByTag('loading');

Event Streams #

// Listen to events
ContextlessDialogs.events.listen((event) {
  print('Dialog ${event.handle.id} ${event.type}');
});

Custom Transitions #

// Built-in transitions
DialogTransitions.fade
DialogTransitions.slideFromBottom
DialogTransitions.scale

// Use in decoration
ContextlessUi.showDialog(
  dialog,
  decoration: DialogDecoration(
    transitionsBuilder: DialogTransitions.fade,
  ),
);

Mixed Component Usage #

void showMixedComponents() {
  // Show multiple component types together
  final snackbar = ContextlessUi.showSnackbar(
    const Text('Background task running'),
    tag: 'background',
  );
  
  final dialog = ContextlessUi.showDialog(
    const ProcessingDialog(),
    tag: 'background',
  );
  
  final toast = ContextlessUi.showToast(
    const Text('Starting process...'),
    tag: 'background',
  );
  
  // Close all background components later
  Timer(const Duration(seconds: 5), () {
    ContextlessUi.closeSnackbarsByTag('background');
    ContextlessUi.closeDialogsByTag('background');
    ContextlessUi.closeToastsByTag('background');
  });
}

Best Practices #

1. Initialize Early #

Always initialize before runApp():

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  
  final navigatorKey = GlobalKey<NavigatorState>();
  ContextlessUi.init(navigatorKey: navigatorKey);
  
  runApp(MyApp(navigatorKey: navigatorKey));
}

2. Use Tags for Organization #

Group related components for easier management:

// Progress components
ContextlessUi.showSnackbar(const Text('Step 1'), tag: 'wizard');
ContextlessUi.showToast(const Text('Step 2'), tag: 'wizard');

// Error components  
ContextlessUi.showSnackbar(const Text('Error'), tag: 'error');

// Close all wizard components when done
ContextlessUi.closeSnackbarsByTag('wizard');
ContextlessUi.closeToastsByTag('wizard');

3. Handle Async Results Properly #

Future<void> showConfirmationDialog() async {
  final confirmed = await ContextlessUi.showDialogAsync<bool>(
    const ConfirmationDialog(),
  );
  
  if (confirmed == true) {
    // User confirmed
    await performAction();
  }
  // Handle null (dismissed) vs false (cancelled)
}

4. Use Consistent Styling Patterns #

// Instead of manually creating styled snackbars
ContextlessUi.showSnackbar(
  const Text('Success!'),
  decoration: const SnackbarDecoration(
    backgroundColor: Colors.green,
  ),
);

// For common patterns, create helper functions
void showSuccessMessage(String message) {
  ContextlessUi.showSnackbar(
    Text(message),
    iconLeft: const Icon(Icons.check_circle, color: Colors.white),
    decoration: const SnackbarDecoration(
      backgroundColor: Colors.green,
    ),
  );
}

5. Listen to Events for Analytics #

void initializeAnalytics() {
  ContextlessUi.events.listen((event) {
    analytics.track('ui_${event.type.name}', {
      'component_type': event.handle.type.name,
      'component_id': event.handle.id,
      'component_tag': event.handle.tag,
      'result': event.result,
    });
  });
}

6. Cleanup on App Disposal #

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void dispose() {
    ContextlessUi.dispose();


```dart
// Wrong
runApp(const MyApp());
ContextlessUi.init(navigatorKey: key); // Too late!

// Correct  
ContextlessUi.init(navigatorKey: key);
runApp(MyApp(navigatorKey: key));

Ensure the same key is passed to both init() and MaterialApp:

final navigatorKey = GlobalKey<NavigatorState>(); // Create once

ContextlessUi.init(navigatorKey: navigatorKey); // Use same key
runApp(MaterialApp(navigatorKey: navigatorKey, ...)); // Use same key

Components Not Appearing #

Check that:

  1. You've called init() with a valid key
  2. The widget tree has been built at least once
  3. You're not calling from an isolate without proper context

Memory Leaks #

Always dispose the system when your app shuts down:

@override
void dispose() {
  ContextlessUi.dispose(); // This closes all components and cleans up
  super.dispose();
}

Component Types #

Component Use Case Key Features
Dialog Modal interactions Barrier, transitions, blocking
Snackbar Status updates Material Design, actions, auto-dismiss
Toast Simple notifications Lightweight, flexible positioning
Bottom Sheet Options, forms Material Design, drag support

Platform Support #

  • Android - Full support with Material Design
  • iOS - Full support with Cupertino styling
  • Web - Full support with responsive design
  • Desktop - Windows, macOS, Linux support
  • Embedded - Flutter embedded platforms

Examples #

Check out the /example folder for a complete working example showcasing:

  • All component types in action
  • Async dialogs with results
  • Tag-based component management
  • Custom styling and transitions
  • Service layer integration
  • Builder patterns
  • Event stream usage

License #

MIT License - see the LICENSE file for details.

Contributing #

Contributions are welcome. Fork the repository, create a feature branch, add tests, and submit a pull request.

0
likes
150
points
100
downloads

Publisher

verified publishertegar.site

Weekly Downloads

A Flutter package for displaying UI components (dialogs, snackbars, toasts, bottom sheets) without BuildContext and managing them by handle or ID.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on contextless_ui