Flutter ButtomNavigationBarItem add round circle and make it clickable - flutter

so, I am working on my ButtomNavigationBar which should include a rectangle for the icon in the center. I have already archived that. However, now I am facing one problems:
Sadly the shape and icon itself is not clickable. Nothing happens when I click on it (even when I try printing something to the console). It only switches the screen when clicking slightly outside of the shape. For me this seems like a "z-index" problem. Any idea on how I can solve this?
I also have tried to wrap my Container into a GestureDetector but that also does not work..
BottomNavigationBarItem(
icon: GestureDetector(
onTap: () => { onClicked },
child:
Container(
// same code as below
),
),
label: 'Add',
),
This is my complete code:
BottomNavigation
class BottomNavigation extends StatelessWidget {
int selectedIndex;
ValueChanged<int> onClicked;
BottomNavigation({Key? key, required this.selectedIndex, required this.onClicked}) : super(key: key);
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: selectedIndex,
selectedItemColor: AppColors.orange,
onTap: onClicked,
type: BottomNavigationBarType.fixed,
showSelectedLabels: false,
showUnselectedLabels: false,
backgroundColor: AppColors.white,
items: <BottomNavigationBarItem>[
const BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
label: 'Home',
),
const BottomNavigationBarItem(
icon: Icon(CupertinoIcons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Container(
decoration: BoxDecoration(
color: AppColors.orange,
shape: BoxShape.circle,
),
child: Padding(
padding: const EdgeInsets.all(0.0),
child: IconButton(
onPressed: () => { onClicked },
icon: Icon(CupertinoIcons.plus, color: AppColors.white)
)
),
),
label: 'Add',
),
const BottomNavigationBarItem(
icon: Icon(CupertinoIcons.bell),
label: 'Notifications',
),
const BottomNavigationBarItem(
icon: Icon(CupertinoIcons.news),
label: 'Blog',
),
],
);
}
}
Home (where the BottomNavigation gets integrated):
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _selectedIndex = 0;
final screens = [
HomePage(),
SearchPage(),
ProductSubmitPage(),
NotificationPage(),
BlogPage()
];
void onClicked(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: screens[_selectedIndex],
bottomNavigationBar: BottomNavigation(
selectedIndex: _selectedIndex,
onClicked: onClicked,
)
);
}
}
This stack inspired my on how to add a shape to the icon: https://stackoverflow.com/a/67577496/9445999
UPDATE:
Here is my dartpad: https://dartpad.dev/?id=c42f306078c7ece655816482c5c0d413
Kind regards

Your error is on the calling of your function.
You should be doing this like either one of this lines:
//Being 2 the index of this in the list
onPressed: () => onClicked(2),
onPressed: () {onClicked(2);},
I have no experience with this BottomNavigationBarItem so I don't know why it's behaving this way, but that would solve your problem.

Related

How to add sliding transition animation to IndexedStack?

I'm working with IndexedStack and I would like to add a sliding transition animation when the page is changed with the Bottom Navigation Bar (NOT fade animation).
This is an abstract of my code:
class _LoggedHandleState extends State<LoggedHandle> {
int _selectedPage = 1;
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("title"),
),
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.white60,
backgroundColor: Colors.red,
selectedItemColor: Colors.white,
currentIndex: _selectedPage,
onTap: (int index) {
setState(() {
_selectedPage = index;
});
},
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Hello',
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
label: 'Account',
),
]),
body: IndexedStack(
index: _selectedPage,
children: [HelloView(), HomeView(), UserView()],
),
);
}
}
PS: I need to use IndexedStack in order to mantain the state, so I can't use PageBuilder
At the end of the day, IndexedStack is just a Stack of elements that will show the current tab on top. To achieve what you want I'd suggest to do something similar, yet different, like this:
class LoggedHandle extends StatefulWidget {
final _pages = <Widget>[HelloView(), HomeView(), UserView()];
#override
State<StatefulWidget> createState() => _LoggedHandleState();
}
class _LoggedHandleState extends State<LoggedHandle> {
var _selectedPage = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('title'),
),
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.white60,
backgroundColor: Colors.red,
selectedItemColor: Colors.white,
currentIndex: _selectedPage,
onTap: (i) {
setState(() {
_selectedPage = i;
});
},
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Hello',
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
label: 'Account',
),
],
),
body: widget._pages[_selectedPage],
);
}
}
Then, for the animation, there's a Widget for that (AnimatedSwitcher). By exploiting that, along with an AnimatedWidget of your choice (or a custom one), you'll be good to go.
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 650),
transitionBuilder: (child, animation) =>
ScaleTransition(scale: animation, child: child),
child: widget._pages[_selectedPage],
),

