# Quick Start Guide - Riverpod 3.0 Providers ## Setup Complete! ✅ All Riverpod 3.0 providers have been successfully implemented and code has been generated. --- ## Quick Import Reference ### Import All Cart Providers ```dart import 'package:retail/features/home/presentation/providers/providers.dart'; ``` ### Import All Product Providers ```dart import 'package:retail/features/products/presentation/providers/providers.dart'; ``` ### Import All Category Providers ```dart import 'package:retail/features/categories/presentation/providers/providers.dart'; ``` ### Import All Settings Providers ```dart import 'package:retail/features/settings/presentation/providers/providers.dart'; ``` ### Import Core Providers (Sync, Network) ```dart import 'package:retail/core/providers/providers.dart'; ``` --- ## Usage Examples ### 1. Display Products ```dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:retail/features/products/presentation/providers/providers.dart'; class ProductsPage extends ConsumerWidget { const ProductsPage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final productsAsync = ref.watch(productsProvider); return Scaffold( appBar: AppBar(title: const Text('Products')), body: productsAsync.when( data: (products) => GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 0.75, ), itemCount: products.length, itemBuilder: (context, index) { final product = products[index]; return Card( child: Column( children: [ Text(product.name), Text('\$${product.price.toStringAsFixed(2)}'), ElevatedButton( onPressed: () { ref.read(cartProvider.notifier).addItem(product, 1); }, child: const Text('Add to Cart'), ), ], ), ); }, ), loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => Center(child: Text('Error: $error')), ), ); } } ``` --- ### 2. Search and Filter Products ```dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:retail/features/products/presentation/providers/providers.dart'; import 'package:retail/features/categories/presentation/providers/providers.dart'; class FilteredProductsPage extends ConsumerWidget { const FilteredProductsPage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final filteredProducts = ref.watch(filteredProductsProvider); final searchQuery = ref.watch(searchQueryProvider); final categoriesAsync = ref.watch(categoriesProvider); return Scaffold( appBar: AppBar( title: const Text('Products'), bottom: PreferredSize( preferredSize: const Size.fromHeight(60), child: Padding( padding: const EdgeInsets.all(8.0), child: TextField( decoration: const InputDecoration( hintText: 'Search products...', prefixIcon: Icon(Icons.search), border: OutlineInputBorder(), ), onChanged: (value) { ref.read(searchQueryProvider.notifier).setQuery(value); }, ), ), ), ), body: Column( children: [ // Category filter chips categoriesAsync.when( data: (categories) => SizedBox( height: 50, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: categories.length + 1, itemBuilder: (context, index) { if (index == 0) { return Padding( padding: const EdgeInsets.all(4.0), child: FilterChip( label: const Text('All'), selected: ref.watch(selectedCategoryProvider) == null, onSelected: (_) { ref.read(selectedCategoryProvider.notifier).clearSelection(); }, ), ); } final category = categories[index - 1]; return Padding( padding: const EdgeInsets.all(4.0), child: FilterChip( label: Text(category.name), selected: ref.watch(selectedCategoryProvider) == category.id, onSelected: (_) { ref.read(selectedCategoryProvider.notifier).selectCategory(category.id); }, ), ); }, ), ), loading: () => const SizedBox.shrink(), error: (_, __) => const SizedBox.shrink(), ), // Products grid Expanded( child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, ), itemCount: filteredProducts.length, itemBuilder: (context, index) { final product = filteredProducts[index]; return Card( child: Column( children: [ Text(product.name), Text('\$${product.price}'), ], ), ); }, ), ), ], ), ); } } ``` --- ### 3. Shopping Cart ```dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:retail/features/home/presentation/providers/providers.dart'; class CartPage extends ConsumerWidget { const CartPage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final cartItems = ref.watch(cartProvider); final cartTotal = ref.watch(cartTotalProvider); return Scaffold( appBar: AppBar( title: Text('Cart (${cartTotal.itemCount})'), actions: [ IconButton( icon: const Icon(Icons.delete_outline), onPressed: () { ref.read(cartProvider.notifier).clearCart(); }, ), ], ), body: Column( children: [ Expanded( child: ListView.builder( itemCount: cartItems.length, itemBuilder: (context, index) { final item = cartItems[index]; return ListTile( title: Text(item.productName), subtitle: Text('\$${item.price.toStringAsFixed(2)}'), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.remove), onPressed: () { ref.read(cartProvider.notifier).decrementQuantity(item.productId); }, ), Text('${item.quantity}'), IconButton( icon: const Icon(Icons.add), onPressed: () { ref.read(cartProvider.notifier).incrementQuantity(item.productId); }, ), IconButton( icon: const Icon(Icons.delete), onPressed: () { ref.read(cartProvider.notifier).removeItem(item.productId); }, ), ], ), ); }, ), ), // Cart summary Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('Subtotal:'), Text('\$${cartTotal.subtotal.toStringAsFixed(2)}'), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Tax (${(cartTotal.taxRate * 100).toStringAsFixed(0)}%):'), Text('\$${cartTotal.tax.toStringAsFixed(2)}'), ], ), const Divider(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('Total:', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), Text('\$${cartTotal.total.toStringAsFixed(2)}', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), ], ), const SizedBox(height: 16), ElevatedButton( onPressed: cartItems.isEmpty ? null : () { // Handle checkout }, child: const Text('Checkout'), ), ], ), ), ), ], ), ); } } ``` --- ### 4. Settings Page ```dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:retail/features/settings/presentation/providers/providers.dart'; class SettingsPage extends ConsumerWidget { const SettingsPage({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final settingsAsync = ref.watch(settingsProvider); final themeMode = ref.watch(themeModeProvider); return Scaffold( appBar: AppBar(title: const Text('Settings')), body: settingsAsync.when( data: (settings) => ListView( children: [ // Theme settings ListTile( title: const Text('Theme'), subtitle: Text(themeMode.toString().split('.').last), trailing: SegmentedButton( segments: const [ ButtonSegment(value: ThemeMode.light, label: Text('Light')), ButtonSegment(value: ThemeMode.dark, label: Text('Dark')), ButtonSegment(value: ThemeMode.system, label: Text('System')), ], selected: {themeMode}, onSelectionChanged: (Set newSelection) { ref.read(settingsProvider.notifier).updateThemeMode(newSelection.first); }, ), ), // Language ListTile( title: const Text('Language'), subtitle: Text(settings.language), trailing: DropdownButton( value: settings.language, items: ref.watch(supportedLanguagesProvider).map((lang) { return DropdownMenuItem( value: lang.code, child: Text(lang.nativeName), ); }).toList(), onChanged: (value) { if (value != null) { ref.read(settingsProvider.notifier).updateLanguage(value); } }, ), ), // Tax rate ListTile( title: const Text('Tax Rate'), subtitle: Text('${(settings.taxRate * 100).toStringAsFixed(1)}%'), trailing: SizedBox( width: 100, child: TextField( keyboardType: TextInputType.number, decoration: const InputDecoration(suffix: Text('%')), onSubmitted: (value) { final rate = double.tryParse(value); if (rate != null) { ref.read(settingsProvider.notifier).updateTaxRate(rate / 100); } }, ), ), ), // Store name ListTile( title: const Text('Store Name'), subtitle: Text(settings.storeName), trailing: IconButton( icon: const Icon(Icons.edit), onPressed: () { // Show dialog to edit }, ), ), ], ), loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => Center(child: Text('Error: $error')), ), ); } } ``` --- ### 5. Sync Data ```dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:retail/core/providers/providers.dart'; class SyncButton extends ConsumerWidget { const SyncButton({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final syncAsync = ref.watch(syncStatusProvider); final lastSync = ref.watch(lastSyncTimeProvider); return syncAsync.when( data: (syncResult) { if (syncResult.isSyncing) { return const CircularProgressIndicator(); } return Column( children: [ ElevatedButton.icon( icon: const Icon(Icons.sync), label: const Text('Sync Data'), onPressed: () { ref.read(syncStatusProvider.notifier).syncAll(); }, ), if (lastSync != null) Text( 'Last synced: ${lastSync.toString()}', style: Theme.of(context).textTheme.bodySmall, ), if (syncResult.isOffline) const Text( 'Offline - No internet connection', style: TextStyle(color: Colors.orange), ), if (syncResult.isFailed) Text( 'Sync failed: ${syncResult.message}', style: const TextStyle(color: Colors.red), ), if (syncResult.isSuccess) const Text( 'Sync successful', style: TextStyle(color: Colors.green), ), ], ); }, loading: () => const CircularProgressIndicator(), error: (error, stack) => Text('Error: $error'), ); } } ``` --- ### 6. Main App Setup ```dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:retail/features/settings/presentation/providers/providers.dart'; void main() { runApp( // Wrap entire app with ProviderScope const ProviderScope( child: MyApp(), ), ); } class MyApp extends ConsumerWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final themeMode = ref.watch(themeModeProvider); return MaterialApp( title: 'Retail POS', themeMode: themeMode, theme: ThemeData.light(), darkTheme: ThemeData.dark(), home: const HomePage(), ); } } ``` --- ## Common Patterns ### Pattern 1: Optimized Watching (Selective Rebuilds) ```dart // Bad - rebuilds on any cart change final cart = ref.watch(cartProvider); // Good - rebuilds only when length changes final itemCount = ref.watch(cartProvider.select((items) => items.length)); ``` ### Pattern 2: Async Operations ```dart // Always use AsyncValue.guard for error handling Future syncData() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { return await dataSource.fetchData(); }); } ``` ### Pattern 3: Listening to Changes ```dart ref.listen(cartProvider, (previous, next) { if (next.isNotEmpty && previous?.isEmpty == true) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Item added to cart')), ); } }); ``` ### Pattern 4: Invalidate and Refresh ```dart // Invalidate - resets provider ref.invalidate(productsProvider); // Refresh - invalidate + read immediately final products = ref.refresh(productsProvider); ``` --- ## Testing Providers ```dart import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:retail/features/home/presentation/providers/providers.dart'; void main() { test('Cart adds items correctly', () { final container = ProviderContainer(); addTearDown(container.dispose); // Initial state expect(container.read(cartProvider), isEmpty); // Add item final product = Product(/*...*/); container.read(cartProvider.notifier).addItem(product, 1); // Verify expect(container.read(cartProvider).length, 1); expect(container.read(cartItemCountProvider), 1); }); } ``` --- ## Next Steps 1. ✅ Providers are implemented and generated 2. ✅ All dependencies are installed 3. ✅ Code generation is complete 4. 🔄 Replace mock data sources with Hive implementations 5. 🔄 Build UI pages using the providers 6. 🔄 Add error handling and loading states 7. 🔄 Write tests for providers 8. 🔄 Implement actual API sync --- ## Need Help? - **Full Documentation**: See `PROVIDERS_DOCUMENTATION.md` - **Provider List**: See `PROVIDERS_SUMMARY.md` - **Riverpod Docs**: https://riverpod.dev --- ## All Providers Ready to Use! 🚀 Start building your UI with confidence - all state management is in place!