12 KiB
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 userlogout()- Sign out usercheckAuthStatus()- Check if token existsclearError()- Clear error messagereset()- Reset to initial state
Warehouse Methods
loadWarehouses()- Fetch all warehousesselectWarehouse(warehouse)- Select a warehouseclearSelection()- Clear selected warehouserefresh()- Reload warehousesclearError()- Clear error messagereset()- Reset to initial state
Products Methods
loadProducts(warehouseId, name, type)- Fetch productsrefreshProducts()- Reload current productsclearProducts()- 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
ProviderScopewraps your app inmain.dart - Check that you're using
ConsumerWidgetorConsumerStatefulWidget
State Not Updating
- Use
ref.watch()notref.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) |