I am trying to implement Flutter's Tab Bar with 3 tabs and an AnimatedList inside those tabs. I want to use the same list and filter the list according to each tab (past tasks, today's tasks, and future tasks), however during my implementation of the tab bar together with the animatedlist I am getting an error regarding a duplicate global key in the widget tree. https://pastebin.com/iAW6DH9m . What would be the best way to deal with this error? Thank you for any help.
edit: I tried using this method to fix this. Multiple widgets used the same GlobalKey while it did fix my error I was then unable to access "currentstate" method on the key to be able to add more items to the list. I then tried a similar method using using GlobalKey and it resulted in a similar error of duplicate global keys.
This is my tab bar implementation
import 'package:flutter/material.dart';
import 'search_widget.dart';
import 'animatedlist_widget.dart';
class Dashboard extends StatefulWidget {
#override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
centerTitle: true,
actions: <Widget>[
new IconButton(icon: new Icon(Icons.grid_on), onPressed: null)
],
title: new Text('Dashboard'),
elevation: 0,
),
floatingActionButton: new FloatingActionButton(
onPressed: () {
_onFabPress(context);
},
child: new Icon(Icons.add)),
body: Scaffold(
appBar: new SearchWidget(
onPressed: () => print('implement search'),
icon: Icons.search,
title: 'Search',
preferredSize: Size.fromHeight(50.0),
),
body: DefaultTabController(
length: 3,
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: Container(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: new TabBar(
unselectedLabelColor: Colors.black45,
labelColor: Colors.white,
indicator: CustomTabIndicator(),
tabs: <Widget>[
new Tab(text: "Past"),
new Tab(text: "Today"),
new Tab(text: "Future")
]),
),
),
),
body: new TabBarView(
children: <Widget>[
AnimatedTaskList(),
AnimatedTaskList(),
AnimatedTaskList()
],
)
),
),
),
);
}
void _onFabPress(context) {
AnimatedTaskList().addUser();
}
/*showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return Container(
child: new Wrap(children: <Widget>[
new TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter Task Title')),
new TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter Task Details',
)),
]));
});
}*/
}
class CustomTabIndicator extends Decoration {
#override
BoxPainter createBoxPainter([onChanged]) {
// TODO: implement createBoxPainter
return new _CustomPainter(this, onChanged);
}
}
class _CustomPainter extends BoxPainter {
final CustomTabIndicator decoration;
_CustomPainter(this.decoration, VoidCallback onChanged)
: assert(decoration != null),
super(onChanged);
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
// TODO: implement paint
assert(configuration != null);
assert(configuration.size != null);
final indicatorHeight = 30.0;
final Rect rect = Offset(
offset.dx, (configuration.size.height / 2) - indicatorHeight / 2) &
Size(configuration.size.width, indicatorHeight);
final Paint paint = Paint();
paint.color = Colors.blueAccent;
paint.style = PaintingStyle.fill;
canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(30)), paint);
}
}
This is my animatedlist class:
import 'package:flutter/material.dart';
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
class AnimatedTaskList extends StatefulWidget {
void addUser() {
int index = listData.length;
listData.add(
TaskModel(
taskTitle: "Grocery Shopping",
taskDetails: "Costco",
),
);
_listKey.currentState
.insertItem(index, duration: Duration(milliseconds: 500));
}
#override
_AnimatedTaskListState createState() => _AnimatedTaskListState();
}
class _AnimatedTaskListState extends State<AnimatedTaskList> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: SafeArea(
child: AnimatedList(
key: _listKey,
initialItemCount: listData.length,
itemBuilder:
(BuildContext context, int index, Animation animation) {
return Card(
child: FadeTransition(
opacity: animation,
child: ListTile(
title: Text(listData[index].taskTitle),
subtitle: Text(listData[index].taskDetails),
onLongPress: () {
//todo delete user
},
)));
})),
);
}
}
class TaskModel {
TaskModel({this.taskTitle, this.taskDetails});
String taskTitle;
String taskDetails;
}
List<TaskModel> listData = [
TaskModel(
taskTitle: "Linear Algebra",
taskDetails: "Chapter 4",
),
TaskModel(
taskTitle: "Physics",
taskDetails: "Chapter 9",
),
TaskModel(
taskTitle: "Software Construction",
taskDetails: "Architecture",
),
];
I fixed my issue by moving
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
into my _AnimatedTaskListState class, and adding a constructor and private key to my AnimatedTaskList class
final GlobalKey<AnimatedListState> _key;
AnimatedTaskList(this._key);
#override
_AnimatedTaskListState createState() => _AnimatedTaskListState(_key);
then in my tab bar implementation I changed it to reflect my new constructor
AnimatedTaskList(GlobalKey<AnimatedListState>(debugLabel: "key 1"));
AnimatedTaskList(GlobalKey<AnimatedListState>(debugLabel: "key 2"));
AnimatedTaskList(GlobalKey<AnimatedListState>(debugLabel: "key 3"));
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;
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 have created a Navigation Drawer in Flutter.
Source available here - https://github.com/deadcoder0904/flutter_navigation_drawer
It looks like this -
First Screen
When I click the button, it goes to
Second Screen
When I click the button, it goes to
Tabs Screen
When I click the hamburger icon on First Screen, it goes to
Drawer Screen
Now when I click on the 2nd List Item on Drawer Screen, I get the Second Screen like this
Relevant code is in navigation_drawer.dart which looks like -
class NavigationDrawer extends StatefulWidget {
_NavigationDrawerState createState() => _NavigationDrawerState();
}
class _NavigationDrawerState extends State<NavigationDrawer> {
int _selectionIndex = 0;
final drawerItems = [
DrawerItem("First Screen", Icons.looks_one),
DrawerItem("Second Screen", Icons.looks_two),
DrawerItem("Tabs", Icons.tab),
];
_getDrawerItemScreen(int pos) {
switch (pos) {
case 1:
return SecondScreen();
case 2:
return Tabs();
default:
return FirstScreen();
}
}
_onSelectItem(int index) {
setState(() {
_selectionIndex = index;
_getDrawerItemScreen(_selectionIndex);
});
Navigator.of(context).pop();
}
#override
Widget build(BuildContext context) {
List<Widget> drawerOptions = [];
for (var i = 0; i < drawerItems.length; i++) {
var d = drawerItems[i];
drawerOptions.add(ListTile(
leading: Icon(d.icon),
title: Text(
d.title,
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w400),
),
selected: i == _selectionIndex,
onTap: () => _onSelectItem(i),
));
}
return Scaffold(
appBar: AppBar(
title: Text('First Screen'),
),
drawer: Drawer(
child: Column(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: Text('Akshay Kadam (A2K)'),
accountEmail: Text('a2k#gmail.com'),
),
Column(
children: drawerOptions,
),
],
),
),
body: _getDrawerItemScreen(_selectionIndex),
);
}
}
How do I get the Second Screen without the Hamburger Icon & First Screen title?
First, change your code to set HomePage
body: _getDrawerItemScreen(_selectionIndex),
to
body: FirstScreen(),
Secondly,
_onSelectItem(int index) {
setState(() {
_selectionIndex = index;
_getDrawerItemScreen(_selectionIndex);
});
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => _getDrawerItemScreen(_selectionIndex),
),
);
}
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