Hi I'm new to Flutter and I'm trying to add BottomNavigationBar to my application, but I got a problem as I wrote in title.
As you can see below, I'm placing a state of currentIndex and a method called when tapped in a different class.
page_frame.dart (This has a state and a method)
// ...
class PageFrame extends StatefulWidget {
const PageFrame({super.key});
@override
State<PageFrame> createState() => _PageFrameState();
}
class _PageFrameState extends State<PageFrame> {
int _currentPageIndex = 0;
final List<Widget> _pages = <Widget>[
const Home(),
const Announcement()
];
void _onPageTap(int index) {
setState(() {
_currentPageIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor : const Color(0xFF183977),
body : _pages.elementAt(_currentPageIndex),
bottomNavigationBar : SPBottomNavigationBar(
currentIndex : _currentPageIndex,
onTap : _onPageTap
),
);
}
}
my_bottom_navigation_var.dart (This receives a state and a method from above)
// ...
class MyBottomNavigationBar extends StatefulWidget {
final int currentIndex;
final ValueChanged<int> onTap;
const MyBottomNavigationBar({required this.currentIndex, required this.onTap, Key? key})
: super(key: key);
@override
State<MyBottomNavigationBar> createState() => _MyBottomNavigationBarState();
}
class _MyBottomNavigationBarState extends State<MyBottomNavigationBar> {
late int currentIndex;
late ValueChanged<int> onTap;
@override
void initState() {
super.initState();
currentIndex = widget.currentIndex;
onTap = widget.onTap;
}
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
unselectedItemColor : const Color(0xFFFFFFFF),
selectedItemColor : const Color(0xFF008BF1),
showUnselectedLabels: true,
items : const [
BottomNavigationBarItem(
icon : Icon(Icons.home),
label : 'Home',
backgroundColor : Color(0xFF000000)
),
BottomNavigationBarItem(
icon : Icon(Icons.announcement),
label : 'Announcement',
backgroundColor : Color(0xFF000000)
)
],
currentIndex : currentIndex,
onTap : onTap,
);
}
}
This results in if I tap an icon of Announcement, only the screen changes from Home to Announcement, but what icon of selected is still Home like this: Screen : Home, Selection : Home Screen : Announcement, Selection : Home I thought this could happens because of lack of interactivity, even if it were, I have no idea how to get this interactive. Please tell me what to do. Thanks.
CodePudding user response:
Your MyBottomNavigationBar
state class has its life-cycle, It gets currentIndex
value for the 1st time on initState
. You can directly access the widget class variable with
widget.variableName
You can use like
class MyBottomNavigationBar extends StatefulWidget {
final int currentIndex;
final ValueChanged<int> onTap;
const MyBottomNavigationBar(
{required this.currentIndex, required this.onTap, Key? key})
: super(key: key);
@override
State<MyBottomNavigationBar> createState() => _MyBottomNavigationBarState();
}
class _MyBottomNavigationBarState extends State<MyBottomNavigationBar> {
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
unselectedItemColor: const Color(0xFFFFFFFF),
selectedItemColor: const Color(0xFF008BF1),
showUnselectedLabels: true,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Color(0xFF000000)),
BottomNavigationBarItem(
icon: Icon(Icons.announcement),
label: 'Announcement',
backgroundColor: Color(0xFF000000))
],
currentIndex: widget.currentIndex,
onTap: widget.onTap,
);
}
}
Also you can just use stateless widget here, the parent(home) widget pass new value on index changes and this widget get rebuild automatically.
class MyBottomNavigationBar extends StatelessWidget {
final int currentIndex;
final ValueChanged<int> onTap;
const MyBottomNavigationBar(
{required this.currentIndex, required this.onTap, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
unselectedItemColor: const Color(0xFFFFFFFF),
selectedItemColor: const Color(0xFF008BF1),
showUnselectedLabels: true,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Color(0xFF000000)),
BottomNavigationBarItem(
icon: Icon(Icons.announcement),
label: 'Announcement',
backgroundColor: Color(0xFF000000))
],
currentIndex: currentIndex,
onTap: onTap,
);
}
}
CodePudding user response:
The reason your main screen is updating while the NavigationBar
is not is simply because they have two different states.
In your main screen, you have a variable with the current page index and when the user taps the NavigationBar
you update this and redraw the widget. This will not redraw the child widget, which has its own state that hasn't changed.
There are two simple fixes for this.
NavigationBar as StatelessWidget
Since the Navbar never uses its state, you could convert it into a StatelessWidget
and pass the active index in the constructor. That way, when the main screen changes it will rebuild the NavigationBar
with the new index.
class MyBottomNavigationBar extends StatelessWidget {
final int currentIndex;
final ValueChanged<int> onTap;
const MyBottomNavigationBar(
{Key? key, required this.currentIndex, required this.onTap})
: super(key: key);
// ...
NavigationBar as StatefulWidget
The other way to fix this is to keep the NavigationBar
as a StatefulWidget
but when the user taps you update the state in here and also pass up the selected index. Just change the onTap()
as follows:
Instead of onTap: onTap
you will write
onTap: (index) {
setState((){
currentIndex = index;
});
widget.onTap(index);
}
As you can see, you don't even need to create a state variable for the callback function inside the initState()
. Just call it here.