A micro library inspired by vue's observables (small, simple).
The name is a play on words from mendix's mobx for react.
lib/main.dart
import 'package:flutter/material.dart';
import 'package:dobx/dobx.dart';
import 'package:todo/todo.dart';
void main() {
runApp(new AppWidget());
}
// Dynamic parts
enum Root {
$todo_input,
$todo_list,
}
const String HEADER_TITLE = 'Todo List';
class AppWidget extends StatelessWidget {
final App app = new App('');
// widget factory for the reactive views
// this links the observables and the stateful widgets subscribed to them.
final WF wf = WF.get(0);
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: HEADER_TITLE,
theme: ui.THEME,
home: new Scaffold(
appBar: new AppBar(
title: new Text(HEADER_TITLE),
actions: [
ui.icon_defpad_btn(Icons.filter_list, _filterPressed, color: Colors.white),
new ui.AppBarPopup(_filterSelected, ['All', 'Pending', 'Completed']),
],
bottom: new ui.AppBarWidget(newBar),
),
body: new Padding(
padding: const EdgeInsets.only(top: 8.0),
child: wf.$($todo_list, Root.$todo_list),
),
),
);
}
void _filterSelected(int idx) {
if (!app.todos.isEmpty)
app.filter = Todo_Filter.values[idx];
}
void _filterPressed() {
if (!app.todos.isEmpty)
app.filter = App.rotate(app.filter);
}
Widget newBar(BuildContext context) {
return new Column(
children: <Widget>[
ui.fluid_box(ui.input_label('What needs to be done?'),
ui.fluid_box(wf.$($todo_input, Root.$todo_input)),
],
);
}
void _titleChanged(InputValue iv) {
final String title = iv.text.trim();
if (title.isEmpty) return;
// newest first
app.todos.insert(0, Todo.$create(title, completed: false));
// pass null to force clear
app.pnew.title = null;
}
Widget $todo_input(BuildContext context) {
return ui.input(app.pnew.title, _titleChanged);
}
Widget $todo_list(BuildContext context) {
// build your todo list
}
todo/lib/app.dart
import 'package:dobx/dobx.dart';
import './todo.dart';
enum Todo_Filter {
ALL,
PENDING,
COMPLETED,
}
class App extends PubSub {
final List<Todo> _todos = new ObservableList<Todo>();
final Todo pnew;
Todo_Filter _filter = Todo_Filter.ALL;
App(String initialText) : pnew = Todo.$createObservable(initialText);
// Returns the instance (no slicing happens if null is provided)
// dobx uses this existing method signature as a hook to subscribe the caller when tracking is on
// Also, maybe 'sublist' could read as subscribe to list? :-)
List<Todo> get todos => _todos.sublist(null);
// pojo property observables
get filter { $sub(1); return _filter; }
set filter(String filter) { if (filter != null && filter == _filter) return; _filter = filter ?? Todo_Filter.ALL; $pub(1); }
}
This boilerplate is usually generated by a compiler with a schema like:
message Todo {
required string title = 1;
optional bool completed = 2 [ default = false ];
}
with dobx_gen
lib/observables.dart
library todo.observables;
import 'package:dobx/dobx.dart';
import 'package:dobx_gen/core.dart';
part 'observables.g.dart';
@dobx
abstract class Todo {
String title;
bool completed;
factory Todo() => _$Todo(); // this method is generated
}
Full example code in todo_example