Files
worker/RIVERPOD_SUMMARY.md
Phuoc Nguyen 628c81ce13 runable
2025-10-17 17:22:28 +07:00

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 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:

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:

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

  1. Use .select() for optimization

    final name = ref.watch(userProvider.select((user) => user.name));
    
  2. Use AsyncValue.guard() for error handling

    state = await AsyncValue.guard(() async {
      return await api.call();
    });
    
  3. Check ref.mounted after async operations

    await Future.delayed(Duration(seconds: 1));
    if (!ref.mounted) return;
    state = newValue;
    
  4. Use autoDispose by default

    @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

    // 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)

    // Use Notifier instead
    @riverpod
    class Counter extends _$Counter {
      @override
      int build() => 0;
      void increment() => state++;
    }
    
  3. 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

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:

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:

  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! 🚀