13 KiB
Riverpod 3.0 Setup Summary - Worker Flutter App
✅ Setup Complete!
Riverpod 3.0 with code generation has been successfully configured for the Worker Flutter app.
What Was Configured
1. Dependencies Updated (pubspec.yaml)
Production:
flutter_riverpod: ^3.0.0- Core Riverpod package for Flutterriverpod_annotation: ^3.0.0- Annotations for code generation
Development:
build_runner: ^2.4.11- Code generation engineriverpod_generator: ^3.0.0- Generates provider coderiverpod_lint: ^3.0.0- Riverpod-specific lintingcustom_lint: ^0.8.0- Required for riverpod_lint
2. Build Configuration (build.yaml)
✅ Configured to auto-generate code for:
**_provider.dartfiles**/providers/*.dartdirectories**/notifiers/*.dartdirectories
3. Linting (analysis_options.yaml)
✅ Enabled custom_lint with Riverpod rules:
provider_dependencies- Proper dependency trackingavoid_ref_read_inside_build- Performance optimizationavoid_public_notifier_properties- Encapsulationfunctional_ref- Proper ref usagenotifier_build- Correct Notifier implementation- And more...
4. App Initialization (main.dart)
✅ Wrapped with ProviderScope:
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}
5. Core Providers Created
connectivity_provider.dart - Network Monitoring
Four providers for connectivity management:
- connectivityProvider - Connectivity instance
- connectivityStreamProvider - Real-time connectivity stream
- currentConnectivityProvider - One-time connectivity check
- isOnlineProvider - Boolean online/offline stream
Usage Example:
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final connectivityState = ref.watch(connectivityStreamProvider);
return connectivityState.when(
data: (status) {
if (status == ConnectivityStatus.offline) {
return OfflineBanner();
}
return OnlineContent();
},
loading: () => CircularProgressIndicator(),
error: (error, _) => ErrorView(error),
);
}
}
provider_examples.dart - Comprehensive Examples
Complete examples of all Riverpod 3.0 patterns:
- ✅ Simple providers (immutable values)
- ✅ Async providers (FutureProvider pattern)
- ✅ Stream providers
- ✅ Notifier (mutable state with methods)
- ✅ AsyncNotifier (async mutable state)
- ✅ StreamNotifier (stream mutable state)
- ✅ Family (parameters as function arguments)
- ✅ Provider composition
- ✅ AutoDispose vs KeepAlive
- ✅ Lifecycle hooks
- ✅ Error handling with AsyncValue.guard()
- ✅ ref.mounted checks
- ✅ Invalidation and refresh
6. Documentation
✅ RIVERPOD_SETUP.md - Complete setup guide with:
- Installation instructions
- Code generation commands
- Usage patterns and examples
- Testing strategies
- Migration guide from Riverpod 2.x
- Common issues and solutions
✅ lib/core/providers/README.md - Provider architecture documentation
✅ scripts/setup_riverpod.sh - Automated setup script
Directory Structure
lib/core/providers/
├── connectivity_provider.dart # Network monitoring provider
├── connectivity_provider.g.dart # ✅ Generated code
├── provider_examples.dart # All Riverpod 3.0 patterns
├── provider_examples.g.dart # ✅ Generated code
└── README.md # Architecture docs
scripts/
└── setup_riverpod.sh # Automated setup script
Root files:
├── build.yaml # Build configuration
├── analysis_options.yaml # Linting configuration
├── RIVERPOD_SETUP.md # Complete guide
└── RIVERPOD_SUMMARY.md # This file
✅ Verification
All code generation completed successfully:
- ✅ connectivity_provider.g.dart generated
- ✅ provider_examples.g.dart generated
- ✅ No Riverpod-related errors in flutter analyze
- ✅ Dependencies installed
- ✅ ProviderScope configured
Quick Commands
Install Dependencies
flutter pub get
Generate Provider Code
# One-time generation
dart run build_runner build --delete-conflicting-outputs
# Watch mode (recommended during development)
dart run build_runner watch -d
# Clean and rebuild
dart run build_runner clean && dart run build_runner build --delete-conflicting-outputs
Run Linting
# Check for Riverpod issues
dart run custom_lint
# Auto-fix issues
dart run custom_lint --fix
Analyze Code
flutter analyze
Use Setup Script
./scripts/setup_riverpod.sh
Riverpod 3.0 Key Features
1. @riverpod Annotation
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'my_provider.g.dart';
@riverpod
String myValue(MyValueRef ref) => 'Hello';
2. Family as Function Parameters
@riverpod
Future<User> user(UserRef ref, String userId) async {
return await fetchUser(userId);
}
// Usage
ref.watch(userProvider('user123'));
3. Notifier for Mutable State
@riverpod
class Counter extends _$Counter {
@override
int build() => 0;
void increment() => state++;
}
// Usage
ref.watch(counterProvider); // Get state
ref.read(counterProvider.notifier).increment(); // Call method
4. AsyncNotifier for Async Mutable State
@riverpod
class UserProfile extends _$UserProfile {
@override
Future<User> build() async => await fetchUser();
Future<void> update(String name) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
return await updateUser(name);
});
}
}
5. Unified Ref Type
// All providers use the same Ref type
@riverpod
Future<String> example(ExampleRef ref) async {
ref.watch(provider1);
ref.read(provider2);
ref.listen(provider3, (prev, next) {});
}
6. ref.mounted Check
@riverpod
class Example extends _$Example {
@override
String build() => 'Initial';
Future<void> update() async {
await Future.delayed(Duration(seconds: 2));
// Check if still mounted
if (!ref.mounted) return;
state = 'Updated';
}
}
Usage in Widgets
ConsumerWidget
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final value = ref.watch(myProvider);
return Text(value);
}
}
ConsumerStatefulWidget
class MyWidget extends ConsumerStatefulWidget {
@override
ConsumerState<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends ConsumerState<MyWidget> {
@override
Widget build(BuildContext context) {
final value = ref.watch(myProvider);
return Text(value);
}
}
Consumer (for optimization)
Consumer(
builder: (context, ref, child) {
final count = ref.watch(counterProvider);
return Text('$count');
},
)
Best Practices
✅ DO
-
Use .select() for optimization
final name = ref.watch(userProvider.select((user) => user.name)); -
Use AsyncValue.guard() for error handling
state = await AsyncValue.guard(() async { return await api.call(); }); -
Check ref.mounted after async operations
await Future.delayed(Duration(seconds: 1)); if (!ref.mounted) return; state = newValue; -
Use autoDispose by default
@riverpod // autoDispose by default String example(ExampleRef ref) => 'value'; -
Keep providers in dedicated directories
lib/features/auth/presentation/providers/ lib/features/products/presentation/providers/
❌ DON'T
-
Don't use ref.read() in build methods
// BAD Widget build(BuildContext context, WidgetRef ref) { final value = ref.read(myProvider); // ❌ return Text(value); } // GOOD Widget build(BuildContext context, WidgetRef ref) { final value = ref.watch(myProvider); // ✅ return Text(value); } -
Don't use StateNotifierProvider (deprecated in Riverpod 3.0)
// Use Notifier instead @riverpod class Counter extends _$Counter { @override int build() => 0; void increment() => state++; } -
Don't forget the part directive
// Required! part 'my_provider.g.dart';
Next Steps
1. For Feature Development
Create providers in feature-specific directories:
lib/features/auth/presentation/providers/
├── auth_provider.dart
├── auth_provider.g.dart # Generated
├── login_form_provider.dart
└── login_form_provider.g.dart # Generated
2. Provider Template
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'my_provider.g.dart';
@riverpod
class MyFeature extends _$MyFeature {
@override
MyState build() {
// Initialize
return MyState.initial();
}
void updateState() {
// Modify state
state = state.copyWith(/* ... */);
}
}
3. Run Code Generation
After creating a provider:
dart run build_runner watch -d
4. Use in Widgets
class MyScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(myFeatureProvider);
return Column(
children: [
Text(state.value),
ElevatedButton(
onPressed: () {
ref.read(myFeatureProvider.notifier).updateState();
},
child: Text('Update'),
),
],
);
}
}
Testing
Unit Test Example
test('counter increments', () {
final container = ProviderContainer();
addTearDown(container.dispose);
expect(container.read(counterProvider), 0);
container.read(counterProvider.notifier).increment();
expect(container.read(counterProvider), 1);
});
Widget Test Example
testWidgets('displays user name', (tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
userProvider.overrideWith((ref) => User(name: 'Test')),
],
child: MaterialApp(home: UserScreen()),
),
);
expect(find.text('Test'), findsOneWidget);
});
Examples Reference
All Riverpod 3.0 patterns are documented with working examples in: 📄 lib/core/providers/provider_examples.dart
This file includes:
- ✅ 11 different provider patterns
- ✅ Real code examples (not pseudocode)
- ✅ Detailed comments explaining each pattern
- ✅ Usage examples in comments
- ✅ Migration notes from Riverpod 2.x
Connectivity Provider
The connectivity provider is a real-world example showing:
- ✅ Simple provider (Connectivity instance)
- ✅ Stream provider (connectivity changes)
- ✅ Future provider (one-time check)
- ✅ Derived provider (isOnline boolean)
- ✅ Proper documentation
- ✅ Usage examples
Use it as a template for creating your own providers!
Resources
- 📚 Riverpod Documentation
- 📚 Code Generation Guide
- 📚 Migration Guide
- 📄 Provider Examples
- 📄 Connectivity Provider
- 📄 Complete Setup Guide
Support & Help
If you encounter issues:
- Check examples in provider_examples.dart
- Review documentation in RIVERPOD_SETUP.md
- Run linting with
dart run custom_lint - Check generated files (*.g.dart) exist
- Verify part directive is present in provider files
- Ensure ProviderScope wraps the app in main.dart
Common Issues & Solutions
Issue: "Target of URI doesn't exist"
Solution: Run code generation:
dart run build_runner build --delete-conflicting-outputs
Issue: "Classes can only mix in mixins"
Solution: Make sure the part directive is correct:
part 'my_provider.g.dart'; // Must match filename
Issue: Provider not updating
Solution: Use ref.watch() in build, ref.read() in callbacks
Issue: Too many rebuilds
Solution: Use .select() to watch specific fields
Conclusion
✅ Riverpod 3.0 with code generation is fully configured and ready to use!
Key Benefits:
- ✅ Type-safe state management
- ✅ Less boilerplate with code generation
- ✅ Automatic provider type selection
- ✅ Better hot-reload support
- ✅ Comprehensive linting
- ✅ Excellent documentation
You can now:
- Create providers using @riverpod annotation
- Use connectivity monitoring immediately
- Reference provider_examples.dart for patterns
- Start building feature-specific providers
- Test providers with ProviderContainer
Happy coding! 🚀