552 lines
13 KiB
Markdown
552 lines
13 KiB
Markdown
# 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 Flutter
|
|
- `riverpod_annotation: ^3.0.0` - Annotations for code generation
|
|
|
|
**Development:**
|
|
- `build_runner: ^2.4.11` - Code generation engine
|
|
- `riverpod_generator: ^3.0.0` - Generates provider code
|
|
- `riverpod_lint: ^3.0.0` - Riverpod-specific linting
|
|
- `custom_lint: ^0.8.0` - Required for riverpod_lint
|
|
|
|
### 2. Build Configuration (build.yaml)
|
|
|
|
✅ Configured to auto-generate code for:
|
|
- `**_provider.dart` files
|
|
- `**/providers/*.dart` directories
|
|
- `**/notifiers/*.dart` directories
|
|
|
|
### 3. Linting (analysis_options.yaml)
|
|
|
|
✅ Enabled custom_lint with Riverpod rules:
|
|
- `provider_dependencies` - Proper dependency tracking
|
|
- `avoid_ref_read_inside_build` - Performance optimization
|
|
- `avoid_public_notifier_properties` - Encapsulation
|
|
- `functional_ref` - Proper ref usage
|
|
- `notifier_build` - Correct Notifier implementation
|
|
- And more...
|
|
|
|
### 4. App Initialization (main.dart)
|
|
|
|
✅ Wrapped with ProviderScope:
|
|
```dart
|
|
void main() {
|
|
runApp(
|
|
const ProviderScope(
|
|
child: MyApp(),
|
|
),
|
|
);
|
|
}
|
|
```
|
|
|
|
### 5. Core Providers Created
|
|
|
|
#### **connectivity_provider.dart** - Network Monitoring
|
|
|
|
Four providers for connectivity management:
|
|
|
|
1. **connectivityProvider** - Connectivity instance
|
|
2. **connectivityStreamProvider** - Real-time connectivity stream
|
|
3. **currentConnectivityProvider** - One-time connectivity check
|
|
4. **isOnlineProvider** - Boolean online/offline stream
|
|
|
|
**Usage Example:**
|
|
```dart
|
|
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
|
|
```bash
|
|
flutter pub get
|
|
```
|
|
|
|
### Generate Provider Code
|
|
```bash
|
|
# 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
|
|
```bash
|
|
# Check for Riverpod issues
|
|
dart run custom_lint
|
|
|
|
# Auto-fix issues
|
|
dart run custom_lint --fix
|
|
```
|
|
|
|
### Analyze Code
|
|
```bash
|
|
flutter analyze
|
|
```
|
|
|
|
### Use Setup Script
|
|
```bash
|
|
./scripts/setup_riverpod.sh
|
|
```
|
|
|
|
## Riverpod 3.0 Key Features
|
|
|
|
### 1. @riverpod Annotation
|
|
```dart
|
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
|
|
part 'my_provider.g.dart';
|
|
|
|
@riverpod
|
|
String myValue(MyValueRef ref) => 'Hello';
|
|
```
|
|
|
|
### 2. Family as Function Parameters
|
|
```dart
|
|
@riverpod
|
|
Future<User> user(UserRef ref, String userId) async {
|
|
return await fetchUser(userId);
|
|
}
|
|
|
|
// Usage
|
|
ref.watch(userProvider('user123'));
|
|
```
|
|
|
|
### 3. Notifier for Mutable State
|
|
```dart
|
|
@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
|
|
```dart
|
|
@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
|
|
```dart
|
|
// 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
|
|
```dart
|
|
@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
|
|
```dart
|
|
class MyWidget extends ConsumerWidget {
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final value = ref.watch(myProvider);
|
|
return Text(value);
|
|
}
|
|
}
|
|
```
|
|
|
|
### ConsumerStatefulWidget
|
|
```dart
|
|
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)
|
|
```dart
|
|
Consumer(
|
|
builder: (context, ref, child) {
|
|
final count = ref.watch(counterProvider);
|
|
return Text('$count');
|
|
},
|
|
)
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### ✅ DO
|
|
|
|
1. **Use .select() for optimization**
|
|
```dart
|
|
final name = ref.watch(userProvider.select((user) => user.name));
|
|
```
|
|
|
|
2. **Use AsyncValue.guard() for error handling**
|
|
```dart
|
|
state = await AsyncValue.guard(() async {
|
|
return await api.call();
|
|
});
|
|
```
|
|
|
|
3. **Check ref.mounted after async operations**
|
|
```dart
|
|
await Future.delayed(Duration(seconds: 1));
|
|
if (!ref.mounted) return;
|
|
state = newValue;
|
|
```
|
|
|
|
4. **Use autoDispose by default**
|
|
```dart
|
|
@riverpod // autoDispose by default
|
|
String example(ExampleRef ref) => 'value';
|
|
```
|
|
|
|
5. **Keep providers in dedicated directories**
|
|
```
|
|
lib/features/auth/presentation/providers/
|
|
lib/features/products/presentation/providers/
|
|
```
|
|
|
|
### ❌ DON'T
|
|
|
|
1. **Don't use ref.read() in build methods**
|
|
```dart
|
|
// 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);
|
|
}
|
|
```
|
|
|
|
2. **Don't use StateNotifierProvider** (deprecated in Riverpod 3.0)
|
|
```dart
|
|
// Use Notifier instead
|
|
@riverpod
|
|
class Counter extends _$Counter {
|
|
@override
|
|
int build() => 0;
|
|
void increment() => state++;
|
|
}
|
|
```
|
|
|
|
3. **Don't forget the part directive**
|
|
```dart
|
|
// 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
|
|
|
|
```dart
|
|
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:
|
|
```bash
|
|
dart run build_runner watch -d
|
|
```
|
|
|
|
### 4. Use in Widgets
|
|
|
|
```dart
|
|
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
|
|
```dart
|
|
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
|
|
```dart
|
|
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](https://riverpod.dev)
|
|
- 📚 [Code Generation Guide](https://riverpod.dev/docs/concepts/about_code_generation)
|
|
- 📚 [Migration Guide](https://riverpod.dev/docs/migration/from_state_notifier)
|
|
- 📄 [Provider Examples](./lib/core/providers/provider_examples.dart)
|
|
- 📄 [Connectivity Provider](./lib/core/providers/connectivity_provider.dart)
|
|
- 📄 [Complete Setup Guide](./RIVERPOD_SETUP.md)
|
|
|
|
## Support & Help
|
|
|
|
If you encounter issues:
|
|
|
|
1. **Check examples** in provider_examples.dart
|
|
2. **Review documentation** in RIVERPOD_SETUP.md
|
|
3. **Run linting** with `dart run custom_lint`
|
|
4. **Check generated files** (*.g.dart) exist
|
|
5. **Verify part directive** is present in provider files
|
|
6. **Ensure ProviderScope** wraps the app in main.dart
|
|
|
|
## Common Issues & Solutions
|
|
|
|
### Issue: "Target of URI doesn't exist"
|
|
**Solution:** Run code generation:
|
|
```bash
|
|
dart run build_runner build --delete-conflicting-outputs
|
|
```
|
|
|
|
### Issue: "Classes can only mix in mixins"
|
|
**Solution:** Make sure the part directive is correct:
|
|
```dart
|
|
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:**
|
|
1. Create providers using @riverpod annotation
|
|
2. Use connectivity monitoring immediately
|
|
3. Reference provider_examples.dart for patterns
|
|
4. Start building feature-specific providers
|
|
5. Test providers with ProviderContainer
|
|
|
|
**Happy coding! 🚀**
|