riverpod_sugar 1.0.0 copy "riverpod_sugar: ^1.0.0" to clipboard
riverpod_sugar: ^1.0.0 copied to clipboard

Essential Flutter widgets and utilities for flutter_riverpod. Includes RxWidget, AsyncValue helpers, form validation, and debouncing tools to reduce boilerplate code.

๐Ÿฏ Riverpod Sugar #

pub package style: very good analysis License: MIT

Sweet utilities for flutter_riverpod that reduce boilerplate and improve developer ergonomics without hiding the ref object.

โœจ Features #

  • ๐ŸŽฏ RxWidget & RxBuilder: Cleaner widget definitions with less boilerplate
  • โšก AsyncValue.easyWhen: Simplified async state handling with sensible defaults
  • ๐Ÿ”„ Debouncer: Intelligent input debouncing for search and user interactions
  • ๐Ÿ“ FormManager: Effortless form validation state management
  • ๐Ÿ”— Provider Combiners: Combine multiple providers elegantly
  • ๐Ÿ› ๏ธ Common Validators: Pre-built validation functions for forms

๐Ÿ“ฆ Installation #

Add riverpod_sugar to your pubspec.yaml:

dependencies:
  riverpod_sugar: ^1.0.0

Then import it in your Dart code:

import 'package:riverpod_sugar/riverpod_sugar.dart';

๐Ÿš€ Quick Start #

RxWidget - Cleaner ConsumerWidget #

Before (Standard Riverpod):

class CounterWidget extends ConsumerWidget {
  const CounterWidget({super.key});
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('Count: $count');
  }
}

After (With RxWidget):

class CounterWidget extends RxWidget {
  const CounterWidget({super.key});
  
  @override
  Widget buildRx(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('Count: $count');
  }
}

easyWhen - Simplified AsyncValue Handling #

Before (Standard Riverpod):

ref.watch(userProvider).when(
  data: (user) => Text('Hello ${user.name}!'),
  loading: () => const CircularProgressIndicator(),
  error: (error, stack) => Text('Error: $error'),
)

After (With easyWhen):

ref.watch(userProvider).easyWhen(
  data: (user) => Text('Hello ${user.name}!'),
  // loading & error widgets provided automatically!
)

Debouncer - Smart Search Input #

class SearchWidget extends RxStatefulWidget {
  @override
  State<SearchWidget> createState() => _SearchWidgetState();
}

class _SearchWidgetState extends RxState<SearchWidget> {
  final _debouncer = Debouncer(milliseconds: 300);
  
  @override
  Widget buildRx(BuildContext context, WidgetRef ref) {
    return TextField(
      onChanged: (query) {
        _debouncer.run(() {
          ref.read(searchProvider.notifier).updateQuery(query);
        });
      },
    );
  }
  
  @override
  void dispose() {
    _debouncer.dispose();
    super.dispose();
  }
}

๐Ÿ“š Detailed Examples #

Form Validation with FormManager #

final formManagerProvider = StateNotifierProvider<FormManager, FormState>((ref) {
  return FormManager();
});

class RegistrationForm extends RxWidget {
  @override
  Widget buildRx(BuildContext context, WidgetRef ref) {
    final formState = ref.watch(formManagerProvider);
    final formManager = ref.read(formManagerProvider.notifier);
    
    return Column(
      children: [
        TextFormField(
          decoration: InputDecoration(
            labelText: 'Email',
            errorText: formState.getError('email'),
          ),
          onChanged: (value) {
            formManager.validateField(
              'email',
              value,
              CommonValidators.combine([
                CommonValidators.required('Email is required'),
                CommonValidators.email('Please enter a valid email'),
              ]),
            );
          },
        ),
        ElevatedButton(
          onPressed: formState.isValid ? () => _submitForm() : null,
          child: Text('Submit'),
        ),
      ],
    );
  }
}

Provider Combination #

final userProvider = FutureProvider<User>((ref) => fetchUser());
final settingsProvider = FutureProvider<Settings>((ref) => fetchSettings());

// Combine multiple providers
final combinedProvider = ProviderCombiners.combine2(
  userProvider,
  settingsProvider,
);

// Use in widget
class UserDashboard extends RxWidget {
  @override
  Widget buildRx(BuildContext context, WidgetRef ref) {
    final combined = ref.watch(combinedProvider);
    
    return combined.easyWhen(
      data: ((user, settings)) => DashboardContent(user, settings),
    );
  }
}

Advanced AsyncValue Operations #

final userProvider = FutureProvider<User>((ref) => fetchUser());

// Transform async data
final userNameProvider = userProvider.mapData((user) => user.name);

// Check conditions on async data
final isAdminProvider = Provider<bool>((ref) {
  final user = ref.watch(userProvider);
  return user.hasDataWhere((user) => user.role == 'admin');
});

// Combine async providers
final dashboardDataProvider = AsyncProviderCombiners.combine3(
  userProvider,
  postsProvider,
  notificationsProvider,
);

๐ŸŽฏ Core Components #

RxWidget Family #

Widget Use Case
RxWidget Replaces ConsumerWidget with cleaner syntax
RxStatefulWidget Replaces ConsumerStatefulWidget
RxBuilder Inline reactive widgets without creating new classes
RxShow Conditional rendering based on provider state

AsyncValue Extensions #

Extension Purpose
easyWhen() Simplified async state handling with defaults
mapData() Transform data while preserving async state
hasDataWhere() Check conditions on async data
dataOrNull Get data or null safely

Form Management #

Component Purpose
FormManager Manages form validation state
CommonValidators Pre-built validation functions
FormState Immutable form state with error tracking

Utilities #

Utility Purpose
Debouncer Delay function execution (search, API calls)
AdvancedDebouncer Advanced debouncing with leading/trailing options
ProviderCombiners Combine multiple providers
AsyncProviderCombiners Combine async providers intelligently

๐Ÿ”ง Advanced Usage #

Custom Loading and Error Widgets #

userProvider.easyWhen(
  data: (user) => UserProfile(user),
  loading: () => CustomShimmer(),
  error: (error, stack) => CustomErrorWidget(error),
)

Advanced Debouncing #

final advancedDebouncer = AdvancedDebouncer(
  milliseconds: 300,
  maxWait: 2000,     // Force execution after 2 seconds
  leading: true,     // Execute immediately on first call
  trailing: true,    // Execute after delay
);

Multiple Field Validation #

formManager.validateFields({
  'email': (emailValue, CommonValidators.email()),
  'password': (passwordValue, CommonValidators.minLength(8)),
  'confirmPassword': (confirmValue, CommonValidators.matches(passwordValue)),
});

๐ŸŽจ Best Practices #

โœ… Do's #

  • Use RxWidget for widgets that only consume providers
  • Dispose Debouncer in StatefulWidgets
  • Combine related providers for better performance
  • Use CommonValidators for standard validation
  • Leverage easyWhen for most async operations

โŒ Don'ts #

  • Don't override both build and buildRx methods
  • Don't forget to dispose resources (Debouncer, controllers)
  • Don't use RxWidget for widgets that don't need providers
  • Don't ignore form validation errors

Performance Tips #

// โœ… Good: Combine related providers
final dashboardProvider = ProviderCombiners.combine2(userProvider, settingsProvider);

// โŒ Avoid: Multiple separate watches
// final user = ref.watch(userProvider);
// final settings = ref.watch(settingsProvider);

// โœ… Good: Use debouncer for expensive operations
final debouncer = Debouncer(milliseconds: 300);
onChanged: (query) => debouncer.run(() => search(query));

// โŒ Avoid: Direct expensive calls
// onChanged: (query) => search(query);

๐Ÿงช Testing #

Riverpod Sugar works seamlessly with Riverpod's testing utilities:

void main() {
  test('RxWidget updates when provider changes', () async {
    final container = ProviderContainer();
    
    // Test your RxWidget with the container
    await tester.pumpWidget(
      ProviderScope(
        parent: container,
        child: MyRxWidget(),
      ),
    );
    
    // Verify behavior
    expect(find.text('Initial Value'), findsOneWidget);
    
    // Update provider and test
    container.read(myProvider.notifier).state = 'Updated Value';
    await tester.pump();
    
    expect(find.text('Updated Value'), findsOneWidget);
  });
}

๐Ÿ”„ Migration Guide #

From ConsumerWidget to RxWidget #

  1. Change the parent class:

    // Before
    class MyWidget extends ConsumerWidget {
       
    // After  
    class MyWidget extends RxWidget {
    
  2. Rename the build method:

    // Before
    Widget build(BuildContext context, WidgetRef ref) {
       
    // After
    Widget buildRx(BuildContext context, WidgetRef ref) {
    
  3. That's it! Your existing code works unchanged.

From Manual AsyncValue Handling #

  1. Replace verbose when calls:
    // Before
    asyncValue.when(
      data: (data) => MyWidget(data),
      loading: () => CircularProgressIndicator(),
      error: (e, s) => Text('Error: $e'),
    )
       
    // After
    asyncValue.easyWhen(
      data: (data) => MyWidget(data),
    )
    

๐Ÿค Contributing #

We welcome contributions! Here's how you can help:

  1. ๐Ÿ› Report bugs - Open an issue with reproduction steps
  2. ๐Ÿ’ก Suggest features - We'd love to hear your ideas
  3. ๐Ÿ“ Improve docs - Help make the documentation even better
  4. ๐Ÿงช Add tests - More test coverage is always appreciated
  5. ๐Ÿ’ป Submit PRs - Bug fixes and features are welcome

Development Setup #

# Clone the repository
git clone https://github.com/mukhbit0/riverpod_sugar.git

# Install dependencies
flutter pub get

# Run tests
flutter test

# Run example app
cd example && flutter run

๐Ÿ“„ License #

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ™ Acknowledgments #

  • Riverpod - The amazing state management solution this package extends
  • Flutter Team - For the incredible framework
  • Community - For feedback and suggestions that make this package better

๐Ÿ“ˆ Package Stats #

  • โœ… Null Safety: Full null safety support
  • โœ… Flutter 3.10+: Compatible with latest Flutter versions
  • โœ… Dart 3.0+: Uses latest Dart features
  • โœ… Well Tested: Comprehensive test coverage
  • โœ… Well Documented: Extensive documentation and examples
  • โœ… Zero Dependencies: Only depends on flutter_riverpod

Made with โค๏ธ for the Flutter community

If you like this package, please give it a โญ on GitHub and a ๐Ÿ‘ on pub.dev!

6
likes
0
points
102
downloads

Publisher

verified publisherionicerrrrscode.com

Weekly Downloads

Essential Flutter widgets and utilities for flutter_riverpod. Includes RxWidget, AsyncValue helpers, form validation, and debouncing tools to reduce boilerplate code.

Repository (GitHub)
View/report issues

Topics

#flutter #riverpod #state-management #boilerplate #helpers

Documentation

Documentation

License

unknown (license)

Dependencies

flutter, flutter_riverpod

More

Packages that depend on riverpod_sugar