felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.79k stars 3.39k forks source link

How can I get a global store(bloc) out of the widgets tree #526

Closed jiyarong closed 5 years ago

jiyarong commented 5 years ago

I want dispatch an event in my api_serivice.dart when api response auth failed, just like

userBloc.dispatch(UserAuthFailed());

But I cannot get userBloc in my api_serivice.dart like

final userBloc = BlocProvider.of<UserBloc>(context);

I try to import a new Userbloc in my api_serivice.dart and I dispatch an event

 UserBloc ub = UserBloc();
 ub.dispatch(UserAuthFailed());

But this Userbloc obviously not equal to another one

Please help me

bigworld12 commented 5 years ago

business logic should be done in the BLoC (which actually stands for business logic component). your api service is a provider, not a bloc, a bloc should use the provider and dispatch events accordingly. check the architecture documentation here https://felangel.github.io/bloc/#/architecture

felangel commented 5 years ago

Hi @jiyarong 👋 Thanks for opening an issue!

To provide a global instance of a bloc you should use BlocProvider at the root of your widget tree

@override
Widget build(BuildContext context) {
  return BlocProvider<MyBloc>(
    builder: (context) => MyBloc(),
    child: MaterialApp(...),
  );
}

Then from anywhere in your app you can access the bloc via BuildContext like:

BlocProvider.of<MyBloc>(context);

Hope that helps and as @bigworld12 mentioned, this is all covered in the bloc documentation 👍

jiyarong commented 5 years ago

I cannot get context in my api_service.dart, that's the point

felangel commented 5 years ago

@jiyarong you should be injecting the bloc into your ApiService via the constructor instead of directly trying to access it via BuildContext.

class MyWidget extends StatefulWidget {
  @override
  State<MyWidget createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  ApiService _apiService;

  @override
  void initState() {
    super.initState();
    _apiService = ApiService(myBloc: BlocProvider.of<MyBloc>(context));
  }

  @override
  Widget build(BuildContext context) { ... }
}

Also like @bigworld12 mentioned, what you're doing is not standard. You should be injecting the ApiService (Repository) into your Bloc not the other way around.

jiyarong commented 5 years ago

@jiyarong you should be injecting the bloc into your ApiService via the constructor instead of directly trying to access it via BuildContext.

class MyWidget extends StatefulWidget {
  @override
  State<MyWidget createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  ApiService _apiService;

  @override
  void initState() {
    super.initState();
    _apiService = ApiService(myBloc: BlocProvider.of<MyBloc>(context));
  }

  @override
  Widget build(BuildContext context) { ... }
}

Also like @bigworld12 mentioned, what you're doing is not standard. You should be injecting the ApiService (Repository) into your Bloc not the other way around.

This is my main.dart

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

class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<UserBloc>(
        builder: (context) => UserBloc(),
        child: Provider(
            builder: (_) => MyService.create(userBloc: BlocProvider.of<UserBloc>(context)),
            dispose: (context, MyService service) => service.client.dispose(),
            child: MaterialApp(
                title: 'Flutter Demo',
                theme: ThemeData(
                  primarySwatch: Colors.blue,
                ),
                onGenerateRoute: RouteGenerator.generateRoute,
                home: Root())));
  }
}

I got some errors: I/flutter (11393): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ I/flutter (11393): The following assertion was thrown building Builder: I/flutter (11393): BlocProvider.of() called with a context that does not contain a Bloc of type UserBloc. I/flutter (11393): No ancestor could be found starting from the context that was passed to I/flutter (11393): BlocProvider.of(). I/flutter (11393): This can happen if the context you use comes from a widget above the BlocProvider. I/flutter (11393): This can also happen if you used BlocProviderTree and didn't explicity provide I/flutter (11393): the BlocProvider types: BlocProvider(bloc: UserBloc()) instead of I/flutter (11393): BlocProvider(bloc: UserBloc()). I/flutter (11393): The context used was: MyApp(state: _MyAppState#a5822) I/flutter (11393): I/flutter (11393):

bigworld12 commented 5 years ago

why you have builder: (_) => instead of builder: (context) => ?