Watchable

The simplest Flutter state management. Ever.

final counter = 0.watch;        // Create
counter.value++;                // Update  
counter.build((count) => Text('$count'))  // UI

That's it. You just learned the entire library.

30-Second Start

dependencies:
  watchable: ^4.1.2
import 'package:watchable/watchable.dart';

class CounterApp extends StatelessWidget {
  final counter = 0.watch;
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: counter.build((count) => Text('$count', style: TextStyle(fontSize: 48))),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => counter.value++,
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

Done. Your app now has reactive state with automatic UI updates.


Why Developers Love It

Old Way (Provider/Bloc/GetX) Watchable Way
notifyListeners() counter.value++
Consumer<T> widgets counter.build()
Multiple concepts to learn 2 concepts total
Boilerplate code Zero boilerplate

Real Examples

Form Validation

final email = ''.watch;
final password = ''.watch;

// Multi-field validation in one line
(email, password).build((e, p) => ElevatedButton(
  onPressed: e.contains('@') && p.length >= 6 ? submit : null,
  child: Text('Login'),
))

Shopping Cart

final price = 10.0.watch;
final quantity = 1.watch;

// Auto-calculated total
(price, quantity).build((p, q) => Text('Total: \$${p * q}'))

// Update anywhere
FloatingActionButton(
  onPressed: () => quantity.value++,
  child: Icon(Icons.add),
)

Todo List

final todos = <String>[].watch;

// Dynamic list
todos.build((list) => ListView(
  children: list.map((todo) => ListTile(title: Text(todo))).toList(),
))

// Add todo
todos.value = [...todos.value, 'New todo'];

All UI Patterns

// Text & Basic Widgets
message.build((msg) => Text(msg))

// Conditional Rendering  
isLoading.build((loading) => loading ? Spinner() : Content())

// Forms
(email, password).build((e, p) => LoginButton(valid: isValid(e, p)))

// Lists
items.build((list) => ListView.builder(...))

// Complex State
(user, settings, theme).build((u, s, t) => ProfilePage(...))

Advanced Features

Smart Rebuilds (Performance)

// Only rebuild when value changes significantly
temperature.build(
  (temp) => ThermometerWidget(temp),
  shouldRebuild: (prev, curr) => (prev - curr).abs() > 1.0,
)

Derived State

final firstName = 'John'.watch;
final lastName = 'Doe'.watch;

// Create computed state
final fullName = (firstName, lastName).combine((f, l) => '$f $l');

// Use everywhere
fullName.build((name) => Text(name))
appBar.build((name) => AppBar(title: Text(name)))

Architecture Example

class TodoApp {
  // State
  final todos = <Todo>[].watch;
  final filter = 'all'.watch;
  
  // Computed
  late final visibleTodos = (todos, filter).combine((list, f) => 
    f == 'active' ? list.where((t) => !t.done).toList() : list
  );
  
  // UI
  Widget build() => Column(children: [
    // Filter buttons
    filter.build((f) => FilterButtons(current: f)),
    
    // Todo list
    visibleTodos.build((todos) => TodoList(todos)),
    
    // Add button  
    FloatingActionButton(onPressed: addTodo),
  ]);
  
  void addTodo() => todos.value = [...todos.value, Todo('New')];
}

Benchmarks

Feature Watchable GetX Provider Riverpod
Learning Time 30 seconds 2 hours 1 day 2 days
Lines of Code 70% less Good Verbose Complex
Performance Excellent Good OK Excellent
Type Safety Yes No Yes Yes

Migration

From GetX

// GetX
final counter = 0.obs;
counter.value++;
Obx(() => Text('${counter.value}'))

// Watchable (same syntax, better!)
final counter = 0.watch;
counter.value++;
counter.build((count) => Text('$count'))

From Provider

// Provider (verbose)
ChangeNotifierProvider(
  create: (_) => CounterNotifier(),
  child: Consumer<CounterNotifier>(
    builder: (context, counter, child) => Text('${counter.value}'),
  ),
)

// Watchable (simple)
final counter = 0.watch;
counter.build((count) => Text('$count'))

Why Switch?

  • Learn once, use everywhere - Same pattern for all state
  • Zero boilerplate - No providers, consumers, or notifiers
  • Automatic optimization - Built-in performance features
  • Type safe - Compile-time error catching
  • Tiny size - Single import, lightweight

Complete Guide

Need more examples? Check our comprehensive guide with:

  • Complex forms with validation
  • Shopping cart with calculations
  • Real-time chat interfaces
  • Performance optimization tips

Pro Tips

// Events (no special event type needed!)
final notification = ''.watch;
notification.value = 'Hello!';  // Triggers UI update

// Multiple state updates
final user = User().watch;
user.value = user.value.copyWith(name: 'New Name');

// Bulk operations
final items = <Item>[].watch;
items.value = [...items.value, newItem];  // Add
items.value = items.value.where((i) => i.id != id).toList();  // Remove

That's it! You now know everything about Watchable.

🚀 Start building - it really is this simple.


Made with ❤️ for Flutter developers who value simplicity

Libraries

watchable