Files
minhthu/lib/core/di/QUICK_REFERENCE.md
2025-10-28 00:09:46 +07:00

12 KiB

Riverpod Providers Quick Reference

Essential Providers at a Glance

Authentication

// Check if user is logged in
final isAuth = ref.watch(isAuthenticatedProvider);

// Get current user
final user = ref.watch(currentUserProvider);

// Login
ref.read(authProvider.notifier).login(username, password);

// Logout
ref.read(authProvider.notifier).logout();

// Check auth on app start
ref.read(authProvider.notifier).checkAuthStatus();

// Listen to auth changes
ref.listen<AuthState>(authProvider, (previous, next) {
  if (next.isAuthenticated) {
    // Navigate to home
  }
  if (next.error != null) {
    // Show error
  }
});

Warehouse

// Load warehouses
ref.read(warehouseProvider.notifier).loadWarehouses();

// Get warehouses list
final warehouses = ref.watch(warehousesListProvider);

// Get selected warehouse
final selected = ref.watch(selectedWarehouseProvider);

// Select a warehouse
ref.read(warehouseProvider.notifier).selectWarehouse(warehouse);

// Clear selection
ref.read(warehouseProvider.notifier).clearSelection();

// Check loading state
final isLoading = ref.watch(isWarehouseLoadingProvider);

// Get error
final error = ref.watch(warehouseErrorProvider);

Products

// Load products
ref.read(productsProvider.notifier).loadProducts(
  warehouseId,
  warehouseName,
  'import', // or 'export'
);

// Get products list
final products = ref.watch(productsListProvider);

// Refresh products
ref.read(productsProvider.notifier).refreshProducts();

// Clear products
ref.read(productsProvider.notifier).clearProducts();

// Check loading state
final isLoading = ref.watch(isProductsLoadingProvider);

// Get products count
final count = ref.watch(productsCountProvider);

// Get operation type
final type = ref.watch(operationTypeProvider);

// Get error
final error = ref.watch(productsErrorProvider);

Widget Setup

ConsumerWidget (Stateless)

class MyPage extends ConsumerWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final data = ref.watch(someProvider);
    return Widget();
  }
}

ConsumerStatefulWidget (Stateful)

class MyPage extends ConsumerStatefulWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  ConsumerState<MyPage> createState() => _MyPageState();
}

class _MyPageState extends ConsumerState<MyPage> {
  @override
  void initState() {
    super.initState();
    // Load data on init
    Future.microtask(() {
      ref.read(someProvider.notifier).loadData();
    });
  }

  @override
  Widget build(BuildContext context) {
    final data = ref.watch(someProvider);
    return Widget();
  }
}

Common Patterns

Pattern 1: Display Loading State

final isLoading = ref.watch(isAuthLoadingProvider);

return isLoading
    ? CircularProgressIndicator()
    : YourContent();

Pattern 2: Handle Errors

final error = ref.watch(authErrorProvider);

return Column(
  children: [
    if (error != null)
      Text(error, style: TextStyle(color: Colors.red)),
    YourContent(),
  ],
);

Pattern 3: Conditional Navigation

ref.listen<AuthState>(authProvider, (previous, next) {
  if (next.isAuthenticated) {
    Navigator.pushReplacementNamed(context, '/home');
  }
});

Pattern 4: Pull to Refresh

RefreshIndicator(
  onRefresh: () async {
    await ref.read(warehouseProvider.notifier).refresh();
  },
  child: ListView(...),
)

Pattern 5: Load Data on Page Open

@override
void initState() {
  super.initState();
  Future.microtask(() {
    ref.read(warehouseProvider.notifier).loadWarehouses();
  });
}

Key Methods by Feature

Auth Methods

  • login(username, password) - Authenticate user
  • logout() - Sign out user
  • checkAuthStatus() - Check if token exists
  • clearError() - Clear error message
  • reset() - Reset to initial state

Warehouse Methods

  • loadWarehouses() - Fetch all warehouses
  • selectWarehouse(warehouse) - Select a warehouse
  • clearSelection() - Clear selected warehouse
  • refresh() - Reload warehouses
  • clearError() - Clear error message
  • reset() - Reset to initial state

Products Methods

  • loadProducts(warehouseId, name, type) - Fetch products
  • refreshProducts() - Reload current products
  • clearProducts() - Clear products list

Provider Types Explained

Provider (Read-only)

// For services, repositories, use cases
final myServiceProvider = Provider<MyService>((ref) {
  return MyService();
});

// Usage
final service = ref.watch(myServiceProvider);

StateNotifierProvider (Mutable State)

// For managing mutable state
final myStateProvider = StateNotifierProvider<MyNotifier, MyState>((ref) {
  return MyNotifier();
});

// Usage - watch state
final state = ref.watch(myStateProvider);

// Usage - call methods
ref.read(myStateProvider.notifier).doSomething();

Ref Methods

ref.watch()

  • Use in build() method
  • Rebuilds widget when provider changes
  • Reactive to state updates
final data = ref.watch(someProvider);

ref.read()

  • Use in event handlers, callbacks
  • One-time read, no rebuild
  • For calling methods
onPressed: () {
  ref.read(authProvider.notifier).login(user, pass);
}

ref.listen()

  • Use for side effects
  • Navigation, dialogs, snackbars
  • Doesn't rebuild widget
ref.listen<AuthState>(authProvider, (previous, next) {
  if (next.error != null) {
    showDialog(...);
  }
});

App Initialization

