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.

example/lib/main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:async_switcher/async_switcher.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Async Switcher Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Async Switcher Examples'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildExampleCard(
            context,
            title: 'Example 1: Basic Usage',
            subtitle: 'Manual state management with StateWrapper',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const BasicExample()),
            ),
          ),
          const SizedBox(height: 16),
          _buildExampleCard(
            context,
            title: 'Example 2: Future Integration',
            subtitle: 'Using StateWrapper.fromFuture()',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const FutureExample()),
            ),
          ),
          const SizedBox(height: 16),
          _buildExampleCard(
            context,
            title: 'Example 3: Stream Integration',
            subtitle: 'Using StateWrapper.fromStream()',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const StreamExample()),
            ),
          ),
          const SizedBox(height: 16),
          _buildExampleCard(
            context,
            title: 'Example 4: Custom Widgets',
            subtitle: 'Custom loading, empty, and error widgets',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const CustomWidgetsExample()),
            ),
          ),
          const SizedBox(height: 16),
          _buildExampleCard(
            context,
            title: 'Example 5: Product List',
            subtitle: 'Real-world example with API simulation',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const ProductListExample()),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildExampleCard(
    BuildContext context, {
    required String title,
    required String subtitle,
    required VoidCallback onTap,
  }) {
    return Card(
      elevation: 2,
      child: ListTile(
        title: Text(title),
        subtitle: Text(subtitle),
        trailing: const Icon(Icons.arrow_forward_ios),
        onTap: onTap,
      ),
    );
  }
}

// Example 1: Basic Usage
class BasicExample extends StatefulWidget {
  const BasicExample({super.key});

  @override
  State<BasicExample> createState() => _BasicExampleState();
}

class _BasicExampleState extends State<BasicExample> {
  AsyncValue<List<String>> _state = const AsyncLoading();

  @override
  void initState() {
    super.initState();
    _loadData();
  }

  Future<void> _loadData() async {
    setState(() => _state = const AsyncLoading());
    await Future.delayed(const Duration(seconds: 1));
    setState(() => _state = AsyncData(['Item 1', 'Item 2', 'Item 3']));
  }

  Future<void> _loadError() async {
    setState(() => _state = const AsyncLoading());
    await Future.delayed(const Duration(seconds: 1));
    setState(() => _state = const AsyncError('Failed to load data'));
  }

  Future<void> _loadEmpty() async {
    setState(() => _state = const AsyncLoading());
    await Future.delayed(const Duration(seconds: 1));
    setState(() => _state = const AsyncEmpty());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Basic Usage')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: _loadData,
                  child: const Text('Load Data'),
                ),
                ElevatedButton(
                  onPressed: _loadError,
                  child: const Text('Load Error'),
                ),
                ElevatedButton(
                  onPressed: _loadEmpty,
                  child: const Text('Load Empty'),
                ),
              ],
            ),
          ),
          Expanded(
            child: StateWrapper<List<String>>(
              state: _state,
              builder: (context, items) => ListView.builder(
                itemCount: items.length,
                itemBuilder: (context, index) => ListTile(
                  title: Text(items[index]),
                ),
              ),
              onRetry: _loadData,
            ),
          ),
        ],
      ),
    );
  }
}

// Example 2: Future Integration
class FutureExample extends StatelessWidget {
  const FutureExample({super.key});

  Future<List<Product>> _fetchProducts() async {
    await Future.delayed(const Duration(seconds: 2));
    return [
      Product(name: 'Product 1', price: 29.99, id: 1),
      Product(name: 'Product 2', price: 39.99, id: 2),
      Product(name: 'Product 3', price: 49.99, id: 3),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Future Integration')),
      body: StateWrapper.fromFuture<List<Product>>(
        future: _fetchProducts(),
        builder: (context, products) => ListView.builder(
          itemCount: products.length,
          itemBuilder: (context, index) {
            final product = products[index];
            return Card(
              margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              child: ListTile(
                title: Text(product.name),
                subtitle: Text('\$${product.price.toStringAsFixed(2)}'),
                leading: CircleAvatar(child: Text('${product.id}')),
              ),
            );
          },
        ),
        loadingMessage: 'Loading products...',
        emptyMessage: 'No products available',
      ),
    );
  }
}

// Example 3: Stream Integration
class StreamExample extends StatelessWidget {
  const StreamExample({super.key});

