jab 0.0.4

  • Readme
  • Changelog
  • Example
  • Installing
  • 76

Jab #

Jab is a simple hierarchical dependency injection framework for Flutter. It offers easy dependency overriding in the Widget tree and provides out-of-the-box support for the BLoC pattern.

Getting Started #

In the Jab Example app, there are three main services: Logger, CounterStore and CounterBloc. Logger has no dependencies. CounterStore depends on the Logger, and CounterBloc depends on both the Logger and the CounterStore.

Resolving Dependencies #

All services in Jab should be provided using a JabFactory<T> method that passes an injector function as a parameter and is supposed to return an instance of a service, e.g:

final providers = <JabFactory>[
  (jab) => ServiceA(),
  (jab) => ServiceB(jab()),
  (jab) => ServiceC(jab(), jab()),
  (jab) => ServiceD(jab()),
];

Root Injector #

The Logger service is initialized by the root injector before the application starts:

void main() {
  Jab.provideForRoot([
    (_) => Logger(),
  ]);
  runApp(CounterApp());
}

Consider using the root injector for singleton services in your application.

The root injector can also be used during testing.

Tree Injector #

The two remaining services (CounterStore and CounterBloc) are provided to a Jab that wraps the application in the widget tree:

class CounterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Jab(
      providers: () => [
        (jab) => CounterStore(jab()),
        (jab) => CounterBloc(jab(), jab()),
      ],
      child: MaterialApp(
        title: 'Jab Examples',
      ),
    );
  }
}

Consider using widget tree injection to provide your services with a lifecycle (for example, you can create a Jab that wraps a module in your app that can only be accessed by authenticated users).

If your service implements Sink (e.g. Sink<void>), Jab will call the close method on it before the Jab instance gets disposed (i.e. is removed from the widget tree).

Getting a Service #

To obtain an instance of a service in your code, you can call the Jab.get<Service>(context) function. For example:

ClearButton(
  onPressed: () {
    Jab.get<CounterStore>(context).clear();
  },
)

Jab with BLoC #

If you're using Jab with the Business Logic Component pattern, consider providing an instance of the Jab using the BlocMixin<T> with your State:

class _CounterViewState extends ViewState<CounterView> with BlocMixin<CounterBloc> {

Important: insted of extending State, you need to extend ViewState from the Jab library.

Once you use theBlocMixin in your ViewState, you can simply call the bloc property to acces your business component:

IncrementButton(
  onPressed: () {
    bloc.add(CounterEvent.increment);
  },
)

You can use Jab with both a custom BloC implementation and with the bloc library. If you create your BloC yourself, consider making it implement Sink<void>. Jab will detect that your BloC implements a Sink and correctly dispose it by calling the close method when the ViewState is removed from the widget tree.

Overriding Dependencies #

Anywhere in the widget tree, you can override an existing service and provide a custom implementation. The Jab Example app provides a custom implementation of the CounterBloc called GoldenCounterBloc:

class GoldenCounterView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Jab(
      providers: () => [
        (jab) => GoldenCounterBloc(jab(), jab()),
      ],
      child: CounterView(),
    );
  }
}

Thanks to hierarchical depency injection, the CounterView class doesn't have to change to reflect the differences in the implementation of the two BLoCs.

[0.0.1] - 5 May 2020 #

  • Initial Jab library implementation

[0.0.2] - 5 May 2020 #

  • Added a usage example

[0.0.3] - 5 May 2020 #

  • Added the option to use root as the main dependency injector (e.g. for testing purposes).

[0.0.4] - 2 June 2020 #

  • Breaking change: If an instance of Service cannot be found in the Widget tree, Jab will attempt to find the Service instance in all injectors which are currently initialized.

example/main.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:jab/jab.dart';

void main() {
  runApp(CounterApp());
}

enum CounterEvent { increment, decrement, clear }

class CounterBloc extends ValueNotifier<int> implements Sink<CounterEvent> {
  CounterBloc() : super(0);

  @override
  void add(CounterEvent event) {
    switch (event) {
      case CounterEvent.increment:
        value = value + 1;
        break;
      case CounterEvent.decrement:
        value = value - 1;
        break;
      case CounterEvent.clear:
        value = 0;
        break;
    }
  }

  @override
  void close() {
    dispose();
  }
}

class CounterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Jab(
      providers: () => [
        (_) => CounterBloc(),
      ],
      child: MaterialApp(
        title: 'Counter',
        home: CounterView(),
      ),
    );
  }
}

class CounterView extends StatefulWidget {
  @override
  _CounterViewState createState() => _CounterViewState();
}

class _CounterViewState extends ViewState<CounterView> with BlocMixin<CounterBloc> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Counter'),
      ),
      body: ValueListenableBuilder<int>(
        valueListenable: bloc,
        builder: (BuildContext context, int value, _) {
          return Center(
            child: Text(
              value.toString(),
              style: TextStyle(fontSize: 48),
            ),
          );
        },
      ),
      floatingActionButton: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          IncrementButton(
            onPressed: () {
              bloc.add(CounterEvent.increment);
            },
          ),
          SizedBox(height: 16),
          DecrementButton(
            onPressed: () {
              bloc.add(CounterEvent.decrement);
            },
          ),
          SizedBox(height: 16),
          ClearButton(
            onPressed: () {
              bloc.add(CounterEvent.clear);
            },
          )
        ],
      ),
    );
  }
}

class IncrementButton extends StatelessWidget {
  const IncrementButton({Key key, this.onPressed}) : super(key: key);

  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      heroTag: 'Increment',
      tooltip: 'Increment',
      child: Icon(Icons.add),
      onPressed: onPressed,
    );
  }
}

class DecrementButton extends StatelessWidget {
  const DecrementButton({Key key, this.onPressed}) : super(key: key);

  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      heroTag: 'Decrement',
      tooltip: 'Decrement',
      child: Icon(Icons.remove),
      onPressed: onPressed,
    );
  }
}

class ClearButton extends StatelessWidget {
  const ClearButton({Key key, this.onPressed}) : super(key: key);

  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      heroTag: 'Clear',
      tooltip: 'Clear',
      child: Icon(Icons.delete),
      onPressed: onPressed,
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  jab: ^0.0.4

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:jab/jab.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
56
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
90
Overall:
Weighted score of the above. [more]
76
Learn more about scoring.

We analyzed this package on Jul 14, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.15
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

Because:

  • jab that is a package requiring null.

Health suggestions

Format lib/jab.dart.

Run flutter format to format lib/jab.dart.

Maintenance suggestions

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.6.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8 1.2.2
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
flutter_test
jab_example_app