简体   繁体   中英

update sibling state (data) flutter bloc

I am trying to update data in a sibling widget which already loaded data at first. The second widget adds to the same data and fetches it at the same time showing an updated data. The new data is not reflected in the first widget. Take a shopping cart and quantity of items as an example. When I add new items or change quantities in the cart it does not reflect in the cart. All data is remotely called in a django rest api. If I reload the app the new data shows in the cart.

this is the widget where i want to change the data in it

class MainScreen extends StatefulWidget {
  final TokenResponse token;

  const MainScreen({Key key, this.token});

  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  BottomNavBarBloc _bottomNavBarBloc;
  CartBloc cartBloc;

  @override
  void initState() {
    _bottomNavBarBloc = BottomNavBarBloc();

    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    cartBloc = BlocProvider.of<CartBloc>(context);
    cartBloc.add(FetchOrderSummaryEvent());


    return Scaffold(
      backgroundColor: Colors.white,
      body: AnnotatedRegion<SystemUiOverlayStyle>(
        value: const SystemUiOverlayStyle(
          statusBarColor: Colors.transparent,
        ),
        sized: false,
        child: StreamBuilder<NavBarItem>(
            stream: _bottomNavBarBloc.itemStream,
            initialData: _bottomNavBarBloc.defaultItem,
            builder:
                (BuildContext context, AsyncSnapshot<NavBarItem> snapshot) {
              switch (snapshot.data) {
                case NavBarItem.HOME:
                  return HomeScreen();
                case NavBarItem.OFFERS:
                  return OffersScreen();
                case NavBarItem.REWARDS:
                  return _testScreen();
                case NavBarItem.FAVORITE:
                  return _testScreen();
                case NavBarItem.BAG:
                  return CartScreen();
                case NavBarItem.ACCOUNT:
                  return AccountMainScreen();
              }
              return Container();
            }),
      ),
      bottomNavigationBar: StreamBuilder(
        stream: _bottomNavBarBloc.itemStream,
        initialData: _bottomNavBarBloc.defaultItem,
        builder: (BuildContext context, AsyncSnapshot<NavBarItem> snapshot) {
          return BottomNavigationBar(
            unselectedItemColor: Style.Colors.secondaryColor,
            selectedItemColor: Style.Colors.primaryColor,
            backgroundColor: Colors.white,
            selectedFontSize: 10.0,
            unselectedFontSize: 10.0,
            type: BottomNavigationBarType.fixed,
            currentIndex: snapshot.data.index,
            onTap: _bottomNavBarBloc.pickItem,
            items: [
              BottomNavigationBarItem(
                  title: Padding(
                    padding: EdgeInsets.only(top: 5.0),
                    child: Text("Home"),
                  ),
                  icon: Icon(
                    FontAwesomeIcons.bars,
                    color: Style.Colors.secondaryColor,
                  ),
                  activeIcon: Icon(
                    FontAwesomeIcons.bars,
                    color: Style.Colors.primaryColor,
                  )),
              BottomNavigationBarItem(
                  title: Padding(
                    padding: EdgeInsets.only(top: 5.0),
                    child: Text("Offers"),
                  ),
                  icon: Icon(
                    FontAwesomeIcons.tag,
                    color: Style.Colors.secondaryColor,
                  ),
                  activeIcon: Icon(
                    FontAwesomeIcons.tag,
                    color: Style.Colors.primaryColor,
                  )),
              BottomNavigationBarItem(
                  title: Padding(
                    padding: EdgeInsets.only(top: 5.0),
                    child: Text("Rewards"),
                  ),
                  icon: Icon(
                    FontAwesomeIcons.gift,
                    color: Style.Colors.secondaryColor,
                  ),
                  activeIcon: Icon(
                    FontAwesomeIcons.gift,
                    color: Style.Colors.primaryColor,
                  )),
              BottomNavigationBarItem(
                  title: Padding(
                    padding: EdgeInsets.only(top: 5.0),
                    child: Text("Favorite"),
                  ),
                  icon: Icon(
                    FontAwesomeIcons.heart,
                    color: Style.Colors.secondaryColor,
                  ),
                  activeIcon: Icon(
                    FontAwesomeIcons.heart,
                    color: Style.Colors.primaryColor,
                  )),
              BottomNavigationBarItem(
                  title: Padding(
                    padding: EdgeInsets.only(top: 5.0),
                    child: Text("Bag"),
                  ),
                  icon: Container(
                    width: 30,
                    height: 25,
                    child: Stack(children: <Widget>[
                      Icon(
                        FontAwesomeIcons.shoppingBag,
                        color: Style.Colors.secondaryColor,
                      ),
                      BlocBuilder<AuthenticationBloc, AuthenticationState>(
                        builder: (context, state) {
                          if (state is AuthenticationAuthenticated) {

                            return BlocBuilder<CartBloc, CartState>(
                              builder: (context, state) {
                                if (state is CartLoaded) {

                                  print("loaded ${state.order.totalItemQuantity}"); // <=== change this data
                                  return Positioned(
                                    right: 0,
                                    top: 0,
                                    child: Container(
                                      height: 20.0,
                                      width: 20.0,
                                      decoration: BoxDecoration(
                                        shape: BoxShape.circle,
                                        color: Colors.red,
                                        border: Border.all(
                                          width: 1.0,
                                          color: Colors.white,
                                        ),
                                      ),
                                      child: Column(
                                        mainAxisAlignment:
                                            MainAxisAlignment.center,
                                        crossAxisAlignment:
                                            CrossAxisAlignment.center,
                                        children: <Widget>[
                                          Padding(
                                            padding: const EdgeInsets.only(
                                              left: 1.0,
                                              bottom: 1.0,
                                            ),
                                            child: Text(
                                              state.order.totalItemQuantity !=null? state.order.totalItemQuantity.toString():"",
                                              style: TextStyle(
                                                color: Colors.white,
                                                fontSize: 10.0,
                                                fontWeight: FontWeight.bold,
                                              ),
                                            ),
                                          ),
                                        ],
                                      ),
                                    ),
                                  );
                                } else {
                                  return Container(

                                  );
                                }
                              },
                            );
                          } else {
                            return Text(" ");
                          }
                        },
                      )
                    ]),
                  ),
                  activeIcon: Container(
                    width: 30,
                    height: 25,
                    child: Stack(children: <Widget>[
                      Icon(
                        FontAwesomeIcons.shoppingBag,
                        color: Style.Colors.primaryColor,
                      ),
                      BlocBuilder<AuthenticationBloc, AuthenticationState>(
                        builder: (context, state) {
                          if (state is AuthenticationAuthenticated) {

                            return BlocBuilder<CartBloc, CartState>(
                              builder: (context, state) {
                                if (state is CartLoaded) {
                                  print("loaded ${state.order.totalItemQuantity}");
                                  return Positioned(
                                    right: 0,
                                    top: 0,
                                    child: Container(
                                      height: 20.0,
                                      width: 20.0,
                                      decoration: BoxDecoration(
                                        shape: BoxShape.circle,
                                        color: Colors.red,
                                        border: Border.all(
                                          width: 1.0,
                                          color: Colors.white,
                                        ),
                                      ),
                                      child: Column(
                                        mainAxisAlignment:
                                        MainAxisAlignment.center,
                                        crossAxisAlignment:
                                        CrossAxisAlignment.center,
                                        children: <Widget>[
                                          Padding(
                                            padding: const EdgeInsets.only(
                                              left: 1.0,
                                              bottom: 1.0,
                                            ),
                                            child: Text(
                                              state.order.totalItemQuantity !=null? state.order.totalItemQuantity.toString():"",
                                              style: TextStyle(
                                                color: Colors.white,
                                                fontSize: 10.0,
                                                fontWeight: FontWeight.bold,
                                              ),
                                            ),
                                          ),
                                        ],
                                      ),
                                    ),
                                  );
                                } else {
                                  return Container(

                                  );
                                }
                              },
                            );
                          } else {
                            return Text(" ");
                          }
                        },
                      )
                    ]),
                  ),),
              BottomNavigationBarItem(
                  title: Padding(
                    padding: EdgeInsets.only(top: 5.0),
                    child: BlocBuilder<AuthenticationBloc, AuthenticationState>(
                      builder: (context, state) {
                        if (state is AuthenticationAuthenticated) {
                          return Text("Account");
                        } else {
                          return Text("Login");
                        }
                      },
                    ),
                  ),
                  icon: Icon(
                    FontAwesomeIcons.user,
                    color: Style.Colors.secondaryColor,
                  ),
                  activeIcon: Icon(
                    FontAwesomeIcons.user,
                    color: Style.Colors.primaryColor,
                  )),
            ],
          );
        },
      ),
    );
  }

