runable
This commit is contained in:
551
RIVERPOD_SUMMARY.md
Normal file
551
RIVERPOD_SUMMARY.md
Normal file
@@ -0,0 +1,551 @@
|
||||
# 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! 🚀**
|
||||
Reference in New Issue
Block a user