  Stream<List<String>> _createStream() async* {
    yield* Stream.periodic(const Duration(seconds: 1), (count) => count)
        .asyncMap((count) async {
      await Future.delayed(const Duration(seconds: 1));
      return List.generate(count + 1, (i) => 'Stream Item ${i + 1}');
    }).take(5);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Stream Integration')),
      body: StateWrapper.fromStream<List<String>>(
        stream: _createStream(),
        builder: (context, items) => ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) => ListTile(
            title: Text(items[index]),
            leading: CircleAvatar(child: Text('${index + 1}')),
          ),
        ),
        loadingMessage: 'Waiting for stream data...',
      ),
    );
  }
}

// Example 4: Custom Widgets
class CustomWidgetsExample extends StatefulWidget {
  const CustomWidgetsExample({super.key});

  @override
  State<CustomWidgetsExample> createState() => _CustomWidgetsExampleState();
}

class _CustomWidgetsExampleState extends State<CustomWidgetsExample> {
  AsyncValue<List<String>> _state = const AsyncLoading();

  @override
  void initState() {
    super.initState();
    _loadData();
  }

  Future<void> _loadData() async {
    setState(() => _state = const AsyncLoading());
    await Future.delayed(const Duration(seconds: 2));
    setState(() => _state = AsyncData(['Custom Item 1', 'Custom Item 2']));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Custom Widgets')),
      body: StateWrapper<List<String>>(
        state: _state,
        builder: (context, items) => ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) => ListTile(
            title: Text(items[index]),
          ),
        ),
        loading: const Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              CircularProgressIndicator(),
              SizedBox(height: 16),
              Text('Loading with custom widget...'),
            ],
          ),
        ),
        empty: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.inbox, size: 64, color: Colors.grey),
              const SizedBox(height: 16),
              Text('Nothing to show', style: TextStyle(fontSize: 18)),
            ],
          ),
        ),
        error: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.error_outline, size: 64, color: Colors.red),
              const SizedBox(height: 16),
              Text('Oops! Something went wrong',
                  style: TextStyle(fontSize: 18)),
            ],
          ),
        ),
        onRetry: _loadData,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _loadData,
        child: const Icon(Icons.refresh),
      ),
    );
  }
}

// Example 5: Product List (Real-world example)
class ProductListExample extends StatefulWidget {
  const ProductListExample({super.key});

  @override
  State<ProductListExample> createState() => _ProductListExampleState();
}

class _ProductListExampleState extends State<ProductListExample> {
  Future<List<Product>> _fetchProducts() async {
    await Future.delayed(const Duration(seconds: 2));

    // Simulate API call that might fail
    if (DateTime.now().millisecond % 3 == 0) {
      throw Exception('Network error: Failed to fetch products');
    }

    // Simulate empty result
    if (DateTime.now().millisecond % 2 == 0) {
      return [];
    }

    return [
      Product(name: 'Laptop', price: 999.99, id: 1),
      Product(name: 'Mouse', price: 29.99, id: 2),
      Product(name: 'Keyboard', price: 79.99, id: 3),
      Product(name: 'Monitor', price: 299.99, id: 4),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Product List'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: () => setState(() {}),
          ),
        ],
      ),
      body: StateWrapper.fromFuture<List<Product>>(
        future: _fetchProducts(),
        builder: (context, products) => GridView.builder(
          padding: const EdgeInsets.all(16),
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            childAspectRatio: 0.75,
            crossAxisSpacing: 16,
            mainAxisSpacing: 16,
          ),
          itemCount: products.length,
          itemBuilder: (context, index) {
            final product = products[index];
            return Card(
              elevation: 2,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Expanded(
                    child: Container(
                      color: Colors.blue.shade100,
                      child: Center(
                        child: Text(
                          product.name[0],
                          style: const TextStyle(
                            fontSize: 48,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(8),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          product.name,
                          style: const TextStyle(
                            fontWeight: FontWeight.bold,
                            fontSize: 16,
                          ),
                        ),
                        const SizedBox(height: 4),
                        Text(
                          '\$${product.price.toStringAsFixed(2)}',
                          style: TextStyle(
                            color: Colors.blue.shade700,
                            fontSize: 18,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            );
          },
        ),
        loadingMessage: 'Fetching products...',
        emptyMessage: 'No products available',
        onRetry: () => setState(() {}),
      ),
    );
  }
}

class Product {
  final String name;
  final double price;
  final int id;

  Product({required this.name, required this.price, required this.id});
}
1
likes
150
points
73
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