  Widget _testScreen() {
    return Center(
      child: Text(
        'Test Screen',
        style: TextStyle(
          fontWeight: FontWeight.bold,
          color: Colors.red,
          fontSize: 25.0,
        ),
      ),
    );
  }
}

i call the API in a sibling widget here i am posting the handling method that was called after pressing a button from a sibling widget:

handleAddItemFromCart(
      String slug, List<ItemVariations> itemVariations, BuildContext context) {
    final cartBloc = BlocProvider.of<CartBloc>(context);
    final currentState = cartBloc.state;
    if( currentState is CartLoaded){
      print("order quantity: ${currentState.order.totalItemQuantity.toString()}");
    }
    print("current state " + currentState.toString());
    cartBloc.add(AddItemToCartEvent(
        slug: slug, variations: formatVariations(itemVariations)));
    cartBloc.add(FetchOrderSummaryEvent());

  }

cart_event.dart

@immutable
abstract class CartEvent extends Equatable {
  CartEvent([List props = const []]):super();


}

class FetchOrderSummaryEvent extends CartEvent {

  FetchOrderSummaryEvent([List props = const[]]) : super(props);

  Order order;

  @override
  List<Object> get props => [this.order];
}

class RemoveItemFromCartEvent extends CartEvent {
  String slug;
  List<int> variations;

