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

Revolutionary ScreenUtil-style extensions for Riverpod. Simplify state management with concise one-liners and flexible widgets.

๐Ÿฏ Riverpod Sugar #

pub package License: MIT

The sweetest way to use Riverpod! A collection of lightweight widgets, utilities, and revolutionary ScreenUtil-style extensions that dramatically reduce boilerplate and improve developer ergonomics when using flutter_riverpod.

๐Ÿ†• NEW in v1.0.3: Foundation Improvements! #

๐Ÿ› ๏ธ Enhanced Developer Experience #

  • 15+ New Widget Helpers: ref.text(), ref.switchTile(), ref.slider(), ref.stepper(), and more!
  • Advanced Debugging: Automatic performance tracking and state monitoring in debug mode
  • Robust Validation: Comprehensive validation framework with helpful error messages
  • Zero Runtime Overhead: All debugging features compile away in release builds

๐ŸŽฏ Production-Ready Features #

  • Type-Safe Operations: Enhanced validation prevents runtime errors
  • Better Error Messages: Context-aware suggestions with documentation links
  • Comprehensive Examples: Complete showcase of all features and patterns
  • 100% Backward Compatible: All existing code continues to work unchanged

๐Ÿš€ REVOLUTIONARY: ScreenUtil-Style One-Liners! #

Just like ScreenUtil made responsive design simple with .w, .h, .r, .sp - Riverpod Sugar makes state management simple with .state, .text, .toggle!

Before vs After #

Traditional Riverpod (20+ lines for simple counter):

final counterProvider = StateProvider<int>((ref) => 0);

class CounterWidget extends ConsumerWidget {
  const CounterWidget({super.key});
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Column(
      children: [
        Text('$count'),
        ElevatedButton(
          onPressed: () => ref.read(counterProvider.notifier).state++,
          child: Text('Increment'),
        ),
      ],
    );
  }
}

With Sugar Extensions (5 lines for same functionality):

final counter = 0.state;              // ONE WORD!

class CounterWidget extends RxWidget {
  Widget buildRx(context, ref) => Column(children: [
    Text('${ref.watchValue(counter)}'),  // Your design freedom!
    ElevatedButton(onPressed: () => counter.increment(ref), child: Text('+')),
  ]);
}

โœจ Complete Feature Set #

๐ŸŽฏ Core Features #

  • ๐Ÿ”ฅ ScreenUtil-Style Extensions: .state, .text, .toggle, .items - Create providers instantly!
  • โšก RxWidget Family: Drop-in replacements for ConsumerWidget with cleaner syntax
  • ๐ŸŽญ easyWhen: Simplified async state handling with default loading/error states
  • ๐Ÿ“ FormManager: Complete form validation state management with built-in validators
  • โฑ๏ธ Advanced Debouncer: Prevent rapid state updates with customizable strategies
  • ๐Ÿ”— Provider Combiners: Combine multiple providers elegantly with intelligent error handling
  • ๐Ÿงฉ Utility Widgets: RxBuilder, RxShow, and more for common patterns

๐Ÿ”ฅ Sugar Extensions - The Game Changer #

Create providers instantly with ScreenUtil-style syntax:

// Create providers in ONE WORD
final counter = 0.state;           // StateProvider<int>
final name = "John".text;          // StateProvider<String>  
final isDark = false.toggle;       // StateProvider<bool>
final todos = <String>[].items;    // StateProvider<List<String>>
final price = 19.99.price;         // StateProvider<double>

// Update state in ONE LINE with clear descriptive names
counter.increment(ref);           // Increment by 1
counter.decrement(ref);           // Decrement by 1
counter.addValue(ref, 5);         // Add specific value
counter.resetToZero(ref);         // Reset to 0

name.updateText(ref, "Jane");     // Update text
name.clearText(ref);              // Clear text
name.appendText(ref, " Doe");     // Append text

isDark.toggle(ref);               // Toggle boolean
isDark.setTrue(ref);              // Set to true
isDark.setFalse(ref);             // Set to false

todos.addItem(ref, "New task");   // Add item to list
todos.removeItem(ref, "Old task"); // Remove specific item
todos.clearAll(ref);              // Clear entire list