void main() {
  runApp(
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends ConsumerStatefulWidget {
  @override
  ConsumerState<MyApp> createState() => _MyAppState();
}

class _MyAppState extends ConsumerState<MyApp> {
  @override
  void initState() {
    super.initState();
    // Check auth on app start
    Future.microtask(() {
      ref.read(authProvider.notifier).checkAuthStatus();
    });
  }

  @override
  Widget build(BuildContext context) {
    final isAuthenticated = ref.watch(isAuthenticatedProvider);

    return MaterialApp(
      home: isAuthenticated
          ? WarehouseSelectionPage()
          : LoginPage(),
    );
  }
}

Complete Example Flow

1. Login Page

class LoginPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final isLoading = ref.watch(isAuthLoadingProvider);
    final error = ref.watch(authErrorProvider);

    ref.listen<AuthState>(authProvider, (previous, next) {
      if (next.isAuthenticated) {
        Navigator.pushReplacementNamed(context, '/warehouses');
      }
    });

    return Scaffold(
      body: Column(
        children: [
          if (error != null)
            Text(error, style: TextStyle(color: Colors.red)),
          TextField(controller: usernameController),
          TextField(controller: passwordController, obscureText: true),
          ElevatedButton(
            onPressed: isLoading ? null : () {
              ref.read(authProvider.notifier).login(
                usernameController.text,
                passwordController.text,
              );
            },
            child: isLoading
                ? CircularProgressIndicator()
                : Text('Login'),
          ),
        ],
      ),
    );
  }
}

2. Warehouse Selection Page

class WarehouseSelectionPage extends ConsumerStatefulWidget {
  @override
  ConsumerState<WarehouseSelectionPage> createState() => _State();
}

class _State extends ConsumerState<WarehouseSelectionPage> {
  @override
  void initState() {
    super.initState();
    Future.microtask(() {
      ref.read(warehouseProvider.notifier).loadWarehouses();
    });
  }

  @override
  Widget build(BuildContext context) {
    final warehouses = ref.watch(warehousesListProvider);
    final isLoading = ref.watch(isWarehouseLoadingProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('Select Warehouse'),
        actions: [
          IconButton(
            icon: Icon(Icons.logout),
            onPressed: () {
              ref.read(authProvider.notifier).logout();
            },
          ),
        ],
      ),
      body: isLoading
          ? Center(child: CircularProgressIndicator())
          : ListView.builder(
              itemCount: warehouses.length,
              itemBuilder: (context, index) {
                final warehouse = warehouses[index];
                return ListTile(
                  title: Text(warehouse.name),
                  subtitle: Text(warehouse.code),
                  onTap: () {
                    ref.read(warehouseProvider.notifier)
                        .selectWarehouse(warehouse);
                    Navigator.pushNamed(context, '/operations');
                  },
                );
              },
            ),
    );
  }
}

3. Products Page

class ProductsPage extends ConsumerStatefulWidget {
  final int warehouseId;
  final String warehouseName;
  final String operationType;

  const ProductsPage({
    required this.warehouseId,
    required this.warehouseName,
    required this.operationType,
  });

  @override
  ConsumerState<ProductsPage> createState() => _ProductsPageState();
}

class _ProductsPageState extends ConsumerState<ProductsPage> {
  @override
  void initState() {
    super.initState();
    Future.microtask(() {
      ref.read(productsProvider.notifier).loadProducts(
        widget.warehouseId,
        widget.warehouseName,
        widget.operationType,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    final products = ref.watch(productsListProvider);
    final isLoading = ref.watch(isProductsLoadingProvider);
    final error = ref.watch(productsErrorProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('${widget.warehouseName} - ${widget.operationType}'),
        actions: [
          IconButton(
            icon: Icon(Icons.refresh),
            onPressed: () {
              ref.read(productsProvider.notifier).refreshProducts();
            },
          ),
        ],
      ),
      body: isLoading
          ? Center(child: CircularProgressIndicator())
          : error != null
              ? Center(child: Text(error))
              : RefreshIndicator(
                  onRefresh: () async {
                    await ref.read(productsProvider.notifier)
                        .refreshProducts();
                  },
                  child: ListView.builder(
                    itemCount: products.length,
                    itemBuilder: (context, index) {
                      final product = products[index];
                      return ListTile(
                        title: Text(product.name),
                        subtitle: Text(product.code),
                        trailing: Text('${product.piecesInStock} pcs'),
                      );
                    },
                  ),
                ),
    );
  }
}

Troubleshooting

Provider Not Found

  • Ensure ProviderScope wraps your app in main.dart
  • Check that you're using ConsumerWidget or ConsumerStatefulWidget

State Not Updating

  • Use ref.watch() not ref.read() in build method
  • Verify the provider is actually updating its state

Null Value

  • Check if data is loaded before accessing
  • Use null-safe operators ?. and ??

Infinite Loop

  • Don't call ref.read(provider.notifier).method() directly in build
  • Use Future.microtask() in initState or callbacks

Cheat Sheet

Task Code
Watch state ref.watch(provider)
Read once ref.read(provider)
Call method ref.read(provider.notifier).method()
Listen for changes ref.listen(provider, callback)
Get loading ref.watch(isXxxLoadingProvider)
Get error ref.watch(xxxErrorProvider)
Check auth ref.watch(isAuthenticatedProvider)
Get user ref.watch(currentUserProvider)
Get warehouses ref.watch(warehousesListProvider)
Get products ref.watch(productsListProvider)