Reputation: 834
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'),
];
Upvotes: 4
Views: 7248
Reputation: 1
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.
Loaded the child widget with Unique Key
Child widgets initialized inside build method.
After this changes, if state changes in parent then the child widget is also getting refreshed.
Thanks
Upvotes: 0
Reputation: 3326
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,
),
);
}
}
Upvotes: 0
Reputation: 12373
Inside the initState
of your FoodCategory
or NationalFood
widget, add the function the retrieves your data.
Upvotes: 0