I just started flutter and trying to build a bottom navigation bar that navigates between 4 pages:
'.../pages/home.dart';
'.../pages/history.dart';
'.../pages/search.dart';
'.../pages/bookmarks.dart';
The home page should be always on display as the main screen when starting the app. (obviously !!)
I build the navigation bar following some documentations.
The navigation bar seems to work with no trouble but the problem is
I have no idea of where and how to implement the rest of the navigation and tab switching logic
this is my main_screen.dart
class _MainScreenState extends State<MainScreen> {
PageController _pageController;
int _page = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _pageController,
onPageChanged: onPageChanged,
children: List.generate(4, (index) => Home()),
),
bottomNavigationBar: Theme(
data: Theme.of(context).copyWith(
canvasColor: Theme.of(context).primaryColor,
primaryColor: Theme.of(context).accentColor,
textTheme: Theme.of(context).textTheme.copyWith(caption: TextStyle(color: Colors.grey[400]),),
),
child: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
Feather.getIconData("home"),
),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(
Feather.getIconData("file"),
),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(
Feather.getIconData("search"),
),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(
Feather.getIconData("bookmark"),
),
title: Container(height: 0.0),
),
],
onTap: navigationTapped,
currentIndex: _page,
),
),
);
}
void navigationTapped(int page){
_pageController.jumpToPage(page);
}
#override
void initState(){
super.initState();
_pageController = PageController(initialPage: 0);
}
#override
void dispose() {
super.dispose();
_pageController.dispose();
}
void onPageChanged(int page){
setState(() {
this._page = page;
});
}
}
So in your PageView you list children These are the pages that should correspond to your tabs.
At the moment you have your Home widget listed 4 times, which will obviously not show any difference when you click the tabs.
if you replace make your children like this it should work fine
children: [Home(), History(), Search(), Bookmarks()]
Related
I have this design where when we open the app, it first goes to the Home Screen by default and I have two Bottom Navigation Bar Items.
I am unable to get it working because I want it to behave how Bottom Navigation Bar works but I want default screen to be the home. And when you click on Cart or Profile, it should highlight that tab but when you click on home, it should remove highlighting from it.
Here is a simple workaround or try search for bottomnavigation packages
check this persistent_bottom_nav_bar pacakge it allows to show bottom navigation in all pages with navigator,In other packages you have to do it yourself all the things.
if you want use persistent_bottom_nav_bar check this example for persistent_bottom_nav_bar
Top 16 Flutter Navigation Libraries
flutter-bottomappbar-navigation-with-fab
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: Text('Title')),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: () {}),
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
child: Container(
height: 56,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(icon: Icon(Icons.home), onPressed: () {}),
IconButton(icon: Icon(Icons.search), onPressed: () {}),
SizedBox(width: 40), // The dummy child
IconButton(icon: Icon(Icons.notifications), onPressed: () {}),
IconButton(icon: Icon(Icons.message), onPressed: () {}),
],
),
)),
),
);
}
}
you can try with this persistent_bottom_nav_bar
Here is the Code for Bottom TabBar in Flutter
class TabView extends StatefulWidget {
const TabView({Key? key}) : super(key: key);
#override
_TabViewState createState() => _TabViewState();
}
class _TabViewState extends State<TabView> with SingleTickerProviderStateMixin {
late TabController tabController;
#override
void initState() {
super.initState();
// TODO: CHANGE LENGTH
tabController = TabController(length: 2, vsync: this);
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: Container(
height: 50,
child: TabBar(
unselectedLabelColor: Colors.grey,
labelColor: kPrimaryColor,
indicatorColor: kPrimaryColor,
labelStyle: TextStyle(fontSize: 11, fontWeight: FontWeight.w500),
tabs: [
Tab(
icon: Icon(Icons.home, size: 25.0,),
text: "Tab1",
iconMargin: EdgeInsets.zero,
),
Tab(
icon: Icon(Icons.person, size: 25.0),
text: "Tab2",
iconMargin: EdgeInsets.zero,
),
],
controller: tabController,
),
),
body: TabBarView(
controller: tabController,
children: [
Tab1(),
Tab2()
],
),
);
}
}
I have a bottom navigation bar, that lets me navigate between pages, while keeping the Bottom Navigation bar in place (Using Persistent Bottom Navigation bar package)
I also want to have a extra navigation button, that sends me to another page not listed on the Bottom Navigation bar, but all the different ways I have tried, it pushes me to another page, that is not inside the wrapper.
How could I navigate to another page from AppBar (Page is not listed on the bottom navigation bar) without losing the Navigation bar?
Attatching wrapper code
class Wrapper extends StatefulWidget {
final BuildContext menuScreenContext;
Wrapper({Key key, this.menuScreenContext}) : super(key: key);
#override
_WrapperState createState() => _WrapperState();
}
class _WrapperState extends State<Wrapper> {
final AuthService _auth = AuthService();
PersistentTabController _controller;
bool _hideNavBar;
#override
void initState() {
super.initState();
_controller = PersistentTabController(initialIndex: 0);
_hideNavBar = false;
}
List<Widget> _buildScreens() {
return [
HomePage(
hideStatus:_hideNavBar,
),
Page1(),
Page2(),
Page3(),
Page4(
hideStatus:_hideNavBar,
),
];
}
List<PersistentBottomNavBarItem> _navBarsItems() {
return [
PersistentBottomNavBarItem(
icon: Icon(Icons.home),
title: "Home",
activeColor: Colors.blue,
inactiveColor: Colors.grey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.search),
title: ("Search"),
activeColor: Colors.teal,
inactiveColor: Colors.grey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.add),
title: ("Add"),
activeColor: Colors.deepOrange,
inactiveColor: Colors.grey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.settings),
title: ("Settings"),
activeColor: Colors.indigo,
inactiveColor: Colors.grey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.settings),
title: ("Settings"),
activeColor: Colors.indigo,
inactiveColor: Colors.grey,
),
];
}
#override
Widget build(BuildContext context)
{
final user = Provider.of<NUser>(context);
if(user==null){
return Authenticate();}
else {
return Scaffold
(
drawer: Drawer(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>
[
TextButton
(child:Text('hey'), onPressed: ()
{
pushNewScreenWithRouteSettings(
context,
settings: RouteSettings(name:'/home'),
screen: HomePage());
}
),
ElevatedButton.icon(
onPressed: () async {await _auth.signOut();},
icon: Icon(Icons.person),
label: Text('Logout'),
),
],
),
),
),
appBar: AppBar(
actions: [
IconButton(iconSize: 150,icon: Image.asset("assets/BUTTON.png", color: Colors.black,height: 1000,width: 1000,), onPressed: ()
{
Navigator.push(context, MaterialPageRoute(builder: (context) => Profile()));
}),
ButtonTheme(
minWidth: 100.0,
height: 100.0,
child: TextButton(
onPressed: () {},
child: Text(" 4444 "),
),
),
],
),
body: PersistentTabView.custom
(
context,
controller: _controller,
screens: _buildScreens(),
confineInSafeArea: true,
itemCount: 5,
handleAndroidBackButtonPress: true,
resizeToAvoidBottomInset: false,
stateManagement: true,
hideNavigationBar: _hideNavBar,
screenTransitionAnimation: ScreenTransitionAnimation(
animateTabTransition: true,
curve: Curves.ease,
duration: Duration(milliseconds: 200),
),
customWidget: CustomNavBarWidget
(
items: _navBarsItems(),
onItemSelected: (index) {
setState(() {
_controller.index = index; // THIS IS CRITICAL!! Don't miss it!
});
},
selectedIndex: _controller.index,
),
),
);
}
}
}
class Profile extends StatefulWidget {
Profile({Key key}): super(key: key);
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title:Text('sample'),
),
);
}
}
I tried creating a class for the page in wrapper, but no luck. Other pages are individual files. I am trying to navigate with the AppBar Button
I want to get a bottom navigation bar, but the Tabs should be text-only. The problem is, that icon is a required property of BottomNavigationBarItem().
Edit: I got it working using a tab bar as bottom nav bar, but #Fernando Rocha 's solution seems to work less tricky and works better. To sum it up, simply add "size: 0" to each icon (you will still need an icon).
I used size 0 at icon size and it worked
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home, size: 0),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business, size: 0),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school, size: 0),
title: Text('School'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
You can do
BottomNavigationBarItem(
icon: Icon(null),
title: Text('Just Text'),
)
to achieve this.
With this approach there will still be an empty space where the Icon is "supposed" to go. With #Fernando Rocha 's approach it looks like the text is centered.
I used tabs instead :
static const List<Tab> _tabs = [
Tab(text: "A"),
Tab(text: "AA"),
Tab(text: "AAA")
];
return WillPopScope(
child: DefaultTabController(
length: _tabs.length,
child: Scaffold(
bottomNavigationBar: Container(
// color: Color(0xFF3F5AA6),
margin: const EdgeInsets.only(bottom: 11),
child: TabBar(
// labelColor: Colors.white,
// unselectedLabelColor: Colors.white60,
// indicatorSize: TabBarIndicatorSize.tab,
indicatorPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 23),
indicatorColor: Colors.white,
onTap: (int index) {
setState(() {
_selectedIndex = index;
});
},
tabs: _tabs,
),
),
body: Center(
child: _pages[_selectedIndex]
),
),
),
onWillPop: () async {
return Navigator.canPop(context);
}
);
this is my second thread to this Topic, this time I will ad a picture and the full code.
The nav bar is working but I want an indicator on which is pressed (the text under the icon is already scaling up a bit but thats not enough)
I want hat the segment which is pressed has a more light background then the others.
How is that possible? I'm really new to Flutter and currently this is the only programming language which is complete confusing for me. screenshot
In the code, I just have included an icon font (font awesome) and 3 pages which the nav bar directs to. (neu, beliebt, profil)
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'Neu.dart';
import 'Beliebt.dart';
import 'Profil.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
int _selectedTab = 0;
final _pageOptions = [
NeuPage(),
BeliebtPage(),
ProfilPage(),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.deepOrangeAccent,
primaryTextTheme: TextTheme(
title: TextStyle(color: Colors.white),
)),
home: Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/logo_straight.png',
fit: BoxFit.contain,
height: 32,
),
],
),
),
body: _pageOptions[_selectedTab],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedTab,
backgroundColor: Colors.deepOrangeAccent,
onTap: (int index) {
setState(() {
_selectedTab = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(FontAwesomeIcons.quoteRight, color: Colors.white),
title: Text('Neu', style: TextStyle(color: Colors.white),
)
),
BottomNavigationBarItem(
icon: Icon(Icons.whatshot, color: Colors.white),
title: Text('Beliebt', style: TextStyle(color: Colors.white),
)
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle, color: Colors.white),
title: Text('Profil', style: TextStyle(color: Colors.white),
)
),
],
),
),
);
}
}
Possible Solutions
Build your own Widget using row and columns
Flutter is an open soruse project , edit the original Widget and submit it or use it your self
here original Widget code
Paint over the Widget
Use a Container , Stack and the bottomNavigationBar size to move the Container
this is what i used here
Screen Record
Step 1
inside MyAppState add GlobalKey variable
GlobalKey _bottomNavigationBarKey = GlobalKey();
Step 2
assign the GlobalKey to the BottomNavigationBar
BottomNavigationBar(
key: _bottomNavigationBarKey,
...)
Step 3
inside MyAppState add _bottomNavigationBarSize variable
Size _bottomNavigationBarSize = Size(0, 0);
Step 4
inside MyAppState add _getbottomNavigationBarSize method to ask the framework for the bottomNavigationBar Size
_getbottomNavigationBarSize() {
final RenderBox bottomNavigationBarRenderBox =
_bottomNavigationBarKey.currentContext.findRenderObject();
final bottomNavigationBarSize = bottomNavigationBarRenderBox.size;
setState(() {
_bottomNavigationBarSize = bottomNavigationBarSize;
});
}
Step 5
inside initState at addPostFrameCallback call _getbottomNavigationBarSize method to tell the framework to calculate the size after the frame drawing is done
#override
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) => _getbottomNavigationBarSize());
}
Step 6
warp the bottomNavigationBar Widget in a Stack Widget
bottomNavigationBar:
Stack
(
children: <Widget>
[
BottomNavigationBar(.....),
],
)
Step 7
add an Positioned Widget after BottomNavigationBar
bottomNavigationBar:
Stack
(
children: <Widget>
[
BottomNavigationBar(.....),
Positioned(.....),
],
)
Step 8
set the Positioned Widget left property
item width = *bottomNavigationBar width dividend by the pages count
1st item offset = 0 * item width = 0
2st item end = 1 * item width = item width
2st item end = 2 *item width = 2 item width
container offset = item width multiplied by the _selectedTab index
Positioned(
left: (_bottomNavigationBarSize.width / _pageOptions.length) * _selectedTab,
),
Step 9
add an Positioned Widget after BottomNavigationBar
Positioned
(
...,
child: Container(.... ),
)
Step 10
in the Container set the height property to bottomNavigationBar height
Container(
height: _bottomNavigationBarSize.height,
....),
Step 10
in the Container set the width property to bottomNavigationBar width divided by the pages count
child: Container(
width: _bottomNavigationBarSize.width / _pageOptions.length,
....),
Step 11
in the Container set the color property to Black with 26% opacity.
child: Container(
....,
color: Colors.black26)
Full Code
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
GlobalKey _bottomNavigationBarKey = GlobalKey();
Size _bottomNavigationBarSize = Size(0, 0);
_getbottomNavigationBarSize() {
final RenderBox bottomNavigationBarRenderBox =
_bottomNavigationBarKey.currentContext.findRenderObject();
final bottomNavigationBarSize = bottomNavigationBarRenderBox.size;
setState(() {
_bottomNavigationBarSize = bottomNavigationBarSize;
});
}
#override
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) => _getbottomNavigationBarSize());
}
int _selectedTab = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
final _pageOptions = [
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.deepOrangeAccent,
primaryTextTheme: TextTheme(
title: TextStyle(color: Colors.white),
)),
home: Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Image"),
],
),
),
body: _pageOptions[_selectedTab],
bottomNavigationBar: Stack(
children: <Widget>[
BottomNavigationBar(
key: _bottomNavigationBarKey,
currentIndex: _selectedTab,
backgroundColor: Colors.deepOrangeAccent,
onTap: (int index) {
setState(() {
_selectedTab = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit, color: Colors.white),
title: Text(
'Neu',
style: TextStyle(color: Colors.white),
),
),
BottomNavigationBarItem(
icon: Icon(Icons.whatshot, color: Colors.white),
title: Text(
'Beliebt',
style: TextStyle(color: Colors.white),
)),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle, color: Colors.white),
title: Text(
'Profil',
style: TextStyle(color: Colors.white),
)),
],
),
Positioned(
left: (_bottomNavigationBarSize.width / _pageOptions.length) *
_selectedTab,
child: Container(
height: _bottomNavigationBarSize.height,
width: _bottomNavigationBarSize.width / _pageOptions.length,
color: Colors.black26),
),
],
),
),
);
}
}
Ref
Get size and position of a widget in Flutter - Coflutter
check this answer I think that might help you , basically it says that you should wrap you nav bar inside material widget (not MatrialApp widget) and override the theme and specify another for your nav bar.
I am using bottom app bar for bottomnavigation in flutter. when tapped on one of the tab in the bottom app bar, i would still want the bottom app bar and app bar to remain as it is in it's fixed position but only the body content changes based on what is tapped.
I have tried to use push() method but it gives me a new page instead with a back button.
Navigation_tabs.dart:
import 'package:flutter/material.dart';
class NavigationTabs extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {},
),
appBar: AppBar(
title: Text('Dashboard'),
),
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
notchMargin: 4.0,
child: new Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(
Icons.home,
color: Colors.cyan[700],
),
onPressed: () {},
),
new Container(
padding: EdgeInsets.only(left: 20),
child: IconButton(
icon: Icon(
Icons.list,
color: Colors.cyan[700],
),
onPressed: () => Navigator.pushNamed(context, '/login'),
)),
new Container(
padding: EdgeInsets.only(left: 120),
child: IconButton(
icon: Icon(
Icons.explore,
color: Colors.cyan[700],
),
onPressed: () {},
)),
new Container(
height: 22.0,
child: new RawMaterialButton(
onPressed: () {},
child: new Icon(
Icons.person,
color: Colors.white,
size: 20.0,
),
shape: new CircleBorder(),
elevation: 1.0,
fillColor: Colors.cyan[700],
))
],
),
));
}
}
I want to be able to only make the page content change without a back button instead of going to a completely new page when one of the tabs is pressed.
You can use a BottomNavigationBarItem instead of creating buttons and use the ontap of the bottomNavigationBar.
class _MyHomePageState extends State<MyHomePage> {
int _index = 0;
final List<Widget> _children = [
Center(child: Text("First Page"),),
Center(child: Text("Second Page"),),
Center(child: Text("Third Page"),)
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Bottom Navigation"),
),
body: Center(
child: (_children[_index ]),
),
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.looks_one),
title: Text('First'),
),
BottomNavigationBarItem(
icon: Icon(Icons.looks_two),
title: Text('Second'),
),
BottomNavigationBarItem(
icon: Icon(Icons.looks_3),
title: Text('Third'),
)
],
),
);
}
void onTabTapped(int index) {
setState(() {
_index = index;
});
}
}
For more detailed explanation:
Flutter documentation