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
Related
I am trying to create a music player and I stared the code for navigation bar and some other customizations. but when I run the app it says
"TabBar widgets require a Material widget ancestor, To introduce a Material widget, you can either directly include one, or use a widget that contains Material itself, such as a Card, Dialog, Drawer, or Scaffold.
The specific widget that could not find a Material ancestor was:
TabBar"
I am new to flutter so I don't know much please help.
import 'package:envymusic/views/home.dart';
import 'package:envymusic/views/library.dart';
import 'package:envymusic/views/account.dart';
import 'package:envymusic/views/explore.dart';
class Tabbar extends StatefulWidget {
const Tabbar({Key? key}) : super(key: key);
#override
State<Tabbar> createState() => _TabbarState();
}
class _TabbarState extends State<Tabbar> {
int _selectedTab = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedTab,
onTap: (index) {
setState(() {
_selectedTab = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: "Home",
),
BottomNavigationBarItem(
icon: Icon(Icons.search_outlined),
label: "Explore",
),
BottomNavigationBarItem(
icon: Icon(Icons.library_music),
label: "Library",
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: "Account",
),
],
),
body: Stack(
children: [
renderView(
0,
const HomeView(),
),
renderView(
1,
const ExploreView(),
),
renderView(
2,
const LibraryView(),
),
renderView(
3,
const AccountView(),
),
],
),
),
);
}
Widget renderView(int tabIndex, Widget view) {
return IgnorePointer(
ignoring: _selectedTab != tabIndex,
child: Opacity(
opacity: _selectedTab == tabIndex ? 1 : 0,
child: view,
),
);
}
}
You can Wrap your Tabbar widget with Material widget.
e.g:
Material(
child: Tabbar(),
),
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],
),
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.
I have a bottom navigation bar defined separately in a file, containing 5 items, and loading 5 pages (which doesn't have defined a bottom navigation bar). It should be present on 4 of the loaded pages but should disappear on the 5th page (CHAT). All the solutions I found online refers to hiding the bar while scrolling up or down, I think I'm quite close to the expected result but I didn't sort it out yet... The code for that "controller" that generates the B-N-Bar is below, a screenshot with the bottom as well. Thank you.
class BottomNavigationBarController extends StatefulWidget {
#override
_BottomNavigationBarControllerState createState() =>
_BottomNavigationBarControllerState();
}
class _BottomNavigationBarControllerState
extends State<BottomNavigationBarController> {
final List<Widget> pages = [
MyHome(
key: PageStorageKey('Page1'),
),
MyStats(
key: PageStorageKey('Page2'),
),
MyCategories(
key: PageStorageKey('Page3'),
),
MyPeopleList(
key: PageStorageKey('Page4'),
),
MyChat(
key: PageStorageKey('Page5'),
),
];
final PageStorageBucket bucket = PageStorageBucket();
int _selectedIndex = 0;
Widget _bottomNavigationBar(int selectedIndex) => BottomNavigationBar(
onTap: (int index) => setState(() => _selectedIndex = index),
currentIndex: selectedIndex,
type: BottomNavigationBarType.fixed,
//
iconSize: 24,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text(
'HOME',
),
),
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart),
title: Text(
'STATS',
),
),
BottomNavigationBarItem(
icon: Icon(Icons.view_list),
title: Text(
'INVENTORY',
),
),
BottomNavigationBarItem(
icon: Icon(Icons.group),
title: Text(
'PEOPLE',
),
),
BottomNavigationBarItem(
icon: Icon(Icons.forum),
title: Text(
'CHAT',
),
),
],
);
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: _bottomNavigationBar(_selectedIndex),
body: PageStorage(
child: pages[_selectedIndex],
bucket: bucket,
),
);
}
}
I did it with an IF/Else in the bottom:
#override
Widget build(BuildContext context) {
if (_selectedIndex < 4) {
return Scaffold(
bottomNavigationBar: _bottomNavigationBar(_selectedIndex),
body: PageStorage(
child: pages[_selectedIndex],
bucket: bucket,
),
);
} else {
return Scaffold(
body: PageStorage(
child: pages[_selectedIndex],
bucket: bucket,
),
);
}
}
I work on flutter and I make an app with 5 tabs, using a BottomNavigationBar, who change the currently displayed content.
When I tap on a tab, the content updates to the new content, but the tabs icon doesn't change.
I have tried to change the BottomNavigationBarType but that changes nothing...
here is base page widget:
class Home extends StatefulWidget {
Home({Key key}) : super(key: key);
#override
_Home createState() => _Home();
}
class _Home extends State<Home> {
int _selectedIndex = 0;
static List<Widget> _widgetOptions = <Widget>[
HomePage(),
CreateTrain(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
print(_selectedIndex);
return Scaffold(
body: _widgetOptions.elementAt(_selectedIndex),
bottomNavigationBar: BottomNavigationBar(
currentIndex: 0, // this will be set when a new tab is tapped
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.home),
title: new Text('Home'),
),
BottomNavigationBarItem(
icon: new Icon(Icons.assignment),
title: new Text('Training'),
),
BottomNavigationBarItem(
icon: new Icon(Icons.play_arrow),
title: new Text('start'),
),
BottomNavigationBarItem(
icon: new Icon(Icons.insert_chart),
title: new Text('Stats'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person), title: Text('Profile'))
],
selectedFontSize: 12,
unselectedFontSize: 12,
selectedItemColor: Colors.amber[800],
unselectedItemColor: Colors.grey[500],
showUnselectedLabels: true,
type: BottomNavigationBarType.fixed,
onTap: _onItemTapped,
),
);
}
}
Here is HomePage widget:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
title: const Text(
'Home',
style: TextStyle(
color: Colors.black,
fontSize: 30,
),
),
backgroundColor: _bgColor,
),
body: Text('data'),
);
}
}
Thanks for your help.
In your code
currentIndex: 0, // this will be set when a new tab is tapped
This should be
currentIndex: _selectedIndex, // this will be set when a new tab is tapped