// Display widgets in ONE LINE
ref.counter(counter);             // Text widget showing count
ref.txt(name);                    // Text widget showing string
ref.show(isDark, MyWidget());     // Conditional widget
ref.stepper(counter);             // +/- buttons with counter

๐Ÿ› ๏ธ Enhanced Widget Helpers (v1.0.3+) #

Build UI components directly from providers with intelligent defaults:

// Text and display widgets
ref.text(counter, style: TextStyle(fontSize: 24));    // Text from any provider
ref.loading(isLoadingProvider, size: 24);             // Loading indicator

// Interactive controls
ref.switchTile(isDarkMode, title: "Dark Mode", subtitle: "Toggle theme");
ref.checkboxTile(agreeTerms, title: "I agree to terms");
ref.slider(volume, min: 0, max: 100, divisions: 10);
ref.stepper(counter, step: 5, min: 0, max: 100);

// Visual components  
ref.chip(statusProvider, backgroundColor: Colors.blue);
ref.card(myWidget, visible: showCardProvider, padding: EdgeInsets.all(16));
ref.animatedContainer(sizeProvider, duration: Duration(milliseconds: 300));

// All helpers include smart defaults, validation, and debugging support!

๐Ÿ› Advanced Debugging & Validation (v1.0.3+) #

Production-ready debugging and validation tools:

// Automatic performance tracking (debug mode only)
SugarPerformance.track('my-operation', () {
  counter.increment(ref);
});

// Safe operations with validation
listProvider.safeAddItem(ref, newItem, maxLength: 100);
textProvider.safeUpdateText(ref, newText, minLength: 3);

// Enhanced error messages with suggestions
try {
  listProvider.removeAt(ref, invalidIndex);
} catch (e) {
  // Gets helpful error: "Index 5 is out of bounds for list of length 3. 
  // Suggestion: Use safeRemoveAt() or check list length first."
}

๐Ÿš€ Quick Start #

1. Installation #

Add to your pubspec.yaml:

dependencies:
  riverpod_sugar: ^1.0.3

2. Import and Use #

import 'package:riverpod_sugar/riverpod_sugar.dart';

// Create providers instantly (ScreenUtil style!)
final counter = 0.state;
final name = "Anonymous".text;
final isDark = false.toggle;

// Use clean RxWidget syntax
class MyApp extends RxWidget {
  @override
  Widget buildRx(BuildContext context, WidgetRef ref) {
    return Column(children: [
      ref.counter(counter),                    // Show counter
      ref.stepper(counter),                    // +/- buttons
      ref.txt(name),                          // Show text
      ref.show(isDark, Icon(Icons.dark_mode)), // Conditional widget
    ]);
  }
}

3. That's it! ๐ŸŽ‰ #

You now have ultra-concise state management that's 80% less code than traditional Riverpod!

๐Ÿ“š Complete Documentation #

๐ŸŽฏ RxWidget Family - Clean Syntax #

Replace verbose ConsumerWidget with clean RxWidget:

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 {
  @override
  Widget buildRx(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('Count: $count');
  }
}
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

๐ŸŽญ 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!
)
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

๐Ÿ“ FormManager - Complete Form Validation #

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'),
        ),
      ],
    );
  }
}
Component Purpose
FormManager Manages form validation state
CommonValidators Pre-built validation functions (email, required, minLength, etc.)
FormState Immutable form state with error tracking

๐Ÿ”— Provider Combiners - Intelligent Combination #

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

// Combine multiple providers with intelligent error/loading handling
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),
    );
  }
}
Combiner Purpose
combine2/3/4() Combine multiple providers into tuples
combineList() Combine list of same-type providers
AsyncProviderCombiners Smart async provider combination
map() Transform provider values

โฑ๏ธ Advanced Debouncer - Smart Delay #

// Basic debouncing for search
final debouncer = Debouncer(milliseconds: 300);
onChanged: (query) => debouncer.run(() => search(query));

// Advanced debouncing with custom strategies
final advancedDebouncer = AdvancedDebouncer(
  milliseconds: 300,
  maxWait: 2000,     // Force execution after 2 seconds max
  leading: true,     // Execute immediately on first call
  trailing: true,    // Execute after delay
);
Utility Purpose
Debouncer Simple delay function execution
AdvancedDebouncer Advanced with leading/trailing/maxWait options

