Instantly share code, notes, and snippets.
Last active
November 15, 2022 17:03
-
Star
12
(12)
You must be signed in to star a gist -
Fork
2
(2)
You must be signed in to fork a gist
-
Save TheHemantKaushik/cad94568ec87f2ced51559e964295600 to your computer and use it in GitHub Desktop.
This Flutter app example show how to use bottom navigation bar with tabs inner navigation. Also, how to handle Android's back button to pop inner screens of tab and double tap bottom navigation item to pop all inner screens of a tab.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// | |
/// Example GIF image: | |
/// https://ibb.co/mbqF72Q | |
/// | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
void main() { | |
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( | |
statusBarColor: Colors.transparent, | |
statusBarBrightness: Brightness.dark, | |
statusBarIconBrightness: Brightness.dark, | |
systemNavigationBarColor: Colors.transparent, | |
systemNavigationBarIconBrightness: Brightness.dark, | |
)); | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'My Flutter App', | |
theme: ThemeData( | |
primarySwatch: Colors.red, | |
canvasColor: Colors.white, | |
), | |
home: DashboardScreen(), | |
); | |
} | |
} | |
class DashboardScreen extends StatefulWidget { | |
@override | |
_DashboardScreenState createState() => _DashboardScreenState(); | |
} | |
class _DashboardScreenState extends State<DashboardScreen> { | |
final _tabNavigator = GlobalKey<TabNavigatorState>(); | |
final _tab1 = GlobalKey<NavigatorState>(); | |
final _tab2 = GlobalKey<NavigatorState>(); | |
final _tab3 = GlobalKey<NavigatorState>(); | |
var _tabSelectedIndex = 0; | |
var _tabPopStack = false; | |
void _setIndex(index) { | |
setState(() { | |
_tabPopStack = _tabSelectedIndex == index; | |
_tabSelectedIndex = index; | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return WillPopScope( | |
onWillPop: () async => !await _tabNavigator.currentState.maybePop(), | |
child: Scaffold( | |
body: TabNavigator( | |
key: _tabNavigator, | |
tabs: <TabItem>[ | |
TabItem(_tab1, PageWithButton(title: 'Audio')), | |
TabItem(_tab2, PageWithButton(title: 'Video')), | |
TabItem(_tab3, PageWithButton(title: 'More')), | |
], | |
selectedIndex: _tabSelectedIndex, | |
popStack: _tabPopStack, | |
), | |
bottomNavigationBar: BottomNavigationBar( | |
currentIndex: _tabSelectedIndex, | |
onTap: _setIndex, | |
items: [ | |
BottomNavigationBarItem( | |
icon: Icon(Icons.audiotrack), | |
title: Text('Audio'), | |
), | |
BottomNavigationBarItem( | |
icon: Icon(Icons.ondemand_video), | |
title: Text('Video'), | |
), | |
BottomNavigationBarItem( | |
icon: Icon(Icons.more_horiz), | |
title: Text('More'), | |
), | |
], | |
), | |
), | |
); | |
} | |
} | |
class PageWithButton extends StatelessWidget { | |
final String title; | |
final int count; | |
const PageWithButton({ | |
Key key, | |
@required this.title, | |
this.count = 0, | |
}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(title), | |
centerTitle: true, | |
), | |
body: Center( | |
child: RaisedButton( | |
child: Text("$title $count"), | |
onPressed: () { | |
Navigator.of(context).push(MaterialPageRoute( | |
builder: (_) => PageWithButton(title: title, count: count + 1), | |
)); | |
}, | |
), | |
), | |
); | |
} | |
} | |
class TabItem { | |
final GlobalKey<NavigatorState> key; | |
final Widget tab; | |
const TabItem(this.key, this.tab); | |
} | |
class TabNavigator extends StatefulWidget { | |
final List<TabItem> tabs; | |
final int selectedIndex; | |
final bool popStack; | |
TabNavigator({ | |
Key key, | |
@required this.tabs, | |
@required this.selectedIndex, | |
this.popStack = false, | |
}) : super(key: key); | |
@override | |
TabNavigatorState createState() => TabNavigatorState(); | |
} | |
class TabNavigatorState extends State<TabNavigator> { | |
/// | |
/// Try to pop widget, return true if popped | |
/// | |
Future<bool> maybePop() { | |
return widget.tabs[widget.selectedIndex].key.currentState.maybePop(); | |
} | |
_popStackIfRequired(BuildContext context) async { | |
if (widget.popStack) { | |
widget.tabs[widget.selectedIndex].key.currentState | |
.popUntil((route) => route.isFirst); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
print('selectedIndex=${widget.selectedIndex}, popStack=${widget.popStack}'); | |
_popStackIfRequired(context); | |
return Stack( | |
children: List.generate(widget.tabs.length, _buildTab), | |
); | |
} | |
Widget _buildTab(int index) { | |
return Offstage( | |
offstage: widget.selectedIndex != index, | |
child: Opacity( | |
opacity: widget.selectedIndex == index ? 1.0 : 0.0, | |
child: Navigator( | |
key: widget.tabs[index].key, | |
onGenerateRoute: (settings) => MaterialPageRoute( | |
settings: settings, | |
builder: (_) => widget.tabs[index].tab, | |
), | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think you'll have to work with Scroll controller.