3

I am working with bottom navigation bar in flutter. I want to refresh every tab when tabs are switched. First I tried to reuse one stateless widget for all the tabs. But it is rerendering pages. My code is as follows:

class _CreateOrderState extends State<CreateOrder> {
  int _currentTabIndex = 0;

  @override
  Widget build(BuildContext context) {
     final _kTabPages = <Widget>[
       FoodCategory(foodCategory: 'nationalFood'),
       FoodCategory(foodCategory: 'fastFood'),
       FoodCategory(foodCategory: 'dessert'),
       FoodCategory(foodCategory: 'drinks'),
     ];

    final _kBottomNavBarItems = <BottomNavigationBarItem>[
      const BottomNavigationBarItem(
        icon: Icon(Icons.fastfood_outlined),
        label: 'Традиционная',
      ),
      const BottomNavigationBarItem(
        icon: Icon(Icons.alarm),
        label: 'Фаст Фуд',
      ),
      const BottomNavigationBarItem(
        icon: Icon(Icons.food_bank_outlined),
        label: 'Дессерты',
      ),
      const BottomNavigationBarItem(
        icon: Icon(Icons.emoji_food_beverage),
        label: 'Напитки',
      ),
    ];
    assert(_kTabPages.length == _kBottomNavBarItems.length);
    final bottomNavBar = BottomNavigationBar(
      items: _kBottomNavBarItems,
      currentIndex: _currentTabIndex,
      type: BottomNavigationBarType.fixed,
      onTap: (int index) {
        setState(() => _currentTabIndex = index);
      },
    );

    return WillPopScope(
      onWillPop: () => _onWillPop(),
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Создание заказа'),
          backgroundColor: Theme.of(context).primaryColor,
          actions: [
            Container(
              child: Stack(
                children: [
                  IconButton(
                    icon: Icon(Icons.shopping_bag_outlined),
                    onPressed: () =>
                        Navigator.pushNamed(context, '/orderReview'),
                    iconSize: 30,
                  ),
                ],
              ),
            )
          ],
        ),
        body: _kTabPages[_currentTabIndex],
        // body: IndexedStack(
        //   index: _currentTabIndex,
        //   children: _kTabPages,
        // ),
        bottomNavigationBar: bottomNavBar,
      ),
    );
  }

This is my stateless widget:


import 'package:counter/blocs/food/food_bloc.dart';
import 'package:counter/data/repository/food_repository.dart';
import 'package:counter/presentation/widgets/Loading.dart';
import 'package:counter/presentation/widgets/MenuItem.dart';
import 'package:counter/presentation/widgets/network_error.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class FoodCategory extends StatelessWidget {
  final String foodCategory;
  FoodCategory({@required this.foodCategory});

  @override
  Widget build(BuildContext context) {
    FoodRepository foodRepository = FoodRepository(category: this.foodCategory);

    return BlocProvider<FoodBloc>(
      create: (BuildContext context) =>
          FoodBloc(foodRepository: foodRepository)..add(FoodLoadEvent()),
      child: Scaffold(
        body: BlocBuilder<FoodBloc, FoodState>(
          builder: (context, state) {
            if (state is FoodInitial) {
              return Text('Initial state');
            }
            if (state is FoodLoadingState) {
              return CustomLoading();
            }
            if (state is FoodLoadedState) {
              return ListView.builder(
                itemBuilder: (BuildContext context, index) {
                  return MenuItem(foodItem: state.loadedFoodItems[index]);
                },
                itemCount: state.loadedFoodItems.length,
              );
            } else {
              return NetworkErrorWidget();
            }
          },
        ),
      ),
    );
  }
}

But when I used different widgets for all the tabs, it has started to work properly and refreshed.

    final _kTabPages = <Widget>[
      NationalFood(foodCategory: 'nationalFood'),
      FastFoodScreen(foodCategory: 'fastFood'),
      DessertsScreen(foodCategory: 'dessert'),
      DrinksScreen(foodCategory: 'drinks'),
    ];
Sardorek Aminjonov
  • 724
  • 2
  • 7
  • 16

3 Answers3

0

Inside the initState of your FoodCategory or NationalFood widget, add the function the retrieves your data.

Huthaifa Muayyad
  • 11,321
  • 3
  • 17
  • 49
0

Since you are placing the _kTabPages and _kBottomNavBarItems initialization within the build() method, these variables are assigned new values every time there's a change in state (when you change tabs). This is why the tabs keep re-rendering.

To stop this, place your initialization within the initState(). Something like this:

import 'package:flutter/material.dart';
import 'package:test_flutter_app/test.dart';

class CreateOrder extends StatefulWidget {
  @override
  _CreateOrderState createState() => _CreateOrderState();
}

class _CreateOrderState extends State<CreateOrder> {
  int _currentTabIndex = 0;
  List<Widget> _kTabPages;
  List<BottomNavigationBarItem> _kBottomNavBarItems;
  BottomNavigationBar bottomNavBar;

  _updateTabs() {
    _kTabPages = <Widget>[
      FoodCategory(key: UniqueKey(), foodCategory: 'nationalFood'),
      FoodCategory(key: UniqueKey(), foodCategory: 'fastFood'),
      FoodCategory(key: UniqueKey(), foodCategory: 'dessert'),
      FoodCategory(key: UniqueKey(), foodCategory: 'drinks'),
    ];

    _kBottomNavBarItems = <BottomNavigationBarItem>[
      const BottomNavigationBarItem(
        icon: Icon(Icons.fastfood_outlined),
        label: 'Традиционная',
      ),
      const BottomNavigationBarItem(
        icon: Icon(Icons.alarm),
        label: 'Фаст Фуд',
      ),
      const BottomNavigationBarItem(
        icon: Icon(Icons.food_bank_outlined),
        label: 'Дессерты',
      ),
      const BottomNavigationBarItem(
        icon: Icon(Icons.emoji_food_beverage),
        label: 'Напитки',
      ),
    ];
    bottomNavBar = BottomNavigationBar(
      items: _kBottomNavBarItems,
      currentIndex: _currentTabIndex,
      type: BottomNavigationBarType.fixed,
      onTap: (int index) {
        setState(() => _currentTabIndex = index);
      },
    );
    assert(_kTabPages.length == _kBottomNavBarItems.length);
  }

  @override
  void initState() {
    _updateTabs();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async => true,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Создание заказа'),
          backgroundColor: Theme.of(context).primaryColor,
        ),
        body: _kTabPages[_currentTabIndex],
        // body: IndexedStack(
        //   index: _currentTabIndex,
        //   children: _kTabPages,
        // ),
        bottomNavigationBar: bottomNavBar,
      ),
    );
  }
}
Bach
  • 2,928
  • 1
  • 6
  • 16
  • Thanks for the answer. But _currentTabIndex is already changing and there is no problem with this. Cuz I have printed the index onTap of a bottom bar. The problem is it is not refreshing my page when I reuse one stateless widget where there are list of items coming from database. But it is working properly when I use different widgets. – Sardorek Aminjonov Apr 05 '21 at 07:47
  • @SardorekAminjonov Try adding a `Key` attribute to each `FoodCategory` widget, then passing a `UniqueKey` in (like in the updated answer) and see if it change – Bach Apr 05 '21 at 13:04
0

I guess this is very late to answer this question. I too faced same kind of problem. Child widgets are not redrawing once the value is changed in parent widget. So I searched for 3 days and finally I found a solution.

  1. Loaded the child widget with Unique Key

  2. Child widgets initialized inside build method.

After this changes, if state changes in parent then the child widget is also getting refreshed.

Thanks