Skip to content

Demos ​

Every demo is a screen in one small Flutter web app, built from docs/demo and running on this site. The code samples in the guides are imported from the same files, so what you read is what's running.

Open the demo app ↗

The basics ​

A root-scoped CounterService keeps its count in a stream. A ViewModel observes the stream, and DependenciesBuilder rebuilds when it changes.

Show the code
dart
/// App-wide counter. Root scope (the default), so every screen shares one.
class CounterService implements InjectableWithDisposeInterface {
  static final Factory = InjectableFactory.withoutDependencies<CounterService>(
    () => CounterService(),
  );
  static final DefaultProvider = Provider(CounterService);

  // State lives in a stream, not in plain fields.
  final _count = BehaviorSubject<int>.seeded(0);
  Stream<int> get count => _count.stream;

  void increment() => _count.add(_count.value + 1);

  @override
  Future<void> dispose() => _count.close();
}
dart
class CounterViewModel extends DependencyBuilderViewModelBase {
  CounterViewModel()
      : super(dependencyDataList: DependencyDataBuilder.list([CounterService]));

  late CounterService _counter;
  int count = 0;

  @override
  void setPropertiesOnDependencyResolution(Dependencies deps) {
    _counter = deps<CounterService>();
  }

  @override
  Iterable<Stream> getListOfStreamsToObserve() => [_counter.count];

  @override
  void updateViewModelPropertiesOnStreamEmission(List snapshots) {
    count = snapshots[0] as int;
  }

  void increment() => _counter.increment();
}
dart
class BasicsDemo extends StatelessWidget {
  const BasicsDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return DependenciesBuilder<CounterViewModel>(
      cleanViewModelInstance: CounterViewModel(),
      builder: (context, vm) => dependencyBuilderViewModelSimpleStateHandlingBuildUtilityFn(
        context: context,
        viewModel: vm,
        loadingWidget: const Center(child: CircularProgressIndicator()),
        build: (context, vm) => Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Text('${vm.count}', key: const Key('count'), style: Theme.of(context).textTheme.displayMedium),
              const SizedBox(height: 16),
              FilledButton.icon(
                onPressed: vm.increment,
                icon: const Icon(Icons.add),
                label: const Text('Increment'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Async services ​

A service that takes two seconds to open, and a screen that shows a loading state until it's ready. Read the guide.

Async services demoOpen full screen ↗

Scoped services ​

Two tables, each with its own TableManager, sharing one ProductRepository. Read the guide.

Scoped services demoOpen full screen ↗

Released under the MIT License.