separate the code of bottom navigation bar from the scaffold

I just need to separate the code of bottom navigation bar from the scaffold. But the issue is when i click another tab to switch to another page, there is a 7 seconds delay.
The code of scaffold Is:
Scaffold(
extendBody: true,
body: IndexedStack(
index: HomeScreen.pageIndex,
children: [
HomePage(isMember: widget.isMember),
(widget.isMember)
? const MyOpportunityPage()
: const AddOpportunity(),
ProfilePage(isMember: widget.isMember),
],
),
bottomNavigationBar: BottomNavBar(
isMember: widget.isMember,
),
);
The Bottom Navigation Bar code is:
class BottomNavBar extends StatelessWidget {
BottomNavBar({
Key? key,
required this.isMember,
}) : super(key: key);
bool isMember;
#override
Widget build(BuildContext context) {
return StatefulBuilder(
builder: (context, StateSetter setState) {
return BottomNavigationBar(
currentIndex: HomeScreen.pageIndex,
onTap: (int value) => setState(() {
HomeScreen.pageIndex = value;
}),
type: BottomNavigationBarType.fixed,
elevation: 2,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'home'.tr,
),
BottomNavigationBarItem(
icon: Icon(Icons.flag_sharp),
label:isMember
? 'my_opportunities'.tr
: "add_opportunity".tr,
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'profile'.tr,
),
],
);
}
);
}
}
I'm not sure why you are having a 7-second delay, especially when you did not specify one, maybe you should first close your program and probably uninstall it and reboot your IDE and try again, and if that is still the case, you can refactor your code like this;
Scaffold(
body: IndexedStack(
index: currentIndex,
children: [
Center(child: MyHomeWidget()),
Center(child: AnotherWidget()),
Center(child: SomeOtherWidget()),
],
),
bottomNavigationBar: BottomNavBar(
isMember: isMember,
currentI: currentIndex,
onPress: (int value) => setState(() {
currentIndex = value;
}),
),
),
class BottomNavBar extends StatelessWidget {
const BottomNavBar({
Key? key,
required this.isMember,
required this.onPress,
this.currentI = 0,
}) : super(key: key);
final bool isMember;
final Function(int) onPress;
final int currentI;
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: currentI,
onTap: onPress,
type: BottomNavigationBarType.fixed,
elevation: 2,
items: [
const BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'home',
),
BottomNavigationBarItem(
icon: const Icon(Icons.flag_sharp),
label: isMember ? 'my_opportunities' : "add_opportunity",
),
const BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'profile',
),
],
);
}
}
Hopefully there would be a difference. Just implement your own index and any other vital part of the code. I tested this on dartpad and it worked fine, here is a link

Why there is no index in navigation bar item?

This is the code without error messages:
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => BottomNavigation();
}
/// This is the private State class that goes with MyStatefulWidget.
class BottomNavigation extends State<MyStatefulWidget> {
int _selectedIndex = 0;
List<Widget> _widgetOptions= <Widget>[
Text('Home'),
Text('Message')
];
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
void _onItemTapped(int index) {
setState(() {
_selectedIndex=index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(icon: Icon(Icons.search), onPressed: () {}),
],
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image:
AssetImage("assets/OptimizeYourFood/OptimizeYourFood2.png"),
fit: BoxFit.cover,
),
color: Colors.transparent)
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.local_dining),
label: 'Reduce',
),
BottomNavigationBarItem(
icon: Icon(FontAwesomeIcons.recycle),
label: 'Reuse and recycle',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
[Nothing comes out of bottom navigation bar items][1]
..................................................................................................................................................................................................This is the end. This is the end. This is the end. This is the end. This is the end.
New Code
New Picture
You are changing the index but not the UI. use _widgetOptions on body. You can directly use body:_widgetOptions[_selectedIndex ] .
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => BottomNavigation();
}
/// This is the private State class that goes with MyStatefulWidget.
class BottomNavigation extends State<MyStatefulWidget> {
int _selectedIndex = 0;
List<Widget> _widgetOptions = <Widget>[Text('Home'), Text('Message')];
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
void _onItemTapped(int index) {
print("tapped $index");
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(icon: Icon(Icons.search), onPressed: () {}),
],
),
body: Column(
children: [
_widgetOptions[_selectedIndex]
// Container(
// decoration: BoxDecoration(
// image: DecorationImage(
// image:
// AssetImage("assets/OptimizeYourFood/OptimizeYourFood2.png"),
// fit: BoxFit.cover,
// ),
// color: Colors.transparent)),
],
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.local_dining),
label: 'Reduce',
),
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
// Icon(FontAwesomeIcons.recycle),
label: 'Reuse and recycle',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
It will use the items array index.
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem( // <- index 0
icon: Icon(Icons.local_dining),
label: 'Reduce',
),
BottomNavigationBarItem( // <- index 1
icon: Icon(FontAwesomeIcons.recycle),
label: 'Reuse and recycle',
),
...
],

Flutter BottomNavigationBar: why selected item shifts other icons?

I'm new to flutter and I created a BottomNavigationBar and add into it some BottomNavigationBarItem that contains flutter icons.
The problem is that, when I select one item of my BottomNavigationBar, this item shifts other icons.
this is a screenshot of my app:
is there a way to block this shift ?
my code:
import 'package:flutter/material.dart';
class Navbar extends StatefulWidget {
const Navbar({ Key key }) : super(key: key);
#override
_NavbarState createState() => _NavbarState();
}
class _NavbarState extends State<Navbar> {
int index = 0;
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
iconSize: 30,
showSelectedLabels: false,
showUnselectedLabels: false,
selectedItemColor: Colors.blueGrey,
unselectedItemColor: Colors.black,
currentIndex: index,
onTap: (int selectedIndex) {
setState(() {
index = selectedIndex;
});
},
items: [
BottomNavigationBarItem(
icon: new Icon(
Icons.home,
),
title: new Text('Home'),
),
BottomNavigationBarItem(
icon: new Icon(
Icons.search,
),
title: new Text('Search'),
),
BottomNavigationBarItem(
icon: Icon(
Icons.add,
),
title: Text('Add')
),
BottomNavigationBarItem(
icon: Icon(
Icons.favorite,
),
title: Text('Add')
),
BottomNavigationBarItem(
icon: Icon(
Icons.account_circle,
),
title: Text('Account')
)
],
);
}
}
You can change this behavior of the BottomNavigationBar by setting its type parameter to BottomNavigationBarType.fixed when constructing it.
BottomNavigationBar(
...
type: BottomNavigationBarType.fixed,
...
}
According to the documentation the default type is fixed if there are four or less items and shifting if there are more.

Set color on active item in a BottomNavigationBarType.fixed

I have a fixed BottomNavigationBar with 4 items as described below.
How can I set the color of the selected item?
I've tried with: fixedColor and selectedItemColor but it would't work.
BottomNavigationBar(
type: BottomNavigationBarType.fixed,
fixedColor: Colors.blue,
//selectedItemColor: Colors.blue,
currentIndex: snapshot.data.index,
onTap: _bottomNavBarBloc.pickItem,
items: [
BottomNavigationBarItem(
icon: Icon(
Icons.access_time,
color: Colors.black,
),
),
BottomNavigationBarItem(...),
BottomNavigationBarItem(...),
BottomNavigationBarItem(...),
]);
You can provide a different icon for Active and Normal Bottom Navigation Item.
BottomNavigationBarItem _getNavigationItem(IconData icon, String title) {
return BottomNavigationBarItem(
backgroundColor: Colors.white,
activeIcon: Icon(
icon,
color: Colors.teal,
),
icon: Icon(icon, color: Colors.grey),
title: Text(title),
);
}
There are two types available. fixed or shifting. If we add fixed type , the buttons inside bottom navigation does not show any effect when use click a particular button. It just keep fixed to the bottom navigation. If we add shifting , it will show some kind of cool animation when we click a particular button.
Check this out here
A workaround perhaps would be something like this though:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new MyHomePage(),
));
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentIndex = 0;
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("StackoverFlow"),
),
body: Container(),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: currentIndex,
onTap: (index) {
setState(() {
currentIndex = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(
Icons.access_time,
color: currentIndex == 0? Colors.blue:Colors.black,
),
title: Container(),
),
BottomNavigationBarItem(
icon: Icon(
Icons.access_time,
color: currentIndex == 1? Colors.blue:Colors.black,
),
title: Container(),
),
]),
);
}
}