async_switcher 1.0.0 copy "async_switcher: ^1.0.0" to clipboard
async_switcher: ^1.0.0 copied to clipboard

A smart Flutter widget that automatically switches UI based on async state (loading → empty → error → data). Framework-independent, supports Future & Stream.

Async Switcher #

A smart Flutter widget that automatically switches UI based on async state. Framework-independent and works with any state management solution.

pub package

Features #

  • Automatic State Switching: Loading → Empty → Error → Data
  • Future & Stream Support: Built-in constructors for Future<T> and Stream<T>
  • Animated Transitions: Smooth transitions between states (configurable)
  • Retry Functionality: Built-in retry callback for error states
  • Auto Empty Detection: Automatically detects empty collections
  • Customizable Widgets: Custom widgets for each state
  • Framework Independent: Works with any state management (Bloc, Riverpod, Provider, etc.)
  • Default Widgets: Beautiful default widgets included out of the box

Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  async_switcher: ^1.0.0

Quick Start #

Basic Usage #

import 'package:async_switcher/async_switcher.dart';

StateWrapper<List<Product>>(
  state: productState,
  builder: (context, products) => ProductList(products: products),
  loadingMessage: 'Loading products...',
  emptyMessage: 'No products found',
  errorMessage: 'Failed to load products',
  onRetry: () => loadProducts(),
)

With Future #

StateWrapper.fromFuture<List<Product>>(
  future: fetchProducts(),
  builder: (context, products) => ProductList(products: products),
  loadingMessage: 'Loading...',
  emptyMessage: 'No products',
  onRetry: () => loadProducts(),
)

With Stream #

StateWrapper.fromStream<List<Product>>(
  stream: productsStream(),
  builder: (context, products) => ProductList(products: products),
  loadingMessage: 'Loading...',
  onRetry: () => resubscribe(),
)

API Reference #

StateWrapper #

The main widget that switches UI based on AsyncValue<T> state.

Constructor Parameters

Parameter Type Description
state AsyncValue<T> The current async state
builder Widget Function(BuildContext, T) Builder for data state (required)
loading Widget? Custom loading widget
empty Widget? Custom empty widget
error Widget? Custom error widget
onRetry VoidCallback? Retry callback for error state
animate bool Enable animated transitions (default: true)
transitionDuration Duration Transition duration (default: 300ms)
transitionCurve Curve Transition curve (default: Curves.easeInOut)
loadingMessage String? Message for default loading widget
emptyMessage String? Message for default empty widget
errorMessage String? Message for default error widget
detectEmpty bool Auto-detect empty collections (default: true)
isEmptyPredicate bool Function(T)? Custom empty detection predicate

AsyncValue #

Sealed class representing async states:

  • AsyncLoading<T>() - Loading state
  • AsyncData<T>(T data) - Success state with data
  • AsyncEmpty<T>() - Empty state (no data)
  • AsyncError<T>(String message, {Object? error, StackTrace? stackTrace}) - Error state

Helper Methods

// Check state type
state.isLoading  // bool
state.hasData    // bool
state.isEmpty    // bool
state.isError    // bool

// Get data if available
final data = state.dataOrNull; // T?

// Map over data
final newState = state.map((data) => data.length);

Customization Examples #

Custom Loading Widget #

StateWrapper<List<Product>>(
  state: productState,
  builder: (context, products) => ProductList(products: products),
  loading: ShimmerLoader(), // Your custom shimmer
)

Custom Empty Widget #

StateWrapper<List<Product>>(
  state: productState,
  builder: (context, products) => ProductList(products: products),
  empty: Center(
    child: Column(
      children: [
        Icon(Icons.shopping_bag_outlined, size: 64),
        Text('Your cart is empty'),
      ],
    ),
  ),
)

Custom Error Widget #

StateWrapper<List<Product>>(
  state: productState,
  builder: (context, products) => ProductList(products: products),
  error: CustomErrorView(
    message: 'Failed to load',
    onRetry: () => loadProducts(),
  ),
)

Disable Animations #

StateWrapper<List<Product>>(
  state: productState,
  builder: (context, products) => ProductList(products: products),
  animate: false,
)

Custom Empty Detection #

StateWrapper<Map<String, dynamic>>(
  state: mapState,
  builder: (context, map) => MapView(map: map),
  isEmptyPredicate: (map) => map.isEmpty, // Custom check
)

State Management Examples #

With Riverpod #

final productsProvider = FutureProvider<List<Product>>((ref) async {
  return await fetchProducts();
});

// In widget
StateWrapper<List<Product>>(
  state: ref.watch(productsProvider).when(
    data: (data) => AsyncData(data),
    loading: () => AsyncLoading(),
    error: (err, stack) => AsyncError(err.toString()),
  ),
  builder: (context, products) => ProductList(products: products),
)

With Bloc #

// In your bloc
Stream<List<Product>> mapEventToState(ProductEvent event) async* {
  yield ProductLoading();
  try {
    final products = await repository.fetchProducts();
    yield ProductLoaded(products);
  } catch (e) {
    yield ProductError(e.toString());
  }
}

// In widget
StateWrapper<List<Product>>(
  state: state.when(
    loading: () => AsyncLoading(),
    loaded: (products) => AsyncData(products),
    error: (message) => AsyncError(message),
  ),
  builder: (context, products) => ProductList(products: products),
)

With Provider/ChangeNotifier #

class ProductNotifier extends ChangeNotifier {
  AsyncValue<List<Product>> state = AsyncLoading();

  Future<void> loadProducts() async {
    state = AsyncLoading();
    notifyListeners();

    try {
      final products = await repository.fetchProducts();
      state = AsyncData(products);
    } catch (e) {
      state = AsyncError(e.toString());
    }
    notifyListeners();
  }
}

// In widget
StateWrapper<List<Product>>(
  state: context.watch<ProductNotifier>().state,
  builder: (context, products) => ProductList(products: products),
  onRetry: () => context.read<ProductNotifier>().loadProducts(),
)

Use Cases #

  • API Call Screens - Handle loading/error/data states automatically
  • List Screens - Perfect for product lists, user lists, etc.
  • Grid Views - Image galleries, product grids
  • Search Results - Empty states when no results found
  • Pagination - Loading more items
  • Firebase Streams - Real-time data from Firestore
  • Forms - Async validation states

Architecture #

The package uses a sealed class hierarchy for type-safe state management:

AsyncValue<T>
├── AsyncLoading<T>
├── AsyncData<T>
├── AsyncEmpty<T>
└── AsyncError<T>

This ensures exhaustive pattern matching and prevents invalid states.

Additional Resources #

  • Example App - Complete working examples
  • CHANGELOG - Version history
  • API Documentation - Full API docs

Testing #

The package includes comprehensive tests. Run them with:

flutter test

Contributing #

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

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License #

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

Acknowledgments #

Inspired by Riverpod's AsyncValue but designed to be framework-independent and more flexible.

Package Health #

This package aims for high pub.flutter-io.cn scores with:

  • Comprehensive documentation
  • Full test coverage
  • Example application
  • Type-safe API
  • Framework independence
1
likes
150
points
43
downloads

Publisher

unverified uploader

Weekly Downloads

A smart Flutter widget that automatically switches UI based on async state (loading → empty → error → data). Framework-independent, supports Future & Stream.

Homepage
Repository (GitLab)
View/report issues

Topics

#flutter #widget #async #state-management #ui

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on async_switcher