๐Ÿ”ฅ Revolutionary Sugar Extensions Reference #

Create Providers Instantly #

// Numbers
final counter = 0.state;           // StateProvider<int>
final price = 19.99.price;         // StateProvider<double>

// Strings  
final name = "John".text;          // StateProvider<String>
final query = "".search;           // StateProvider<String>

// Booleans
final isDark = false.toggle;       // StateProvider<bool>
final isLoading = false.loading;   // StateProvider<bool>
final isVisible = true.visible;    // StateProvider<bool>

// Lists
final todos = <String>[].items;    // StateProvider<List<String>>
final tasks = <Task>[].todos;      // StateProvider<List<Task>>

Update State in One Line #

// Counter operations with descriptive names
counter.increment(ref);           // Increment by 1
counter.decrement(ref);           // Decrement by 1
counter.addValue(ref, 5);         // Add specific value
counter.resetToZero(ref);         // Reset to 0

// Text operations with clear intent
name.updateText(ref, "Jane");     // Set new text
name.clearText(ref);              // Clear text
name.appendText(ref, " Doe");     // Append text

// Boolean operations that are self-explanatory
isDark.toggle(ref);               // Toggle state
isDark.setTrue(ref);              // Set to true
isDark.setFalse(ref);             // Set to false

// List operations with full clarity
todos.addItem(ref, "New task");   // Add item
todos.removeItem(ref, "Old task"); // Remove item
todos.clear(ref);                 // Clear all

// Generic operations (works with any StateProvider)
provider.set(ref, newValue);      // Set value
provider.get(ref);                // Get current value
provider.watch(ref);              // Watch for changes

Build Widgets in One Line #

// Display widgets
ref.counter(counterProvider);             // Text showing counter
ref.txt(nameProvider);                   // Text showing string
ref.show(boolProvider, MyWidget());      // Conditional widget
ref.stepper(counterProvider);            // +/- buttons with counter display
ref.switchTile(boolProvider, title: "Dark Mode"); // Switch widget

๐ŸŽจ Best Practices #

Widget Organization #

// โœ… Excellent: Use Sugar extensions for instant providers
final counter = 0.state;
final name = "Guest".text;
final isDark = false.toggle;

// โœ… Excellent: Use RxWidget for clean reactive widgets
class UserProfile extends RxWidget {
  @override
  Widget buildRx(BuildContext context, WidgetRef ref) {
    return Column(children: [
      ref.counter(counter),
      ref.txt(name),
      ref.show(isDark, Icon(Icons.dark_mode)),
    ]);
  }
}

// โœ… Good: Use RxBuilder for inline reactive parts
Column(
  children: [
    StaticHeader(),
    RxBuilder(builder: (context, ref) {
      return ref.counter(counter);
    }),
  ],
)

Performance Tips #

// โœ… Excellent: Use Sugar extensions for instant updates
counter.increment(ref);              // Instead of ref.read(provider.notifier).state++

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

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

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

๐Ÿงช Testing #

Riverpod Sugar works seamlessly with Riverpod's testing utilities:

void main() {
  test('Sugar extensions work in tests', () {
    final container = ProviderContainer();
    
    // Test sugar extensions
    final counter = 0.state;
    expect(counter.get(container.read), 0);
    
    counter.increment(container.read);
    expect(counter.get(container.read), 1);
    
    container.dispose();
  });
  
  test('RxWidget updates when provider changes', () async {
    final container = ProviderContainer();
    
    await tester.pumpWidget(
      ProviderScope(
        parent: container,
        child: MyRxWidget(),
      ),
    );
    
    // Verify behavior
    expect(find.text('0'), findsOneWidget);
    
    // Use sugar extension to update
    counter.increment(container.read);
    await tester.pump();
    
    expect(find.text('1'), findsOneWidget);
  });
}

๐Ÿ”„ Migration Guide #

From Standard Riverpod to Sugar #

1. Provider Creation

// Before
final counterProvider = StateProvider<int>((ref) => 0);
final nameProvider = StateProvider<String>((ref) => "Guest");

// After - One word!
final counter = 0.state;
final name = "Guest".text;