  RemoveItemFromCartEvent({@required this.slug, this.variations});

  @override
  List<Object> get props => [];
}

class AddItemToCartEvent extends CartEvent {
  String slug;
  List<int> variations;

  AddItemToCartEvent({@required this.slug, @required this.variations});

  @override
  List<Object> get props => [];
}

class RemoveItemEvent extends CartEvent {
  @override
  List<Object> get props => [];
}

class CheckoutEvent extends CartEvent {
  String stripeToken;
  int selectedAddress;

  CheckoutEvent({@required this.stripeToken, @required this.selectedAddress});

  @override
  List<Object> get props => [];
}

cart_state.dart

@immutable
abstract class CartState extends Equatable {
  CartState([List props = const[]]):super();

  @override
  List<Object> get props => [];
}

class CartInitial extends CartState {

  @override
  // TODO: implement props
  List<Object> get props => [];
}

class CartLoading extends CartState {
  @override
  // TODO: implement props
  List<Object> get props => [];
}

class CartLoaded extends CartState {
  Order order;

  CartLoaded({@required this.order});

  CartLoaded copyWith({Order order}) {
    return CartLoaded(order: order ?? this.order);
  }

  @override
  // TODO: implement props
  List<Object> get props => [order];
}

class CartFailure extends CartState {
  String message;

  CartFailure({@required this.message});

  @override
  // TODO: implement props
  List<Object> get props => [message];
}

cart_bloc.dart:

class CartBloc extends Bloc<CartEvent, CartState> {
  MenuItemsRepository menuItemsRepository;

  CartBloc({@required this.menuItemsRepository});

  @override
  // TODO: implement initialState
  CartState get initialState => CartInitial();

  @override
  Stream<CartState> mapEventToState(CartEvent event) async* {
    final currentState = state;

    if (event is FetchOrderSummaryEvent) {
      yield CartLoading();
      try {
        Order order = await menuItemsRepository.getOrder();
        yield CartLoaded(order: order);
      } catch (err) {
        yield CartFailure(message: err.message);
      }
    }
    if (event is AddItemToCartEvent) {
      //yield CartLoading();
      try {
        await menuItemsRepository.addItemToCart(event.slug, event.variations);
      } catch (err) {
        yield CartFailure(message: err.toString());
      }
    }
    if (event is RemoveItemFromCartEvent) {
      yield CartLoading();
      try {
        await menuItemsRepository.removeItemFromCart(
            event.slug, event.variations);
      } catch (err) {
        yield CartFailure(message: err.toString());
      }
    }
    if (event is CheckoutEvent) {
      yield CartLoading();
      try {
        print("token: ${event.stripeToken}");
        await menuItemsRepository.makePayment(
            event.stripeToken, event.selectedAddress);
      } catch (err) {
        yield CartFailure(message: err.toString());
      }
    }
  }
}

repository:

Future<bool> addItemToCart(String slug, List<int> variations) async {
    final token = "Token " + await storage.read(key: utils.TOKEN);

    var params = {
      "slug": slug,
      "variations": variations,
    };
    try {
      Response response = await _dio.post(getAddToCartURL,
          data: jsonEncode(params),
          options: Options(headers: {
            HttpHeaders.authorizationHeader: token,
            HttpHeaders.contentTypeHeader: "application/json",
          }));
      return response.statusCode == 200;
    } catch (error, stackTrace) {
      print("Exception occurred: $error  stackTrace: $stackTrace");
      return false;
    }
  }

Future<Order> getOrder() async {
    final token = "Token " + await storage.read(key: utils.TOKEN);

    var params = {
      "api_key": apiKey,
    };
    try {
      Response response = await _dio.get(getOrderSummaryURL,
          queryParameters: params,
          options: Options(headers: {
            HttpHeaders.authorizationHeader: token,
            HttpHeaders.contentTypeHeader: "application/json",
          }));
      print(response.data);
      return Order.fromJson(response.data);
    } catch (error, stackTrace) {
      print("Exception occurred: $error  stackTrace: $stackTrace");
      return Order.withError(error.toString());
    }
  }

UPDATE: i figured it out. the bloc CartBloc was instantiating 2 times. one at the beginning of the app creation or start in the main.dart file and then another one in a child widget which was the order_summary file. This was was causing to not be able to force the parent widget fetch the data from api. so all the updates was done in the child level not the parent. I hope this would help anyone who might face the same issue. Make sure you instantiate the bloc once only so you can update or receive the updates from the same bloc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM