Ad

How To Use MultiBlocProvider And Pass Multiple Provider In A Child Widget?

- 1 answer

I'm still learning how to use bloc. Currently I have two blocs, PostBloc and LocationBloc. The PostBloc will just call api to post and has its own presentation ui. LocationBloc will only get the location and can be used globally in any features. I want to include the location before posting it to the server. But I don't know how to use both bloc. Below code is an example of what I'm trying to do. I don't know if this is the right approach.

class PostPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(create: (_) => sl<PostBloc>()),
        BlocProvider(create: (_) => sl<LocationBloc>()),
      ],
      child: Scaffold(
        body: buildBody(),
      ),
    );
  }

  buildBody() {
    return BlocListener<PostBloc, PostState>(
      listener: (context, state) {
        if (state is Empty) {
        } else if (state is Loading) {
        } else if (state is Loaded) {
          //show snackbar
        } else if (state is Error) {}
      },
      child: InitialDisplay(),
    );
  }
}

class InitialDisplay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          RaisedButton(onPressed: () {
            showDialog(
                barrierDismissible: false,
                context: context,
                builder: (_) {
                  return BlocProvider<PostBloc>.value(
                  value: BlocProvider.of<PostBloc>(context),
                  child: BlocProvider<LocationBloc>.value(
                      value: BlocProvider.of<LocationBloc>(
                          context),
                      child: PostDetails()));
                });
          }),
        ],
      ),
    );
  }
}

class PostDetails extends StatefulWidget {
  const PostDetails({Key key}) : super(key: key);
  @override
  _PostDetailsState createState() => _PostDetailsState();
}

class _PostDetailsState extends State<PostDetails> {
  double latitude;
  double longitude;

  @override
  Widget build(BuildContext context) {
    return BlocListener<LocationBloc, LocationBlocState>(
      listener: (context, state) {
        if (state is LocationBlocLoadedEvent) {
          latitude = state.location.latitude;
          longitude = state.location.longitude;
        }
      },
      child: Dialog(
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
          backgroundColor: Colors.transparent,
          child: _buildWidget(context)),
    );
  }

  _buildWidget(BuildContext context) {
    return Container(
        height: MediaQuery.of(context).size.height * .55,
        decoration: BoxDecoration(
            color: Colors.white,
            shape: BoxShape.rectangle,
            borderRadius: BorderRadius.circular(16)),
        child: RaisedButton(onPressed: () {
          postWithLocation();
        }));
  }

  void postWithLocation() {
    BlocProvider.of<LocationBloc>(context).add(LoadLocationEvent());
    BlocProvider.of<PostBloc>(context).add(PostEvent(
      post: 'Test post with location',
      long: longitude,
      lat: latitude,
    ));
  }
}

EDIT: Here is the error that I'm getting. I think its because I only pass PostBloc cause I don't know how to pass multiple bloc or if this is the right thing to do.

Error: Could not find the correct Provider<LocationBloc> above this BlocListener<LocationBloc, LocationBlocState> Widget

Edit 2: The BlocLocation is working now. I just passed the blocLocation value same as the PostBloc. My problem now is PostBloc is not waiting for LocationBloc to get location before submitting in postWithLocation() function

BlocProvider<PostBloc>.value(
  value: BlocProvider.of<PostBloc>(context),
  child: BlocProvider<LocationBloc>.value(
      value: BlocProvider.of<LocationBloc>(
          context),
      child: PostDetails()));
Ad

Answer

This is the correct way to do the sequence job in Bloc.

// _PostDetailsState
  @override
  Widget build(BuildContext context) {
    return BlocListener<LocationBloc, LocationBlocState>(
      listener: (context, state) {
        if (state is LocationBlocLoadedEvent) {
          final latitude = state.location.latitude;
          final longitude = state.location.longitude;
          BlocProvider.of<PostBloc>(context).add(PostEvent(
            post: 'Test post with location',
            long: longitude,
            lat: latitude,
          ));
        }
      },
      child: Dialog(
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
          backgroundColor: Colors.transparent,
          child: _buildWidget(context)),
    );
  }

Also remove some code in postWithLocation.

  void postWithLocation(context) {
    BlocProvider.of<LocationBloc>(context).add(LoadLocationEvent());
  }
Ad
source: stackoverflow.com
Ad