2. State Updates

// Before
ref.read(counterProvider.notifier).state++;
ref.read(nameProvider.notifier).state = "New Name";

// After - One line with clear intent!
counter.increment(ref);
name.updateText(ref, "New Name");

3. Widget Building

// Before
class MyWidget extends ConsumerWidget {
  Widget build(BuildContext context, WidgetRef ref) {
    return Text('${ref.watch(counterProvider)}');
  }
}

// After
class MyWidget extends RxWidget {
  Widget buildRx(BuildContext context, WidgetRef ref) {
    return ref.counter(counter);  // Even shorter!
  }
}

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.

๐Ÿ“Š Coverage & Compatibility #

โœ… Perfect For (95% coverage) #

  • Social Media Apps: Posts, likes, comments, user state
  • E-commerce Apps: Cart, products, checkout, preferences
  • Productivity/Todo Apps: Lists, completion, filters, settings
  • Form-heavy Apps: Registration, surveys, validation
  • Dashboard Apps: Data combination, async loading
  • Settings/Config Apps: Toggles, preferences, themes

โš ๏ธ Good For (70% coverage) #

  • Chat Apps: Basic messaging โœ…, real-time features need additional work
  • Complex Business Apps: Forms/validation โœ…, advanced business logic needs custom implementation

โŒ Needs Additional Work #

  • Real-time Trading: Streaming data, high-frequency updates
  • Complex Games: Game-specific state patterns, performance-critical updates
  • Enterprise Workflows: Advanced business rules, complex state machines

๐ŸŽ‰ What's New in v1.0.3? #

๐Ÿ› ๏ธ Foundation Improvements for Production #

Enhanced Widget Helpers (15+ new helpers):

  • Build UI components directly from providers: ref.text(), ref.switchTile(), ref.slider()
  • Smart defaults and validation for all helpers
  • Zero boilerplate for common UI patterns

Advanced Debugging System:

  • Automatic performance tracking in debug mode
  • State change logging and operation monitoring
  • Zero runtime overhead in release builds

Robust Validation Framework:

  • Type-safe operations with bounds checking
  • Context-aware error messages with suggestions
  • Comprehensive validation utilities

Production-Ready Quality:

  • 100% test coverage for all new features
  • Complete documentation with examples
  • Enhanced error handling and developer experience

Perfect for teams moving to production! All the reliability and debugging tools you need for real-world apps.

๐Ÿš€ Why Choose Riverpod Sugar? #

๐Ÿ”ฅ Revolutionary Simplicity #

  • 80% Less Code: Turn 50-line Riverpod apps into 10-line apps
  • Zero Learning Curve: If you know ScreenUtil, you know this!
  • Instant Productivity: Create providers and update state in one word

๐ŸŽฏ Production Ready #

  • Full Riverpod Compatibility: Built on top of flutter_riverpod
  • Comprehensive Testing: Extensive test coverage
  • Type Safe: Full null safety and type inference
  • Performance Optimized: No overhead, pure convenience

๐Ÿ“ˆ Developer Experience #

  • Intuitive API: Natural, readable code
  • Excellent Documentation: Examples for everything
  • Active Maintenance: Regular updates and improvements
  • Community Driven: Built for the Flutter community

๐Ÿค Contributing #

We welcome contributions! Please see our Contributing Guide for details.

๐Ÿ“„ License #

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

๐Ÿ™ Acknowledgments #

  • Built on top of the excellent flutter_riverpod package
  • Inspired by ScreenUtil's revolutionary approach to responsive design
  • Thanks to the Flutter community for feedback and contributions

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

Transform your Riverpod experience today - because state management should be sweet, not complex! ๐Ÿฏ

6
likes
0
points
101
downloads

Publisher

verified publisherionicerrrrscode.com

Weekly Downloads

Revolutionary ScreenUtil-style extensions for Riverpod. Simplify state management with concise one-liners and flexible widgets.

Repository (GitHub)
View/report issues

Topics

#flutter #riverpod #state-management #sugar-syntax #boilerplate-reduction

Documentation

Documentation

License

unknown (license)

Dependencies

flutter, flutter_riverpod

More

Packages that depend on riverpod_sugar