Skip to content

Instantly share code, notes, and snippets.

@TheHemantKaushik
Last active November 15, 2022 17:03
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save TheHemantKaushik/cad94568ec87f2ced51559e964295600 to your computer and use it in GitHub Desktop.
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.
///
/// 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,
),
),
),
);
}
}
@mopilo
Copy link

mopilo commented Aug 25, 2019

editing textfield always pop the stack. any solution?

@NikhilVadoliya
Copy link

I want double tap to scroll top in listview. How it possiable?

@mopilo
Copy link

mopilo commented Sep 13, 2019

I think you'll have to work with Scroll controller.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment