felangel / bloc

A predictable state management library that helps implement the BLoC design pattern

Home Page:https://bloclibrary.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

docs: What is the best way to use riverpod with bloc for dependency injection?

MiniSuperDev opened this issue · comments

Hi, if i want to use riverpod for the dependency injection instead of provider what is the best way?

Using riverpod over provider for dependency injection can have several advantages explained better here, but in general we can have blocs of the same type and avoid using ProxyProvider.

I see riverbloc, but using it makes it very coupled to riverpod and difficult to reuse the widgets in a project that does not use it.

By the way, this is not a request to change provider to riverpod, but a way to document the best way to use it in conjunction with riverpod, since it is very popular for dependency injection and other things that are not relevant here.

Below you can see an example of what I'm doing, maybe do you recommend other way, create extension, widgets, etc.?
In my example we can reuse the bloc, and the widgets that use the bloc in project with and without riverpod.

Thanks

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'main.g.dart';

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);

  void increment() => emit(state + 1);
}

@riverpod // <- generate conuterCubitProvider
CounterCubit counterCubit(CounterCubitRef ref) {
  final cubit = CounterCubit();
  ref.onDispose(cubit.close);
  return cubit;
}

void main() {
  runApp(const ProviderScope(child: MainApp()));
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: CounterScreen());
  }
}

class CounterScreen extends ConsumerWidget {
  const CounterScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
        body: ListView(
      padding: const EdgeInsets.all(16),
      children: [
        CounterView(bloc: ref.watch(counterCubitProvider)),
        const Divider(),
        ElevatedButton(
          onPressed: () => ref.invalidate(counterCubitProvider),
          child: const Text('Simulate dependency change'),
        ),
      ],
    ));
  }
}

class CounterView extends StatelessWidget {
  const CounterView({
    super.key,
    this.bloc,
  });
  final CounterCubit? bloc;

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterCubit, int>(
        bloc: bloc,
        builder: (context, state) {
          return Column(
            children: [
              Text('Counter: $state'),
              ElevatedButton(
                onPressed: () {
                  final bloc = this.bloc ?? context.read<CounterCubit>();
                  bloc.increment();
                },
                child: const Text('Increment'),
              ),
            ],
          );
        });
  }
}

Hi @MiniSuperDev 👋
Thanks for opening an issue!

The approach you demonstrated seems totally fine to me! You can always decouple things even further so that you have "ContainerWidgets" (widgets which provide dependencies) and "PresentationWidgets" (plain-old stateless widgets which render some UI in response to some inputs).

Hope that helps! Closing for now but feel free to add any additional comments and I'm happy to continue the discussion or joins the Bloc Discord to chat with members of the community 👍

Thanks @felangel.
It sounds pretty interesting. Could you give more details about the "ContainerWidgets" and "PresentationWidgets", perhaps a dummy example, it doesn't matter if it is pseudo code.