I am a beginner with flutter and dart. I have been trying to implement a navigationBar on three different pages in my app. The toggling works well for an individual page but I have problems persisting the active and inactive tabs state on all the pages. It seems like when it navigates to another page, I lose the active state too the tabs. This is my code.
AppFooter.dart
import 'package:flutter/material.dart';
class AppFooter extends StatefulWidget {
#override
_AppFooterState createState() => _AppFooterState();
}
class _AppFooterState extends State<AppFooter> {
int index = 0;
#override
Widget build(BuildContext context) {
return new Theme(
data: Theme.of(context).copyWith(
// sets the background color of the `BottomNavigationBar`
canvasColor: Colors.white,
// sets the active color of the `BottomNavigationBar` if `Brightness` is light
primaryColor: Colors.green,
textTheme: Theme.of(context)
.textTheme
.copyWith(caption: new TextStyle(color: Colors.grey))),
child: new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: index,
onTap: (int index) {
setState(() {
this.index = index;
});
switch (index){
case 0: Navigator.of(context).pushNamed('/dashboard');
break;
case 1: Navigator.of(context).pushNamed('/medical centre');
break;
case 2: Navigator.of(context).pushNamed('/history');
break;
}
},
items: [
new BottomNavigationBarItem(
backgroundColor: Colors.white,
icon: index==0?new Image.asset('assets/images/dashboard_active.png'):new Image.asset('assets/images/dashboard_inactive.png'),
title: new Text('Dashboard', style: new TextStyle(fontSize: 12.0))),
new BottomNavigationBarItem(
backgroundColor: Colors.white,
icon: index==1?new Image.asset('assets/images/medical_sevice_active.png'):new Image.asset('assets/images/medical_sevice_inactive.png'),
title: new Text('Health Services', style: new TextStyle(fontSize: 12.0))),
new BottomNavigationBarItem(
icon: InkWell(
child: Icon(
Icons.format_align_left,
// color: green,
size: 20.0,
),
),
title: new Text('History', style: new TextStyle(fontSize: 12.0))),
]),
);
}
}
If I understand your question correctly, you need the bottom navigation bar persisted on all three pages. There is a well-written article on how to achieve it. You can find the details here.
https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf
https://github.com/bizz84/nested-navigation-demo-flutter
All credits go to the original author.
Use PageView and bottomNavigationBar:
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 App';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: App(),
);
}
}
class App extends StatefulWidget {
App({Key key}) : super(key: key);
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
PageController _myPage;
var selectedPage;
#override
void initState() {
super.initState();
_myPage = PageController(initialPage: 1);
selectedPage = 1;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _myPage,
children: <Widget>[
Center(
child: Text("Another Page"),
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Page 1"),
RaisedButton(
onPressed: () {
_myPage.jumpToPage(0);
setState(() {
selectedPage = 0;
});
},
child: Text("Go to another page"),
)
],
)),
Center(child: Text("Page 2")),
Center(child: Text("Page 3")),
],
),
bottomNavigationBar: BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(Icons.home),
color: selectedPage == 1 ? Colors.blue : Colors.grey,
onPressed: () {
_myPage.jumpToPage(1);
setState(() {
selectedPage = 1;
});
},
),
IconButton(
icon: Icon(Icons.star),
color: selectedPage == 2 ? Colors.blue : Colors.grey,
onPressed: () {
_myPage.jumpToPage(2);
setState(() {
selectedPage = 2;
});
},
),
IconButton(
icon: Icon(
Icons.settings,
),
color: selectedPage == 3 ? Colors.blue : Colors.grey,
onPressed: () {
_myPage.jumpToPage(3);
setState(() {
selectedPage = 3;
});
},
),
],
),
));
}
}
In addition, if you want preserve the state between pages such that going to another page won't cause the previous page to lose its state, use AutomaticKeepAliveClientMixin
Also, to lazily load the pages, PageView.builder is another solution.
Hope it helps.
Another great solution is the persistent_bottom_nav_bar package provided by Bilal Shahid.
It is easy to use and offers you a bunch of features:
Highly customizable persistent bottom navigation bar.
Ability to push new screens with or without bottom navigation bar.
20 styles for the bottom navigation bar.
Includes functions for pushing screen with or without the bottom navigation bar i.e. pushNewScreen() and pushNewScreenWithRouteSettings().
Based on flutter's Cupertino(iOS) bottom navigation bar.
Can be translucent for a particular tab.
Custom styling for the navigation bar. Click here for more information.
Handles hardware/software Android back button.
Before I found this package I followed the solution from the article #Abin mentioned in his answer. But I ran into the problem, that all screens from the navbar beeing loaded on first load of the navbar which is not that perfomant. I did not mangaed to solve this, but luckily Bilal Shahid provide a good solution with his package.
All credits to him.
Just copy & past :)
main.dart:
void main() async{
runApp(MyGrillApp());
}
class MyGrillApp extends StatelessWidget {
const MyGrillApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/mainlayout': (context) => MainLayout(),
'/page1': (context) => Page1(),
'/page2': (context) => Page2(),
'/page3': (context) => Page3(),
'/page4': (context) => Page4(),
},
initialRoute: '/mainlayout',
);
}
}
main_layout.dart:
class MainLayout extends StatefulWidget {
#override
_MainLayoutState createState() => _MainLayoutState();
}
class _MainLayoutState extends State<MainLayout> {
int _currentIndex = 0;
final _page1 = GlobalKey<NavigatorState>();
final _page2 = GlobalKey<NavigatorState>();
final _page3 = GlobalKey<NavigatorState>();
final _page4 = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.miniCenterDocked,
floatingActionButton: Padding(
padding: const EdgeInsets.all(6.0),
child: FloatingActionButton(
backgroundColor: Colors.redAccent,
child: const Icon(Icons.add, color: Colors.white),
onPressed: () {
// ToDo...
},
),
),
body: IndexedStack(
index: _currentIndex,
children: <Widget>[
Navigator(
key: _page1,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => Page1(),
),
),
Navigator(
key: _page2,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => Page2(),
),
),
Navigator(
key: _page3,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => Page3(),
),
),
Navigator(
key: _page4,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => Page4(),
),
),
],
),
bottomNavigationBar: BottomAppBar(
shape: const CircularNotchedRectangle(),
clipBehavior: Clip.antiAlias,
child: BottomNavigationBar(
backgroundColor: Colors.white,
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.redAccent,
unselectedItemColor: Colors.grey,
showSelectedLabels: false,
showUnselectedLabels: false,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.date_range), label: 'Statistics'),
BottomNavigationBarItem(icon: Icon(Icons.wallet_giftcard), label: 'Wallet'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
),
);
}
}
Details screen:
class ItemDetailsPage extends StatefulWidget {
const ItemDetailsPage({Key? key}) : super(key: key);
#override
_ItemDetailsPageState createState() => _ItemDetailsPageState();
}
class _ItemDetailsPageState extends State<ItemDetailsPage> with AutomaticKeepAliveClientMixin{
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
backgroundColor: themeColorPrimary,
title: Text('Item details',),
),
body : Container(child: Text('Hello from details'),));
}
#override
bool get wantKeepAlive => true;
}
A note about routing in my solution:
If you encounter trouble when you routing by:
Navigator.pushNamed(context, '/page3');
or by:
Navigator.of(context).pushNamed(Page3());
You can fix it using MaterialPageRoute:
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Page3(),
),
);
You can use IndexedStack to persist State when you touch/change the page
Scaffold(
body: SafeArea(
top: false,
child: IndexedStack(
//Permet de garder le state des vues même quand on change de vue
index: _currentIndex,
children: _children,
),
),
bottomNavigationBar: BottomNavigationBar( items: [ ] ),
);
I highly recommend using stack. This gives you pretty much total control over how and when you would like to show bottom app bar.
Make list of all pages you want to show using your botttomAppBar. Let's say has three icons.
final List<Widget> pages=[FirstScreen(),SecondScreen(),ThirdScreen()];
In the Build Method
Scaffold(
child: Stack(
children: <Widget>[
Navigator(
key: _navigatorKey,
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(
builder: (BuildContext context) => pages[cur_ind],
);
},
),
],
bottomNavigationBar: BottomNavigationBar(
onTap: (int index){
setState(() {
cur_ind=index;
});
},
currentIndex: cur_ind,
fixedColor: Colors.green, //let's say
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.mail),
title: Text('Messages'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person), title: Text('Profile'))
],
),
),
),
where cur_ind is the variable used to control which page to show. And since the body is stacked, the Bottom Navigation Bar will be persistent always.
I created a small, super easy to use package that let you do that effect CustomNavigator.
And wrote a tutorial about it on Medium you can find it here.
So it goes like this
// Here's the custom scaffold widget
// It takes a normal scaffold with mandatory bottom navigation bar
// and children who are your pages
CustomScaffold(
scaffold: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: _items,
),
),
// Children are the pages that will be shown by every click
// They should placed in order such as
// `page 0` will be presented when `item 0` in the [BottomNavigationBar] clicked.
children: <Widget>[
Page('0'),
Page('1'),
Page('2'),
],
// Called when one of the [items] is tapped.
onItemTap: (index) {},
);
The cool thing about this library that it works efficiently. It creates a nested navigator (which is very unpleasant to do) and uses it for navigation in your widget tree.
And of course you can always use the default navigator from MaterialApp
If you are looking for a solution that performs well (that doesn't build the tabs/pages unnecessarily) even using IndexedStack take a look at my answer here
For anyone looking for this in the future auto_route handle this pretty much well with very little boilerplate using AutoTabsScaffold.
Widget build(context) {
return AutoTabsScaffold(
routes: const [
BooksRouter(),
AccountRouter(),
],
bottomNavigationBuilder: (_, tabsRouter) {
return BottomNavigationBar(
currentIndex: tabsRouter.activeIndex,
onTap: tabsRouter.setActiveIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.book),
label: 'Books',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box),
label: 'Account',
),
],
);
},
);
}
I am working on a beta version of an express_app plugin, which achieve the required result.
Two days ago, I implemented an addition where you can set an ExpressHome and it can be any part of your tree, in addition to setting your routes of course. When changing the routes, everything under ExpressHome will change only and the rest will stay the same (i.e. you can have a permanent bar easily.
I will publish a more-recent version this evening, and if you would like a specific demo about your use case, let me know.
i had this issue too...after days of research i came across this package
persistent_bottom_nav_bar: ^4.0.0
it quite easy to implement.
You can use a scaffold widget to contain the whole screen then put IndexedStack widget as a Body option then use at the bottom navigation option in the scaffold widget you favorite implementation of the bottom navigation bar
Scaffold(
// here is the IndexedStack as body
body: IndexedStack(
index: this._bottomNavIndex,
children: [MangaGridView(), FavoriteManga()]),
backgroundColor: Colors.black,
bottomNavigationBar: AnimatedBottomNavigationBar(
icons: [
Icons.home_outlined,
Icons.favorite_border,
Icons.settings,
],
inactiveColor: Colors.black,
activeIndex: this._bottomNavIndex,
gapLocation: GapLocation.none,
activeColor: Theme.of(context).primaryColor,
notchSmoothness: NotchSmoothness.verySmoothEdge,
leftCornerRadius: 32,
rightCornerRadius: 32,
onTap: (index) => setState(() => this._bottomNavIndex = index),
height: 70,
splashColor: Theme.of(context).primaryColor,
splashRadius: 40.0,
splashSpeedInMilliseconds: 400,
iconSize: 34,
),
);
Navigator.of(context).pushNamed(); is for Navigation with page transition. So, in this situation, the method is not match.
You can use BottomNavigationBar with Scaffold.
example code:
class AppFooter extends StatefulWidget {
#override
_AppFooterState createState() => _AppFooterState();
}
class _AppFooterState extends State<AppFooter> {
int _currentIndex = 0;
List<Widget> _pages = [
Text("page1"),
Text("page2"),
Text("page3"),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (int index) {
setState(() {
_currentIndex = index;
});
},
items: [
new BottomNavigationBarItem(
backgroundColor: Colors.white,
icon: _currentIndex == 0
? new Image.asset('assets/images/dashboard_active.png')
: new Image.asset('assets/images/dashboard_inactive.png'),
title:
new Text('Dashboard', style: new TextStyle(fontSize: 12.0))),
new BottomNavigationBarItem(
backgroundColor: Colors.white,
icon: _currentIndex == 1
? new Image.asset('assets/images/medical_sevice_active.png')
: new Image.asset(
'assets/images/medical_sevice_inactive.png'),
title: new Text('Health Services',
style: new TextStyle(fontSize: 12.0))),
new BottomNavigationBarItem(
icon: InkWell(
child: Icon(
Icons.format_align_left,
// color: green,
size: 20.0,
),
),
title: new Text('History', style: new TextStyle(fontSize: 12.0))),
],
),
);
}
}
Just make your index variable static
like:
static int index = 0;
Related
I'm using the sample below (taken from the CupertinoTabScaffold documentation page).
There is a "slide" transition when pushing a new route inside the tab, but when I click on a tabbar item, the content is brutally replaced. How can I have a transition when switching between tabs?
I would like implement something like that: https://github.com/Interactive-Studio/TransitionableTab
CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
title: Text("Tab 0"),
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.news),
title: Text("Tab 1"),
),
],
),
tabBuilder: (BuildContext context, int index) {
return CupertinoTabView(
builder: (BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Page 1 of tab $index'),
),
child: Center(
child: CupertinoButton(
child: const Text('Next page'),
onPressed: () {
Navigator.of(context).push(
CupertinoPageRoute<void>(
builder: (BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Page 2 of tab $index'),
),
child: Center(
child: CupertinoButton(
child: const Text('Back'),
onPressed: () { Navigator.of(context).pop(); },
),
),
);
},
),
);
},
),
),
);
},
);
},
)
May be this package will be help you page_transition: ^2.0.5
https://pub.dev/packages/page_transition
Try this -
It has info on how to animate tabBarView but should be similar to CupertinoTabView
class MyApp2 extends StatefulWidget {
#override
_MyApp2State createState() => _MyApp2State();
}
class _MyApp2State extends State<MyApp2> with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 4, vsync: this);
super.initState();
}
_tabBarView() {
return AnimatedBuilder(
animation: _tabController.animation,
builder: (BuildContext context, snapshot) {
return Transform.rotate(
angle: _tabController.animation.value * pi,
child: [
Container(
color: Colors.blue,
),
Container(
color: Colors.orange,
),
Container(
color: Colors.lightGreen,
),
Container(
color: Colors.red,
),
][_tabController.animation.value.round()],
);
},
);
}
_bottomTabBar() {
return TabBar(
controller: _tabController,
labelColor: Colors.black,
tabs: [
Tab(
icon: new Icon(Icons.home),
),
Tab(
icon: new Icon(Icons.public),
),
Tab(
icon: new Icon(Icons.group),
),
Tab(
icon: new Icon(Icons.person),
)
],
);
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(title: const Text('Bottom App Bar')),
body: _tabBarView(),
bottomNavigationBar: _bottomTabBar(),
),
);
}
}
The above code is taken from the following link-
Flutter - Change the animation of TabBarView
i want to open profile page from home page without press 'profile' button inside bottomnavigation item.
Thanks for help.
this is a main class of my project. i put all page here and i already import all page here.
MainClass
int _currentIndex = 0;
final List<Widget> _children = [
HomePage(),
MessagePage(),
ProfilePage()
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Flutter App'),
),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: onTabTapped,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.home),
title: new Text('Home'),
),
BottomNavigationBarItem(
icon: new Icon(Icons.mail),
title: new Text('Messages'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Profile')
)
],
),
);
}
and this my home page. this page same as profile and message page. i need to open profile page from home page without pressing profile menu in bottom. i just wanna press profile button inside home page.
HomePage
import 'package:flutter/material.dart';
import 'package:navigation/profile_page.dart';
class HomePage extends StatefulWidget {
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Text("Home Page"),
RaisedButton(
child: Text("GO TO PROFILE PAGE"),
onPressed: (){
);
},
)
],
),
),
);
}
}
This can be done by directly calling the class name.
For example consider Profile() be the class extending statefull class.
Home class
....
int _index = 1;
final options = [
Text('Home'),
Text('Message'),
Profile() //directly call the class to load
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("My flutter app"),
),
body: Center(
child: options.elementAt(_index),
),
bottomNavigationBar: BottomNavigationBar(
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.message),
title: Text('message'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('profile')
)
],
currentIndex: _index,
onTap: _onTapped,
),
);
}
void _onTapped(int index) {
setState(() {
_index = index;
});
}
Similarly you can load other classes too.
hope it helped:)
I'm quite new with Flutter and I'm coming from using the Angular framework. Currently, I'm experimenting with flutter to make a desktop application using the following flutter embedding project: https://github.com/Drakirus/go-flutter-desktop-embedder.
I was wondering if someone could explain to me the best way to implement the following:
The black box represents the application as a whole.
The red box represents the custom menu.
The green box represents the content of the page.
How would I go about routing between "widgets" inside of the green area without changing the widget holding the application?
I'd love some direction please.
I am contributing Drakirus 's go-flutter plugin.
This projecd had moved to https://github.com/go-flutter-desktop
The question you ask can use package responsive_scaffold
https://pub.dev/packages/responsive_scaffold
or
you can reference this doc https://iirokrankka.com/2018/01/28/implementing-adaptive-master-detail-layouts/
Basically, there two are different layouts, see comments for detail
class _MasterDetailContainerState extends State<MasterDetailContainer> {
// Track the currently selected item here. Only used for
// tablet layouts.
Item _selectedItem;
Widget _buildMobileLayout() {
return ItemListing(
// Since we're on mobile, just push a new route for the
// item details.
itemSelectedCallback: (item) {
Navigator.push(...);
},
);
}
Widget _buildTabletLayout() {
// For tablets, return a layout that has item listing on the left
// and item details on the right.
return Row(
children: <Widget>[
Flexible(
flex: 1,
child: ItemListing(
// Instead of pushing a new route here, we update
// the currently selected item, which is a part of
// our state now.
itemSelectedCallback: (item) {
setState(() {
_selectedItem = item;
});
},
),
),
Flexible(
flex: 3,
child: ItemDetails(
// The item details just blindly accepts whichever
// item we throw in its way, just like before.
item: _selectedItem,
),
),
],
);
}
For package responsive_scaffold
on-line demo https://fluttercommunity.github.io/responsive_scaffold/#/
github https://github.com/fluttercommunity/responsive_scaffold/
more template code snippets for layout
https://github.com/fluttercommunity/responsive_scaffold/tree/dev
more pictures and demo can found here https://github.com/fluttercommunity/responsive_scaffold/tree/dev/lib/templates/3-column
code snippet 1
import 'package:flutter/material.dart';
import 'package:responsive_scaffold/responsive_scaffold.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var _scaffoldKey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ResponsiveListScaffold.builder(
scaffoldKey: _scaffoldKey,
detailBuilder: (BuildContext context, int index, bool tablet) {
return DetailsScreen(
// appBar: AppBar(
// elevation: 0.0,
// title: Text("Details"),
// actions: [
// IconButton(
// icon: Icon(Icons.share),
// onPressed: () {},
// ),
// IconButton(
// icon: Icon(Icons.delete),
// onPressed: () {
// if (!tablet) Navigator.of(context).pop();
// },
// ),
// ],
// ),
body: Scaffold(
appBar: AppBar(
elevation: 0.0,
title: Text("Details"),
automaticallyImplyLeading: !tablet,
actions: [
IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
if (!tablet) Navigator.of(context).pop();
},
),
],
),
bottomNavigationBar: BottomAppBar(
elevation: 0.0,
child: Container(
child: IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
),
),
body: Container(
child: Center(
child: Text("Item: $index"),
),
),
),
);
},
nullItems: Center(child: CircularProgressIndicator()),
emptyItems: Center(child: Text("No Items Found")),
slivers: <Widget>[
SliverAppBar(
title: Text("App Bar"),
),
],
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: Text(index.toString()),
);
},
bottomNavigationBar: BottomAppBar(
elevation: 0.0,
child: Container(
child: IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text("Snackbar!"),
));
},
),
),
);
}
}
code snippet 2
import 'package:flutter/material.dart';
import 'package:responsive_scaffold/responsive_scaffold.dart';
class MultiColumnNavigationExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ThreeColumnNavigation(
title: Text('Mailboxes'),
showDetailsArrows: true,
backgroundColor: Colors.grey[100],
bottomAppBar: BottomAppBar(
elevation: 1,
child: Row(
children: <Widget>[
IconButton(
icon: Icon(
Icons.filter_list,
color: Colors.transparent,
),
onPressed: () {},
),
],
),
),
sections: [
MainSection(
label: Text('All Inboxes'),
icon: Icon(Icons.mail),
itemCount: 100,
itemBuilder: (context, index, selected) {
return ListTile(
leading: CircleAvatar(
child: Text(index.toString()),
),
selected: selected,
title: Text('Primary Information'),
subtitle: Text('Here are some details about the item'),
);
},
bottomAppBar: BottomAppBar(
elevation: 1,
child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () {},
),
],
),
),
getDetails: (context, index) {
return DetailsWidget(
title: Text('Details'),
child: Center(
child: Text(
index.toString(),
),
),
);
},
),
MainSection(
label: Text('Sent Mail'),
icon: Icon(Icons.send),
itemCount: 100,
itemBuilder: (context, index, selected) {
return ListTile(
leading: CircleAvatar(
child: Text(index.toString()),
),
selected: selected,
title: Text('Secondary Information'),
subtitle: Text('Here are some details about the item'),
);
},
getDetails: (context, index) {
return DetailsWidget(
title: Text('Details'),
actions: [
IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
],
child: Center(
child: Text(
index.toString(),
),
),
);
},
),
],
);
}
}
I'm a noob so please take anything I say with a grain of salt.
I know 2 ways to navigate through widgets and you can find them both here
https://flutter.io/docs/development/ui/navigation
I believe the main difference I can perceive is if you want to
send data to the new 'route' or not (the named route way cannot, at least that I'm aware of);
said so you can keep your main 'screen' and change the red and green widget
using the state of the widget where they are contained
example
class BlackWidget extends StatefulWidget
bla bla bla => BlackWidgetState();
class BlackWidget extend State<BlackWidget>
Widget tallWidget = GreenWidget();
Widget bigWidget = RedWidget();
return
container, column.. etc
Row(
children:[tallWidget,bigWidget
])
button onTap => tallWidget = YellowWidget();
}
GreenWidget... bla bla bla...
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => RedWidget()),
);
}
sorry for the 'bla bla', the part you need is at the bottom,
just added the 'yellow' widget to underline that you can
actually swap the 'green widget' with anything you want
I am trying to create a uniform drawer that is accessible across all pages in my app. How do I make it persist throughout all these pages without having to recreate my custom Drawer widget in every single dart file?
There are a few different options for this. The most basic is hopefully something you've already done, but I'll list it anyways:
1: Create a class for your drawer
Your widget should be its own stateful or stateless widget. This way, you just have to instantiate it each time.
class MyDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(...);
}
}
And then when using it in each page:
Scaffold(
drawer: MyDrawer(...),
...
)
I hope you're already doing this; if not you should be. A class's build function shouldn't be too large or it can lead to poor performance and harder to maintain code; splitting things into logical units will help you in the long run.
2: Create a class for your scaffold
If having to include the same drawer in a scaffold for each page is still too much code, you can instead use a class that encapsulates your scaffold. It would essentially take inputs for each of the scaffold inputs you actually use.
class MyScaffold extends StatelessWidget {
final Widget body;
MyScaffold({this.body});
#override
Widget build(BuildContext context) {
return Scaffold(
body: body,
drawer: MyDrawer(...),
);
}
}
And then instead of using Scaffold in your code, use MyScaffold (but please name it something better =D).
3: Multi level scaffold
I'm only including this way of doing it to be complete, and I don't recommend it. That being said, there are certain things you can't get to work within flutter's normal workflow that you could do by doing this - for example if you want a custom animation for when the user taps on different items in the drawer.
Basically, what you'd do in this case is to have a Scaffold outside of your MaterialApp or Navigator (which I believe would also mean you'd have to have another Navigator outside that, but I'm not 100% sure). You would have the scaffold that's outside your navigation show the drawer while the other one (on each page within the navigation) would do whatever else you need it to do. There's a few caveats - you'd have to make sure you get the right scaffold (i.e. Scaffold.of(context) by itself wouldn't cut it - you'd have to get the context of the first scaffold and use it to find the higher-level one), and you'd probably need to pass a GlobalKey (of the lower-level scaffold) to the Drawer so that it could actually change pages within it.
As I said, I don't recommend this approach, so I'm not going to go into any more detail than that but rather leave it as an exercise for the reader if they want to go down that rabbit hole!
rmtmckenzie is very correct.
Although if you are curious about the multi scaffold solution, this can be more elegant than you think.
To share a drawer between all pages we could add a builder in our MaterialApp instance.
This will instantiate a Scaffold under Navigator but above all routes.
MaterialApp(
title: 'Flutter Demo',
builder: (context, child) {
return Scaffold(
drawer: MyDrawer(),
body: child,
);
},
home: MyHome()
)
Inside your page, you can instantiate another Scaffold without restriction as you'd usually do.
You can then show the shared drawer by doing the following in any widget under MaterialApp :
final ScaffoldState scaffoldState = context.rootAncestorStateOfType(TypeMatcher<ScaffoldState>());
scaffoldState.openDrawer();
Code which you can extract into a nice helper :
class RootScaffold {
static openDrawer(BuildContext context) {
final ScaffoldState scaffoldState =
context.rootAncestorStateOfType(TypeMatcher<ScaffoldState>());
scaffoldState.openDrawer();
}
}
Then reuse using RootScaffold.openDrawer(context)
In Addition to #Rémi Rousselet Answer
MaterialApp(
title: 'Flutter Demo',
builder: (context, child) {
return Scaffold(
drawer: MyDrawer(),
body: child,
);
},
home: MyHome()
)
For Navigation in root drawer if you use Navigator.of(context) // push or pop that will throw error and for that you must use child widget to navigate to different pages
Like that
(child.key as GlobalKey<NavigatorState>).currentState // push or pop
Demo project in Github
if somebody looking for fancy stuff while navigating look here. What I use as a drawer for my project is flutter_inner_drawer package.
I created a stateful class named CustomDrawer.
class CustomDrawer extends StatefulWidget {
final Widget scaffold;
final GlobalKey<InnerDrawerState> innerDrawerKey;
CustomDrawer({
Key key,
this.scaffold,
this.innerDrawerKey,
}) : super(key: key);
#override
_CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
MainPageIcons assets = MainPageIcons();//From my actual code dont care it
final vars = GlobalVars.shared; //From my actual code dont care it
#override
Widget build(BuildContext context) {
return InnerDrawer(
key: widget.innerDrawerKey,
onTapClose: true, // default false
tapScaffoldEnabled: true,
swipe: true, // default true
colorTransition: Colors.teal, // default Color.black54
//innerDrawerCallback: (a) => print(a ),// return bool
leftOffset: 0.2, // default 0.4
leftScale: 1,// default 1
boxShadow: [
BoxShadow(color: Colors.teal,blurRadius: 20.0, // has the effect of softening the shadow
spreadRadius: 10.0, // has the effect of extending the shadow
offset: Offset(
10.0, // horizontal, move right 10
10.0, // vertical, move down 10
),)],
borderRadius: 20, // default 0
leftAnimationType: InnerDrawerAnimation.quadratic, // default static
//when a pointer that is in contact with the screen and moves to the right or left
onDragUpdate: (double val, InnerDrawerDirection direction) =>
setState(() => _dragUpdate = val),
//innerDrawerCallback: (a) => print(a),
// innerDrawerCallback: (a) => print(a), // return true (open) or false (close)
leftChild: menus(), // required if rightChild is not set
scaffold:widget.scaffold
);
}
double _dragUpdate = 0;
Widget menus(){
return
Material(
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
ColorTween(
begin: Colors.blueAccent,
end: Colors.blueGrey[400].withRed(100),
).lerp(_dragUpdate),
ColorTween(
begin: Colors.green,
end: Colors.blueGrey[800].withGreen(80),
).lerp(_dragUpdate),
],
),
),
child: Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 10, bottom: 15),
width: 80,
child: ClipRRect(
child: Image.network(
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSrWfWLnxIT5TnuE-JViLzLuro9IID2d7QEc2sRPTRoGWpgJV75",
),
borderRadius: BorderRadius.circular(60),
),
),
Text(
"User",
style: TextStyle(color: Colors.white, fontSize: 18),
)
],
//mainAxisAlignment: MainAxisAlignment.center,
),
Padding(
padding: EdgeInsets.all(10),
),
ListTile(
onTap: ()=>navigate(Profile.tag),
title: Text(
"Profile",
style: TextStyle(color: Colors.white, fontSize: 14),
),
leading: Icon(
Icons.dashboard,
color: Colors.white,
size: 22,
),
),
ListTile(
title: Text(
"Camera",
style: TextStyle(fontSize: 14,color:Colors.white),
),
leading: Icon(
Icons.camera,
size: 22,
color: Colors.white,
),
onTap: ()=>navigate(Camera.tag)
),
ListTile(
title: Text(
"Pharmacies",
style: TextStyle(fontSize: 14,color:Colors.white),
),
leading: Icon(
Icons.add_to_photos,
size: 22,
color: Colors.white,
),
onTap: ()=>navigate(Pharmacies.tag)
),
],
),
),
Positioned(
bottom: 20,
child: Container(
alignment: Alignment.bottomLeft,
margin: EdgeInsets.only(top: 50),
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 25),
width: double.maxFinite,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(
Icons.all_out,
size: 18,
color: Colors.grey,
),
Text(
" LogOut",
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
],
),
),
)
],
),
),
_dragUpdate < 1
? BackdropFilter(
filter: ImageFilter.blur(
sigmaX: (10 - _dragUpdate * 10),
sigmaY: (10 - _dragUpdate * 10)),
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0),
),
),
)
: null,
].where((a) => a != null).toList(),
));
}
navigate(String route) async{
await navigatorKey.currentState.pushNamed(route).then((_){
Timer(Duration(milliseconds: 500),()=>widget.innerDrawerKey.currentState.toggle() );
});
}
}
I copied example from package and didnt touch original much. only aded a function to toggle after turn back.
navigate(String route) async{
await navigatorKey.currentState.pushNamed(route).then((_){
Timer(Duration(milliseconds: 500),()=>widget.innerDrawerKey.currentState.toggle() );
});
}
to navigate from all over pages aded GlobalKey globally so that reachable from every class
final GlobalKey<NavigatorState> navigatorKey = GlobalKey(debugLabel: "Main Navigator");
inner_drawer also needs a globalkey for state to toogle but if you create only one when navigate between pages it gives duplicate global key error. to avoid I created a global variable named innerKeys
Map<String,GlobalKey<InnerDrawerState>>innerKeys={
'main':GlobalKey<InnerDrawerState>(),
'profile':GlobalKey<InnerDrawerState>(),
'pharmacies':GlobalKey<InnerDrawerState>(),
};
finally I added this CustomDrawer to every pages
#override
Widget build(BuildContext context) {
return CustomDrawer(
innerDrawerKey: vars.innerKeys['profile'],
scaffold:Scaffold(
appBar: CustomAppBar(
title: 'Profile',
actions: <Widget>[
],),
body: Stack(
children: <Widget>[
Background(),
])));
}
I hope it will helps to someone.
NOTE: please check original flutter pack if anything updated. Be avare that this example is not perfect and needs to taken care that if many navigation over this drawer then widget tree will have many pages and performance will be impacted. any tuning suggestion will be appriciated.
My Solution Navigation Drawer with Multiple Fragments using bloc package
First, add below dependencies in your pubspec.yaml file
flutter_bloc: ^4.0.0
Now create below files
drawer_event.dart
import 'nav_drawer_state.dart';
abstract class NavDrawerEvent {
const NavDrawerEvent();
}
class NavigateTo extends NavDrawerEvent {
final NavItem destination;
const NavigateTo(this.destination);
}
nav_drawer_bloc.dart
import 'package:bloc/bloc.dart';
import 'drawer_event.dart';
import 'nav_drawer_state.dart';
class NavDrawerBloc extends Bloc<NavDrawerEvent, NavDrawerState> {
#override
NavDrawerState get initialState => NavDrawerState(NavItem.homePage);
#override
Stream<NavDrawerState> mapEventToState(NavDrawerEvent event) async* {
if (event is NavigateTo) {
if (event.destination != state.selectedItem) {
yield NavDrawerState(event.destination);
}
}
}
}
nav_drawer_state.dart
class NavDrawerState {
final NavItem selectedItem;
const NavDrawerState(this.selectedItem);
}
enum NavItem {
homePage,
profilePage,
orderPage,
myCart,
}
drawer_widget.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutterdrawerwithbloc/bloc/drawer_event.dart';
import 'package:flutterdrawerwithbloc/bloc/nav_drawer_bloc.dart';
import 'package:flutterdrawerwithbloc/bloc/nav_drawer_state.dart';
class NavDrawerWidget extends StatelessWidget {
final String accountName;
final String accountEmail;
final List<_NavigationItem> _listItems = [
_NavigationItem(true, null, null, null),
_NavigationItem(false, NavItem.homePage, "Home", Icons.home),
_NavigationItem(false, NavItem.profilePage, "Profile Page", Icons.person),
_NavigationItem(false, NavItem.orderPage, "My Orders", Icons.list),
_NavigationItem(false, NavItem.myCart, "My Cart", Icons.shopping_cart),
];
NavDrawerWidget(this.accountName, this.accountEmail);
#override
Widget build(BuildContext context) => Drawer(
child: Container(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: _listItems.length,
itemBuilder: (BuildContext context, int index) =>
BlocBuilder<NavDrawerBloc, NavDrawerState>(
builder: (BuildContext context, NavDrawerState state) =>
_buildItem(_listItems[index], state),
)),
));
Widget _buildItem(_NavigationItem data, NavDrawerState state) =>
data.header ? _makeHeaderItem() : _makeListItem(data, state);
Widget _makeHeaderItem() => UserAccountsDrawerHeader(
accountName: Text(accountName, style: TextStyle(color: Colors.white)),
accountEmail: Text(accountEmail, style: TextStyle(color: Colors.white)),
decoration: BoxDecoration(color: Colors.indigo),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white,
foregroundColor: Colors.amber,
child: Icon(
Icons.person,
size: 54,
),
),
);
Widget _makeListItem(_NavigationItem data, NavDrawerState state) => Card(
shape: ContinuousRectangleBorder(borderRadius: BorderRadius.zero),
borderOnForeground: true,
elevation: 0,
margin: EdgeInsets.zero,
child: Builder(
builder: (BuildContext context) => ListTile(
title: Text(
data.title,
style: TextStyle(
color: data.item == state.selectedItem ? Colors.green : Colors.blueGrey,
),
),
leading: Icon(
data.icon,
color: data.item == state.selectedItem ? Colors.green : Colors.blueGrey,
),
onTap: () => _handleItemClick(context, data.item),
),
),
);
void _handleItemClick(BuildContext context, NavItem item) {
BlocProvider.of<NavDrawerBloc>(context).add(NavigateTo(item));
Navigator.pop(context);
}
}
class _NavigationItem {
final bool header;
final NavItem item;
final String title;
final IconData icon;
_NavigationItem(this.header, this.item, this.title, this.icon);
}
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutterdrawerwithbloc/bloc/nav_drawer_bloc.dart';
import 'package:flutterdrawerwithbloc/bloc/nav_drawer_state.dart';
import 'package:flutterdrawerwithbloc/drawer_widget.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Navigation Drawer Demo',
theme: ThemeData(primarySwatch: Colors.blue, scaffoldBackgroundColor: Colors.white),
home: MyHomePage(),
);
;
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
NavDrawerBloc _bloc;
Widget _content;
#override
void initState() {
super.initState();
_bloc = NavDrawerBloc();
_content = _getContentForState(_bloc.state.selectedItem);
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) => BlocProvider<NavDrawerBloc>(
create: (BuildContext context) => _bloc,
child: BlocListener<NavDrawerBloc, NavDrawerState>(
listener: (BuildContext context, NavDrawerState state) {
setState(() {
_content = _getContentForState(state.selectedItem);
});
},
child: BlocBuilder<NavDrawerBloc, NavDrawerState>(
builder: (BuildContext context, NavDrawerState state) => Scaffold(
drawer: NavDrawerWidget("AskNilesh", "rathodnilsrk#gmail.com"),
appBar: AppBar(
title: Text(_getAppbarTitle(state.selectedItem)),
centerTitle: false,
brightness: Brightness.light,
backgroundColor: Colors.indigo,
),
body: AnimatedSwitcher(
switchInCurve: Curves.easeInExpo,
switchOutCurve: Curves.easeOutExpo,
duration: Duration(milliseconds: 300),
child: _content,
),
),
),
));
_getAppbarTitle(NavItem state) {
switch (state) {
case NavItem.homePage:
return 'Home';
case NavItem.profilePage:
return 'Profile Page';
case NavItem.orderPage:
return 'My Orders';
case NavItem.myCart:
return 'My Cart';
default:
return '';
}
}
_getContentForState(NavItem state) {
switch (state) {
case NavItem.homePage:
return Center(
child: Text(
'Home Page',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
case NavItem.profilePage:
return Center(
child: Text(
'Profile Page',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
case NavItem.orderPage:
return Center(
child: Text(
'My Orders',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
case NavItem.myCart:
return Center(
child: Text(
'My Cart',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
default:
return Center(
child: Text(
'Home Page',
style: TextStyle(fontWeight: FontWeight.bold),
),
);
}
}
}
You can find complete project here Navigation Drawer with Multiple Fragments using bloc
In Addition to #Rémi Rousselet Answer, the code has slightly changed (2022) - due to null safety amends.
Replace this:
class RootScaffold {
static openDrawer(BuildContext context) {
final ScaffoldState scaffoldState =
context.rootAncestorStateOfType(TypeMatcher<ScaffoldState>());
scaffoldState.openDrawer();
}
}
...with...
class RootScaffold {
static openDrawer(BuildContext context) {
final ScaffoldState? scaffoldState = context.findRootAncestorStateOfType<ScaffoldState>();
scaffoldState?.openDrawer();
}
}
You can create the ScaffoldCustom when you ensure that all pages have only the body differently.
But I feel that this approach is too restrictive.
So, I am using this.
For the AppBar:
class AppBarPattern1 extends StatelessWidget implements PreferredSizeWidget {
const AppBarPattern1({Key? key}) : super(key: key);
#override
// TODO: implement preferredSize
Size get preferredSize => const Size.fromHeight(kToolbarHeight); // You can change it.
/*
/// The height of the toolbar component of the [AppBar].
const double kToolbarHeight = 56.0;
*/
#override
Widget build(BuildContext context) {
return AppBar();
}
}
For the Drawer:
class DrawerPattern1 extends StatelessWidget {
const DrawerPattern1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const Drawer();
}
}
Using like this:
class ExamplePage extends StatelessWidget {
const ExamplePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: const AppBarPattern1(),
endDrawer: const DrawerPattern1(),
body: SafeArea(child: Container()),
);
}
}
As you see these custom widgets can be const.
I try to build a App with flutter and have a problem by building the navigation. I want to have a navigation like in the current version of youtube app. A Bottom Navigation Bar with three Pages and than for each Page sub Pages with an owen navigation stack. On all subpages it shoud be possible to change the main view and the app shoud save on witch subpage i where. Is that possible? I found no solution for that. I think it shoud be possible because its on the example page of material Design: https://material.io/design/components/bottom-navigation.html#behavior at the Point "Bottom navigation actions".
I would be so thankful for help!
I'd take a look at this code snippet for help.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:my_nit2018/navigarion_drawer.dart';
import 'package:my_nit2018/pages/app/blog/blog_page.dart';
import 'package:my_nit2018/pages/app/home/home_page.dart';
import 'package:my_nit2018/pages/app/library/library_page.dart';
import 'package:my_nit2018/pages/app/notifications/notifications_page.dart';
class MainApp extends StatefulWidget {
FirebaseUser user;
MainApp(this.user);
#override
_MainAppState createState() => new _MainAppState();
}
class _MainAppState extends State<MainApp> {
int i = 0;
var pages = [
new HomePage(),
new BlogPage(),
new LibraryPage(),
new NotificationsPage()
];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: pages[i],
drawer: new AppNavigationDrawer(),
bottomNavigationBar: new BottomNavigationBar(
items: [
new BottomNavigationBarItem(
icon: new Icon(Icons.home),
title: new Text('Home'),
),
new BottomNavigationBarItem(
icon: new Icon(Icons.photo_library),
title: new Text('Blog'),
),
new BottomNavigationBarItem(
icon: new Icon(Icons.book),
title: new Text('Library'),
),
new BottomNavigationBarItem(
icon: new Icon(Icons.notifications),
title: new Text('Notifications'),
),
],
currentIndex: i,
type: BottomNavigationBarType.fixed,
onTap: (index) {
setState(() {
i = index;
});
},
),
);
}
}
AppNavigationDrawer:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:my_nit2018/pages/app/app_state.dart';
import 'package:my_nit2018/pages/app/main_app.dart';
import 'package:my_nit2018/pages/app/profile/profile_page.dart';
import 'package:my_nit2018/pages/auth/login_page.dart';
class AppNavigationDrawer extends StatefulWidget {
#override
_AppNavigationDrawerState createState() => new
_AppNavigationDrawerState();
}
class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
#override
Widget build(BuildContext context) {
final appState = AppState.of(context);
return new Drawer(
child: new ListView(
padding: EdgeInsets.zero,
children: <Widget>[
new DrawerHeader(
child: new Text('MyNiT App'),
decoration: new BoxDecoration(
color: Colors.blue,
),
),
new ListTile(
title: new Text('Todo List'),
leading: new Icon(Icons.list),
onTap: () {
Navigator.pop(context);
},
),
new ListTile(
title: new Text('Subscriptions'),
leading: new Icon(Icons.subscriptions),
onTap: () {
Navigator.pop(context);
},
),
new ListTile(
title: new Text('Activity'),
leading: new Icon(Icons.timelapse),
onTap: () {
Navigator.pop(context);
},
),
new ListTile(
title: new Text('Profile'),
leading: new Icon(Icons.account_circle),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new AppState(
firebaseUser: appState.firebaseUser,
user: appState.user,
child: new ProfilePage(),
),
),
);
},
),
new ListTile(
title: new Text('Logout'),
leading: new Icon(Icons.exit_to_app),
onTap: () {
// Sign out user from app
FirebaseAuth.instance.signOut();
Navigator.of(context).pushAndRemoveUntil(
new MaterialPageRoute(builder: (context) => new LoginPage()),
ModalRoute.withName(null));
},
),
],
),
);
}
}
Try this Simple Bottom Bar
[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),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('School'),
),
\],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber\[800\],
onTap: _onItemTapped,
),
);
}
}][1]
Check this image for Sample