context_di 0.2.4 copy "context_di: ^0.2.4" to clipboard
context_di: ^0.2.4 copied to clipboard

Flutter Provider based Dependency Injection Tool supports two main scopes factory and singleton with code generation

pub package

Project is based on Provider package. Main goal is to create a tool that will help reduce boilerplate code and make it easier to work with dependency injection via Provider.

Features #

2 scopes are supported:

  • factory scope (creates instance every time)
  • singleton scope (one instance per feature)

Parameterized factories


final bloc = context.read<CreateEntityBloc>()(context, (id: _id));
//or
final bloc = createEntityBloc(context, (id: _id));

Scopes based on widget tree lifecycle: dispose.gif

Added Code Generation #

add build_runner and context_di_generator to your project

dev_dependencies:
  build_runner:
  context_di_generator:

create empty feature class that extends FeatureDependencies with _$(ClassName)Mixin

part 'basic_feature.g.dart';

typedef EntityBlocParams = ({int id});

void _dispose(BuildContext context, Repository instance) => instance.dispose();

@Feature()
@Singleton(Repository, as: RepositoryInterface, dispose: _dispose)
@Factory(ListBloc)
@Factory(EntityBloc, params: EntityBlocParams)
class BasicFeature extends FeatureDependencies with _$BasicFeatureMixin {
  const Basic({super.key, super.builder});
}

then run dart run build_runner build -d to generate feature file

generation could be combined with manual registration approach

@Feature()
@Singleton(SupabaseAuthUtils)
@Singleton(GetUserHandler)
@Singleton(SupabaseAuthApi)
@Singleton(AuthRepository, as: IAuthRepository)
@Singleton(ExternalOpenUrl)
class AppFeature extends FeatureDependencies with _$AppFeatureMixin {
  const AppFeature(this.initialDependencies, {super.key, super.builder});

  final List<Registration> initialDependencies;

  @override
  List<Registration> register() => [
    ...initialDependencies, //some initial dependencies
    ...super.register(), //generated dependencies
    registerSingleton<AppBloc>(
            (c) => AppBloc(c.resolve(), c), dispose: (c, bloc) => bloc.close()
    ), //manual registration
  ];
}

if clas has constructor with BuildContext parameter, context will be passed to constructor automatically

Resolve approach #

Now better approach is use code generation and resolve like this:

factories:


final bloc = context.read<CreateListBloc>()(context);

final bloc = context.read<CreateEntityBloc>()(context, (id: _id));

//new preferred way
final bloc = createEntityBloc(context, (id: _id));

createEntityBloc generated top level function

CreateListBloc and CreateEntityBloc will be generated

singletons:


final repo = contex.read<RepositoryInterface>();

old context.resolve<T>() approach still works

Getting started #

Create a feature class that extends FeatureDependencies and override register method. When you need to register a dependency, use registerSingleton, registerSingletonAs, registerFactory or registerParamsFactory methods.

typedef EntityBlocParams = ({int id});

class BasicFeature extends FeatureDependencies {
  const BasicFeature({super.key, super.builder});

  @override
  List<Registration> register(BuildContext context) {
    return [
      registerSingletonAs<Repository, RepositoryInterface>(
            (context) => Repository(context.resolve()),
        dispose: (context, instance) => instance.dispose(),
      ),
      registerFactory(
            (context) => ListBloc(context.resolve<RepositoryInterface>()),
      ),
      registerParamsFactory(
            (context, EntityBlocParams params) =>
            EntityBloc(
              params.id,
              context.resolve(),
            ),
      ),
    ];
  }
}

Add to your widget tree

@RoutePage()
class BasicFeaturePage extends StatelessWidget {
  const BasicFeaturePage({super.key});

  @override
  Widget build(BuildContext context) {
    return BasicFeature(builder: (context) {
      return _Content();
    });
  }
}

And resolve dependencies in feature context

class _Content extends StatefulWidget {
  const _Content();

  @override
  State<_Content> createState() => _ContentState();
}

class _ContentState extends State<_Content> {
  late final ListBloc _listBloc;

  int? _selectedId;

  @override
  void initState() {
    _listBloc = context.resolve<ListBloc>(); //old approach
    _listBloc = context.read<CreateListBloc>()(context); //old approach
    _listBloc = createListBloc(context); //new approach
    super.initState();
  }

  @override
  void dispose() {
    _listBloc.close();
    super.dispose();
  }

  //...
}
@RoutePage()
class EntityPage extends StatelessWidget {
  final int _id;

  const EntityPage({super.key, required int id}) : _id = id;

  @override
  Widget build(BuildContext context) {
    return BlocProvider<EntityBloc>(
      create: (_) => context.resolveWithParams((id: _id)),
      child: BlocBuilder<EntityBloc, EntityState>(builder: (context, state) {
        return Scaffold(
          body: switch (state) {
            Initial() => Center(child: CircularProgressIndicator.adaptive()),
            Loaded() => _Content(state),
          },
        );
      }),
    );
  }
}

basic resolve


final listBloc = context.resolve<ListBloc>(); //old approach
final listBloc = context.read<CreateListBloc>()(context); //old approach
final listBloc = createListBloc(context); //new approach

parametrized resolve


var create = (_) => context.resolveWithParams((id: _id)); //old approach
var create = (_) => context.read<CreateEntityBloc>()(context, (id: _id)); //old approach
var create = (_) => createEntityBloc(context, (id: _id)); //new approach

Additional information #

IMPORTANT NOTICE! You can use resolve or resolveWithParams only in feature context, in lover levels of widget tree. If you have some core dependencies register them on app level:

class MyApp extends StatelessWidget {
  MyApp({super.key});

  final _router = AppRouter();

  @override
  Widget build(BuildContext context) {
    return _RootDependencies(
      builder: (context) => MaterialApp.router(
        title: 'Flutter Demo',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          useMaterial3: true,
        ),
        routerConfig: _router.config(),
      ),
    );
  }
}

class _RootDependencies extends FeatureDependencies {
  const _RootDependencies({super.builder});

  @override
  List<Registration> register(BuildContext context) {
    return [
      registerSingleton<Logger>((_) => Logger()),
    ];
  }
}
3
likes
160
points
174
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter Provider based Dependency Injection Tool supports two main scopes factory and singleton with code generation

Repository (GitHub)

Topics

#dependency #injection #dependency-injection #provider #flutter

Documentation

API reference

License

MIT (license)

Dependencies

flutter, provider

More

Packages that depend on context_di