DEV Community

Pravin Kunnure
Pravin Kunnure

Posted on

LIVO Next-Level Flutter State Management

Flutter is great, but managing state can quickly become messy. You’ve probably used setState for small projects or Provider, Riverpod, or BLoC for bigger apps—but each comes with trade-offs.

⚠️**** Note: This story was originally about reactive_orm, which is now deprecated. Its evolution is now called LIVO. LIVO continues its reactive object-relationship state management approach with improved naming, documentation, and long-term support.

. What is LIVO?

Enter LIVO: a lightweight, reactive ORM-style state management library for Flutter.

It lets your UI react automatically when model properties change — no streams, ChangeNotifier, or boilerplate required.

. With LIVO, you get:

  • Object-wise and field-wise reactivity
  • Nested and shared models
  • Many → One and Many ↔ Many relationships
  • Minimal boilerplate, plain Dart models

. LIVO in action:

  • Object-wise: Update any field and rebuild the entire widget
  • Field-wise: Only rebuild widgets for selected fields
  • Many → One: Multiple models feeding a single observer
  • Many ↔ Many: Shared models reflected across multiple parents

. Getting Started:
Add LIVO to your project:
dependencies:
livo:

1️⃣ Creating a Reactive Model

_import 'package:livo/livo.dart';

class Task extends ReactiveModel {
String _title;
bool _completed = false;
String _status = "Idle";

Task({required String title}) : _title = title;

String get title => _title;
set title(String value) {
if (_title != value) {
_title = value;
notifyListeners(#title); // ✅ Symbol-based
}
}

bool get completed => _completed;
set completed(bool value) {
if (_completed != value) {
_completed = value;
notifyListeners(#completed);
}
}

String get status => status;
set status(String value) {
if (_status != value) {
_status = value;
notifyListeners(#status);
}
}
}

✅ This is just plain Dart. LIVO will handle notifying widgets when fields change.

2️⃣ Object-wise Reactivity

_final objectWise = Task(title: "Object-wise Reactivity");

ReactiveBuilder(
model: objectWise,
builder: (task) {
return ListTile(
title: Text(task.title),
subtitle: Text(task.status),
trailing: Checkbox(
value: task.completed,
onChanged: (v) => task.completed = v!,
),
);
},
);_

Here, checking the checkbox triggers a rebuild of the entire widget.

3️⃣ Field-wise Reactivity (Optimized)

Sometimes, you only want specific fields to trigger a rebuild:

_final fieldWise = Task(title: "Field-wise Reactivity");

ReactiveBuilder(
model: fieldWise,
fields: [#completed, #status],
builder: (task) {
return ListTile(
title: Text(task.title),
subtitle: Text(task.status),
trailing: Checkbox(
value: task.completed,
onChanged: (v) => task.completed = v!,
),
);
},
)
_
✅ Only changes to completed or status rebuild the widget. Other fields are ignored.

4️⃣ Many → One (Aggregation)

Combine multiple models into a single reactive observer:

_class Dashboard extends ReactiveModel {
final List sources;

Dashboard(this.sources) {
for (final task in sources) {
addNested(task); // listen to many
}
}
}

final manyA = Task(title: "Task A");
final manyB = Task(title: "Task B");
final dashboard = Dashboard([manyA, manyB]);

ReactiveBuilder(
model: dashboard,
builder: () => Column(
children: [
Text("A: ${manyA.completed}"),
Text("B: ${manyB.completed}"),
],
),
);

Updating manyA or manyB automatically rebuilds the dashboard widget.

5️⃣ Many ↔ Many (Shared Models)

Models can be shared across multiple parents, keeping the UI in sync everywhere:

_class Group extends ReactiveModel {
final String name;
final List tasks;

Group({required this.name, required this.tasks}) {
for (final task in tasks) addNested(task);
}
}

final group1 = Group(name: "Group 1", tasks: [objectWise, fieldWise]);
final group2 = Group(name: "Group 2", tasks: [fieldWise, manyA]);

ReactiveBuilder(
model: group1,
builder: (g) => Column(
children: g.tasks.map((t) => Text("• ${t.title} → ${t.completed}")).toList(),
),
);
_
✅ Updating a task reflects automatically across all groups that include it.
🔹 How LIVO Works

  • Models extend ReactiveModel
  • Field setters call notifyListeners(#field) whenever a value changes
  • ReactiveBuilder widgets listen to either the whole object or specific fields
  • Nested models propagate changes upward automatically
  • No streams. No manual wiring. Everything updates safely and efficiently.

🔹 Why LIVO

  • Clean Dart models with minimal boilerplate
  • Fine-grained reactivity for optimized performance
  • ORM-style mental model for easier app design
  • Works seamlessly for single fields, nested models, or shared models

🔗 Links

Pub Package: LIVO

GitHub Repository

💡 Tip for Readers
 Start small: use object-wise for quick prototyping. As your app grows, switch to field-wise or nested models for efficiency.

LIVO makes Flutter state management intuitive and fun — without sacrificing performance.

Top comments (0)