Flutter selected product becomes unselected after switching pages

Viewed 40

I am building an online bottle store app using flutter and I am having an issue where if I add a product to favorites the selected product's button won't stay selected on the home page if I switch pages. I have categorized the products using a Tabbar and Tabbarview. I have tried using AutomaticKeepAliveClientMxin to keep the page alive but with no success. Please can anyone assist.

Here's what happens: I click on the selected product enter image description here

then it is added to Favorites enter image description here

Come back to the home page and the selected item is no longer showing that it is selected enter image description here

Here's my code:

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
  ProductProvider productProvider = ProductProvider();
  late TabController tabController;

  @override
  void initState() {
    super.initState();
    tabController = TabController(length: 4, vsync: this);
  }

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

  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    var cart = Provider.of<ShoppingCartProvider>(context);
    var favoriteProvider = Provider.of<FavoriteProvider>(context);
    Size _screenSize = MediaQuery.of(context).size;
    final double itemHeight = (_screenSize.height - kToolbarHeight - 24) / 2;
    final double itemWidth = _screenSize.width / 2;
    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Padding(
              padding: EdgeInsets.all(8.0),
              child: Text(
                'Categories',
                style: TextStyle(
                    fontSize: 20.0,
                    fontFamily: 'Montserrat-ExtraBold',
                    fontWeight: FontWeight.bold),
              ),
            ),
            Container(
              child: Align(
                alignment: Alignment.centerLeft,
                child: TabBar(
                  controller: tabController,
                  indicator:
                      CircleTabIndicator(color: Colors.redAccent, radius: 4.0),
                  isScrollable: true,
                  labelColor: Colors.redAccent,
                  labelStyle: const TextStyle(
                      fontWeight: FontWeight.bold, fontSize: 20.0),
                  unselectedLabelColor: Colors.black,
                  unselectedLabelStyle: const TextStyle(
                      fontWeight: FontWeight.bold, fontSize: 20.0),
                  tabs: const [
                    Tab(text: 'Brandy'),
                    Tab(text: 'Gin'),
                    Tab(text: 'Soft drinks'),
                    Tab(text: 'Whiskey')
                  ],
                ),
              ),
            ),
            Container(
              height: 400,
              width: double.maxFinite,
              child: TabBarView(
                  controller: tabController,
                  children: productProvider.categories.map((bottleCategory) {
                    return GridView.builder(
                      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                        crossAxisCount: 2,
                        childAspectRatio: itemWidth / itemHeight,
                      ),
                      itemCount: bottleCategory.bottleList.length,
                      itemBuilder: (context, index) {
                        return Card(
                          shadowColor: Colors.grey,
                          surfaceTintColor: Colors.amber,
                          shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(20)),
                          child: Stack(
                            children: [
                              Positioned(
                                right: 0,
                                child: InkWell(
                                  onTap: () {
                                    favoriteProvider.toggleFavorites(
                                        bottleCategory.bottleList[index]);
                                    if (favoriteProvider.isExist(
                                        bottleCategory.bottleList[index])) {
                                      ScaffoldMessenger.of(context)
                                          .hideCurrentSnackBar();
                                      ScaffoldMessenger.of(context)
                                          .showSnackBar(
                                        const SnackBar(
                                          content: Text(
                                            "Product Added to Favorite!",
                                            style: TextStyle(fontSize: 16),
                                          ),
                                          backgroundColor: Colors.green,
                                          duration: Duration(seconds: 1),
                                        ),
                                      );
                                    } else {
                                      ScaffoldMessenger.of(context)
                                          .hideCurrentSnackBar();
                                      ScaffoldMessenger.of(context)
                                          .showSnackBar(
                                        const SnackBar(
                                          content: Text(
                                            "Product Removed from Favorite!",
                                            style: TextStyle(fontSize: 16),
                                          ),
                                          backgroundColor: Colors.red,
                                          duration: Duration(seconds: 1),
                                        ),
                                      );
                                    }
                                  },
                                  child: favoriteProvider.isExist(
                                          bottleCategory.bottleList[index])
                                      ? const Icon(
                                          Icons.favorite,
                                          color: Colors.redAccent,
                                        )
                                      : const Icon(Icons.favorite_border),
                                ),
                              ),
                              Column(
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  Center(
                                    child: Image.asset(
                                      bottleCategory.bottleList[index].image,
                                      height: 200.0,
                                    ),
                                  ),
                                  Center(
                                      child: Text(
                                          bottleCategory
                                              .bottleList[index].bottleName,
                                          style: const TextStyle(
                                              fontSize: 20.0,
                                              fontWeight: FontWeight.bold))),
                                  Center(
                                    child: Text(
                                        'R${bottleCategory.bottleList[index].price}'),
                                  )
                                ],
                              ),
                              Positioned(
                                  bottom: 0,
                                  right: 10,
                                  child: IconButton(
                                    icon: const Icon(Icons.add_circle),
                                    iconSize: 40.0,
                                    onPressed: () {
                                      cart.addToCart(
                                          bottleCategory.bottleList[index].id,
                                          bottleCategory
                                              .bottleList[index].bottleName,
                                          bottleCategory
                                              .bottleList[index].price,
                                          bottleCategory
                                              .bottleList[index].image);
                                    },
                                  ))
                            ],
                          ),
                        );
                      },
                    );
                  }).toList()),
            ),
          ],
        ),
      ),
    );
  }
}

class FavoriteProvider with ChangeNotifier {
  List<Bottle> _favItems = [];
  List<Bottle> get favItems {
    return [..._favItems];
  }

  void toggleFavorites(Bottle favBottle) {
    final isExist = _favItems.contains(favBottle);
    if (isExist) {
      _favItems.remove(favBottle);
    } else {
      _favItems.add(favBottle);
    }
    notifyListeners();
  }

  bool isExist(Bottle favBottle) {
    final isExist = _favItems.contains(favBottle);
    return isExist;
  }

  void clearFavorite() {
    _favItems = [];
    notifyListeners();
  }
}
1 Answers

Try using Consumer widget. Like so:

  Consumer<favoriteProvider>(
        builder: (BuildContext context, favorite, _){
          return Icon(
            Icons.favorite,
            color: favorite.isExist(bottleCategory.bottleList[index])? Colors.redAccent : null,
          );
        },
      ),

Consumer widget will refresh or change the state whenever the ChangeNotifier of that model, in this case, FavoriteProvider is triggered, this should allows your widget to change and check itself anytime. So you shouldn't need to keep your state or screen alive all the time.

If that doesn't work, please change your Business Logic in the FavoriteProvider. Instead of using contains, I suggest to use any and identifies each instances with its own id or any of its unique variable. Like so:

  bool isExist(Bottle favBottle) {
    final isExist = _favItems.any((e) =>e.bottleName == favBottle.bottleName);
    return isExist;
  }
Related