fairy 0.5.0+2 copy "fairy: ^0.5.0+2" to clipboard
fairy: ^0.5.0+2 copied to clipboard

A lightweight MVVM framework for Flutter with strongly-typed, reactive data binding state management library, Mainly focused on simplicity and ease of use.

example/lib/main.dart

import 'package:fairy/fairy.dart';
import 'package:flutter/material.dart';

// ViewModel for Counter example demonstrating MVVM pattern with Fairy
class CounterViewModel extends ObservableObject {
  // Reactive property for counter value (auto-disposed with parent)
  late final ObservableProperty<int> counter;
  
  // Commands with canExecute logic (auto-disposed with parent)
  late final RelayCommand incrementCommand;
  late final RelayCommand decrementCommand;
  late final RelayCommand resetCommand;
  
  // Store disposer for cleanup
  VoidCallback? _counterDisposer;
  
  CounterViewModel() {
    counter = ObservableProperty<int>(0, parent: this);
    
    incrementCommand = relayCommand(
      _increment,
    );
    
    // Decrement only enabled when counter > 0
    decrementCommand = relayCommand(
      _decrement,
      canExecute: () => counter.value > 0,
    );
    
    resetCommand = relayCommand(
       _reset,
    );
    
    // When counter changes, refresh commands that depend on its value
    _counterDisposer = propertyChanged(() {
      decrementCommand.notifyCanExecuteChanged();
    });
  }
  
  void _increment() {
    counter.value++;
  }
  
  void _decrement() {
    if (counter.value > 0) {
      counter.value--;
    }
  }
  
  void _reset() {
    counter.value = 0;
  }
  
  @override
  void dispose() {
    _counterDisposer?.call();
    super.dispose(); // Auto-disposes counter and all commands
  }
}

void main() {
  runApp(const CounterApp());
}

class CounterApp extends StatelessWidget {
  const CounterApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fairy Counter Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: FairyScope(
        // Create scoped ViewModel - automatically disposed when widget is removed
        viewModel: (_) => CounterViewModel(),
        child: const CounterPage(),
      ),
    );
  }
}

class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Fairy Counter Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'You have pushed the button this many times:',
              style: TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 20),
            
            // Bind widget: Two-way data binding with counter property
            // Updates automatically when counter changes
            Bind<CounterViewModel, int>(
              selector: (vm) => vm.counter,
              builder: (context, value, update) {
                return Text(
                  '$value',
                  style: Theme.of(context).textTheme.headlineLarge,
                );
              },
            ),
            
            const SizedBox(height: 40),
            
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                // Command widget: Decrement button (disabled when counter = 0)
                Command<CounterViewModel>(
                  command: (vm) => vm.decrementCommand,
                  builder: (context, execute, canExecute) {
                    return ElevatedButton.icon(
                      onPressed: canExecute ? execute : null,
                      icon: const Icon(Icons.remove),
                      label: const Text('Decrement'),
                    );
                  },
                ),
                
                const SizedBox(width: 20),
                
                // Command widget: Increment button
                Command<CounterViewModel>(
                  command: (vm) => vm.incrementCommand,
                  builder: (context, execute, canExecute) {
                    return ElevatedButton.icon(
                      onPressed: canExecute ? execute : null,
                      icon: const Icon(Icons.add),
                      label: const Text('Increment'),
                    );
                  },
                ),
              ],
            ),
            
            const SizedBox(height: 20),
            
            // Command widget: Reset button
            Command<CounterViewModel>(
              command: (vm) => vm.resetCommand,
              builder: (context, execute, canExecute) {
                return ElevatedButton.icon(
                  onPressed: canExecute ? execute : null,
                  icon: const Icon(Icons.refresh),
                  label: const Text('Reset'),
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.orange,
                    foregroundColor: Colors.white,
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}
7
likes
150
points
30
downloads

Publisher

verified publishercircuids.com

Weekly Downloads

A lightweight MVVM framework for Flutter with strongly-typed, reactive data binding state management library, Mainly focused on simplicity and ease of use.

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on fairy