Skip to content

dobx/dobx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dart observables for flutter

A micro library inspired by vue's observables (small, simple).

The name is a play on words from mendix's mobx for react.

Here's a live sample todo app

Example

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
  }

App class

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); }
}

Model

This boilerplate is usually generated by a compiler with a schema like:

message Todo {
  required string title = 1;
  optional bool completed = 2 [ default = false ];
}

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

Releases

No releases published

Packages

No packages published

Languages