首页 > 解决方案 > 更新同级状态(数据)flutter bloc

问题描述

我正在尝试更新最初已经加载数据的同级小部件中的数据。第二个小部件添加到相同的数据并同时获取它,显示更新的数据。新数据不会反映在第一个小部件中。以购物车和商品数量为例。当我在购物车中添加新商品或更改数量时,它不会反映在购物车中。所有数据都在 django rest api 中远程调用。如果我重新加载应用程序,新数据会显示在购物车中。

这是我要更改其中数据的小部件

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,
        ),
      ),
    );
  }
}

我在这里调用同级小部件中的 API 我发布了从同级小部件按下按钮后调用的处理方法:

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());
      }
    }
  }
}

存储库:

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());
    }
  }

标签: flutter

解决方案


更新:我想通了!集团 CartBloc 实例化了 2 次!一个在应用程序创建开始或在 main.dart 文件中开始,然后另一个在子小部件中,即 order_summary 文件。这导致无法强制父小部件从 api 获取数据。所以所有更新都是在子级而不是父级中完成的。我希望这会帮助任何可能面临同样问题的人。确保您只实例化 bloc 一次,以便您可以更新或接收来自同一个 bloc 的更新。


推荐阅读