I am really new at flutter. I need a tap on bottomnavigationBar which will open a new page. But where should I use onTap? On Top (body) I have an other Navigation(Tabbarview). Why I dont have an "onTap" inside the BottomnavigationBar
bottomNavigationBar: SnakeNavigationBar(
style: SnakeBarStyle.floating,
backgroundColor: Color(0xffFFFFFF),
snakeColor: Color(0xff3f51b5),
currentIndex: _selectedItemPosition,
onPositionChanged: (index) =>
setState(() => _selectedItemPosition = index),
padding: EdgeInsets.all(4),
items: [
new BottomNavigationBarItem(icon: Icon(Icons.home)),
new BottomNavigationBarItem(icon: Icon(Icons.description)),
new BottomNavigationBarItem(icon: Icon(Icons.people)),
new BottomNavigationBarItem(icon: Icon(Icons.settings)),
new BottomNavigationBarItem(icon: Icon(Icons.battery_unknown)),
],
)));
There's many ways to use a BottomNavigationBar, in your case, that's a library an uses "onPositionChanged" as "onTap" there, not only you can update your variable, but open change the current widget display on screen.
Here a full example to switch between widgets using a BottomNavigationBar:
import 'package:flutter/material.dart';
void main() => runApp(Demo());
class Demo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: DemoApp());
}
}
class DemoApp extends StatefulWidget {
#override
_DemoAppState createState() => _DemoAppState();
}
class _DemoAppState extends State<DemoApp> {
Widget _currentWidget = Container();
var _currentIndex = 0;
Widget _homeScreen() {
return Container(
color: Colors.green,
);
}
Widget _settingsScreen() {
return Container(
color: Colors.red,
);
}
#override
void initState() {
super.initState();
_loadScreen();
}
void _loadScreen() {
switch(_currentIndex) {
case 0: return setState(() => _currentWidget = _homeScreen());
case 1: return setState(() => _currentWidget = _settingsScreen());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _currentWidget,
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() => _currentIndex = index);
_loadScreen();
},
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(icon: Icon(Icons.settings), title: Text('Settings')),
],
),
);
}
}
The result of this is the following app:
Related
I created a separate file for bottom navigation bar and included the three screens which is to be included in bottom navigation bar .
class _bottomnavscreen extends State<bottomnavscreen> {
int _selectedIndex = 0;
List<Widget> pageList = [home(), create(), profile()];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.add_circle_outline_sharp),
label: 'Create',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
body: pageList.elementAt(_selectedIndex),
);
}
I put this bottomnavscreen as the home in main.dart:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: bottomnavscreen(),
);
}
}
But this bootomnavigation widget is seen in my detailedpost screen and comment screen.
detailedpost screen is pushed from home() through Navigation.push()
Comment screen is pushed from postdetails() through
Navigation.push()
How can I hide this bottom navigation widget in my comment screen and detailedpost screen?
This is how I push to detailpost screen from home()
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => detailpost(
body: document['body'],
title: document['title'],
date: document['date'],
)),
);
You can add condition for specific index like this :
class _bottomnavscreen extends State<bottomnavscreen> {
int _selectedIndex = 0;
List<Widget> pageList = [home(), create(), profile()];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: _selectedIndex == 1 ? Container() : BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.add_circle_outline_sharp),
label: 'Create',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
body: pageList.elementAt(_selectedIndex),
);
}
You should start a base page from the MyApp and add BottomNavigations in that page only.
Now when you navigate to detailedpost screen and comment screen, the BottomNavigations will not be visible.
you can use the offstage property to hide the bottom navigation bar on specific pages by wrapping it in an Offstage widget and setting the offstage property to true:
import 'package:flutter/material.dart';
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _currentIndex = 0;
final List<Widget> _pages = [ HomePage(), SettingsPage(), ];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_currentIndex],
bottomNavigationBar: Offstage(
offstage: _currentIndex == 1,
child: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text('Settings'),
),
],
),
),
);
}
}
I have a BottomNavigation bar, and I have Nested navigation for it, but I also need a Scaffold for every new page in the Top level screen. I get that nested Scaffold is not recommended and I also have a resizeToAvoidBottomInset problem when I use nested Scaffolds.
My Tab navigator:
#override
Widget build(BuildContext context) {
Widget child;
if (tabItem == "Home") {
child = const Home();
} else if (tabItem == 'screen2') {
child = Screen2();
} else if (tabItem == 'screen3') {
child = const Screen3();
} else if (tabItem == 'screen4') {
child = const Screen4(0);
} else if (tabItem == 'Screen5') {
child= Screen5();
} else {
child = const Home();
}
return Navigator(
key: navigatorKey,
onGenerateRoute: (routeSettings) {
return MaterialPageRoute(builder: (context) => child);
},
);
}
I dont want to use Cupertino or custom Navigation bars, I want to do it with Material.dart only
I have tried To add a MaterialApp within the Top Level material app, which works, but there has to be a better way
This is my code for doing this
class CustomerHomeScreen extends StatefulWidget {
const CustomerHomeScreen({Key? key}) : super(key: key);
#override
State<CustomerHomeScreen> createState() => _CustomerHomeScreenState();
}
class _CustomerHomeScreenState extends State<CustomerHomeScreen> {
int _selectedIndex=0;
final List _tabs=[
const HomeScreen(), //You can create all these classes with Statefull widgets
const Screen1(),
const Screen2(),
const Screen3(),
const Screen4(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _tabs[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
selectedItemColor: Colors.black,
unselectedItemColor: Colors.red,
currentIndex: _selectedIndex,
// type: BottomNavigationBarType.fixed,
selectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600),
items: [
const BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
const BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Screen1'),
const BottomNavigationBarItem(icon: Icon(Icons.shop), label: 'Screen2'),
BottomNavigationBarItem(icon: Icon(Icons.shopping_cart)), label: 'Screen3'),
const BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Screen4,'),
],
onTap: (index){
setState(() {
_selectedIndex=index;
});
}
),
);
}
}
I am creating a dynamic list of BottomNavigationBarItem and assigning it to 'items' of BottomNavigationBar. So based on a condition (here is my case, checks and add one more BottomNavigationBarItem if it is not billed. So the number of Icons displayed change.
Normally for a fixed number of items, ontap provide index of the icon tapped. As the order/sequence of icons change, their index also differs.
Now how do I read the label of selected BottomNavigationBarItem and respond in onTap handler instead of tapped index value ?
(Of course in this particular case, I can add the additional button as last one and done with. Need a better solution.)
List<BottomNavigationBarItem> getNavbarItems() {
List<BottomNavigationBarItem> navItems = [];
navItems.add(
const BottomNavigationBarItem(
icon: Icon(Icons.delete_outline,), label: 'Delete',),
);
if (widget.invStatus.isBilled == 0) {
navItems.add(
const BottomNavigationBarItem(
icon: Icon(Icons.receipt), label: 'Bill it ?'),
);
}
navItems.add(
const BottomNavigationBarItem(
icon: Icon(Icons.category_rounded), label: 'Add a Product !'),
);
return navItems;
}
Thanks.
If I understand you correctly, you can use the following approach to get the "label".
Call:
getNavbarItems()[index].label
And have a button to dynamically toggle isBilled.
Complete example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Nav(),
),
),
);
}
}
class Nav extends StatefulWidget {
#override
createState() => NavState();
}
class NavState extends State<Nav> {
var _selectedIndex = 0;
var billIt = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Nav'),
),
body: Center(
child: TextButton(
child: Text('Toggle `billIt`'),
onPressed: () {
setState(() {
billIt = !billIt;
});
},
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
items: getNavbarItems(),
onTap: (int index) {
setState(() {
_selectedIndex = index;
print('label: ${getNavbarItems()[index].label}');
});
},
),
);
}
List<BottomNavigationBarItem> getNavbarItems() {
List<BottomNavigationBarItem> navItems = [];
navItems.add(
const BottomNavigationBarItem(
icon: Icon(
Icons.delete_outline,
),
label: 'Delete',
),
);
if (billIt) {
navItems.add(
const BottomNavigationBarItem(
icon: Icon(Icons.receipt), label: 'Bill it ?'),
);
}
navItems.add(
const BottomNavigationBarItem(
icon: Icon(Icons.category_rounded), label: 'Add a Product !'),
);
return navItems;
}
}
I have a BottomNavigationBar that navigates to other pages when pressing an icon. This works fine, except the value of the currentIndex property in BottomNavigationBar doesn't update when the state updates, which means there is no change on the acual BottomNavigationBar. enter image description here
I'm using a vaiable (_selectedPage) to keep track of the selected index in the BottomNavigationBar, and the value changes when tapping an item, but it's not updating the currentIndex property when the state updates.. why is that?
import 'package:flutter/material.dart';
import 'package:independentproject/pages/home_page.dart';
import 'package:independentproject/pages/cook_recipe.dart';
class PageNavigationBar extends StatefulWidget {
#override
_PageNavigationBarState createState() => _PageNavigationBarState();
}
class _PageNavigationBarState extends State<PageNavigationBar> {
//default page showing in bottom navigation bar will be CookRecipe()
int _selectedPage = 1;
//all pages optional in bottom navigation bar
final List<Widget> _pageOptions = [
HomePage(),
CookRecipe(),
];
void onTapped(int pageTapped) {
setState(() {
//print(pageTapped);
_selectedPage = pageTapped;
Navigator.push(context, MaterialPageRoute(builder: (context) => _pageOptions[pageTapped]));
//print(_selectedPage);
});
}
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
//TODO: currentIndex: doesn't update when the state updates, why?
currentIndex: _selectedPage,
//items showing in bottom navigation bar
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Homepage'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search recipe'),
),
],
onTap: (int pageTapped) {
onTapped(pageTapped);
},
);
}
}
import 'package:flutter/material.dart';
import 'package:independentproject/page_navigation_bar.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Home page'),
),
body: Center(
child: Text('Home page'),
),
bottomNavigationBar: PageNavigationBar(),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:independentproject/page_navigation_bar.dart';
class CookRecipe extends StatefulWidget {
#override
_CookRecipeState createState() => _CookRecipeState();
}
class _CookRecipeState extends State<CookRecipe> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Search recipe'),
),
body: Center(
child: Text('Search recipes'),
),
bottomNavigationBar: PageNavigationBar(),
),
);
}
}
I would advise you to create a widget that will contain the BottomNavigationBar and also PageView that would allow you to switch pages with PageController.
For example:
class _MainScreenState extends State<MainScreen> {
PageController _pageController;
int _page = 1;
Duration pageChanging = Duration(milliseconds: 300);//this is for page animation-not necessary
Curve animationCurve = Curves.linear;//this is for page animation-not necessary
_MainScreenState();
#override
void initState() {
super.initState();
_pageController = PageController(initialPage: 1);
}
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
controller: _pageController,
onPageChanged: onPageChanged,
children: <Widget>[
//here are all the pages you need:
//CookRecipe(),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
Icons.message,
),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(
Icons.home,
),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
title: Container(height: 0.0),
),
],
onTap: navigationTapped,
selectedItemColor: Theme.of(context).backgroundColor,
currentIndex: _page,
),
),
);
}
void navigationTapped(int page) {
_pageController.animateToPage(page,duration: pageChanging,
curve: animationCurve,);
}
#override
void dispose() {
super.dispose();
_pageController.dispose();
}
void onPageChanged(int page) {
if (this.mounted){
setState(() {
this._page = page;
});
}}
You can also do this without the PageView,and use only the controller to switch pages.
BTW-you create new instance of the navigation bar when you load a page which is bad practice
it is because PageNavigationBar is a own class, when you call there a setstate only this class updates
take a look at the Provider
a very usefull state management Package
or you can also handle your Page, when the NavBar is your Main Page and you have only one Page
return MaterialApp(
home: Scaffold(
appBar: ownAppBar(_selectedPage),
body: _bodyOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedPage,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Homepage'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search recipe'),
),
],
onTap: (int pageTapped) {
onTapped(pageTapped);
},
)
),
);
final List<Widget> _bodyOptions = [
HomePage(),
CookRecipe(),
];
You don't need to use Navigator to change pages, I modified your code just try.
import 'package:flutter/material.dart';
main() {
runApp(MaterialApp(home: PageNavigationBar()));
}
class PageNavigationBar extends StatefulWidget {
#override
_PageNavigationBarState createState() => _PageNavigationBarState();
}
class _PageNavigationBarState extends State<PageNavigationBar> {
//default page showing in bottom navigation bar will be CookRecipe()
int _selectedPage = 1;
//all pages optional in bottom navigation bar
final List<Widget> _pageOptions = [
HomePage(),
CookRecipe(),
];
void onTapped(int pageTapped) {
setState(() {
//print(pageTapped);
_selectedPage = pageTapped;
// Navigator.push(context, MaterialPageRoute(builder: (context) => _pageOptions[pageTapped]));
//print(_selectedPage);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _pageOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
//TODO: currentIndex: doesn't update when the state updates, why?
currentIndex: _selectedPage,
//items showing in bottom navigation bar
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Homepage'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search recipe'),
),
],
onTap: (int pageTapped) {
onTapped(pageTapped);
},
),
);
}
}
class CookRecipe extends StatefulWidget {
#override
_CookRecipeState createState() => _CookRecipeState();
}
class _CookRecipeState extends State<CookRecipe> {
#override
Widget build(BuildContext context) {
return Center(
child: Text('Search recipes'),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Center(
child: Text('Home page'),
);
}
}
I am developing Flutter application with AppBar and BottomNavigationBar. Here is the code:
class MainPage extends StatefulWidget {
final MainModel model;
MainPage(this.model);
#override
State<StatefulWidget> createState() {
return _MainPageState();
}
}
class _MainPageState extends State<MainPage> {
int _selectedIndex = 0;
Widget _openSelectedPage() {
switch(_selectedIndex) {
case 0:
return HomePage(widget.model);
case 1:
return CapturePage(widget.model);
case 2:
return MealsPage(widget.model);
default:
return HomePage(widget.model);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My App'),
),
body: _openSelectedPage(),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.camera_alt),
title: Text('Capture'),
),
BottomNavigationBarItem(
icon: Icon(Icons.format_list_bulleted),
title: Text('Meals'),
),
],
currentIndex: _selectedIndex,
onTap: (int index) {
setState(() {
_selectedIndex = index;
});
}
),
);
}
}
How can I have custom AppBar on MealsPage? If I add another Scaffold in build method of MealsPage, then it creates two AppBars, which is not what I want. I only want to have one AppBar, the one that I define in MealsPage.
class MainPage extends StatefulWidget {
final MainModel model;
MainPage(this.model);
#override
State<StatefulWidget> createState() {
return _MainPageState();
}
}
class _MainPageState extends State<MainPage> {
int _selectedIndex = 0;
Widget _openSelectedPage() {
switch (_selectedIndex) {
case 0:
return HomePage(widget.model);
case 1:
return CapturePage(widget.model);
case 2:
return MealsPage(widget.model);
default:
return HomePage(widget.model);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _selectedIndex == 2 ? null : AppBar(title: Text('My App')),
body: _openSelectedPage(),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.camera_alt),
title: Text('Capture'),
),
BottomNavigationBarItem(
icon: Icon(Icons.format_list_bulleted),
title: Text('Meals'),
),
],
currentIndex: _selectedIndex,
onTap: (int index) {
setState(() {
_selectedIndex = index;
});
},
),
);
}
}