Skip to content

Instantly share code, notes, and snippets.

@Ahmadre
Created February 17, 2020 13:27
Show Gist options
  • Save Ahmadre/c0894a8d7d68fd0ec2daa2caa2879194 to your computer and use it in GitHub Desktop.
Save Ahmadre/c0894a8d7d68fd0ec2daa2caa2879194 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
const _tabletBreakpoint = 720.0;
const _desktopBreakpoint = 1440.0;
const _minHeight = 400.0;
const _tabletSpacingVertical = 15.0;
const _tabletSpacingHorizontial = 10.0;
const _drawerWidth = 304.0;
class NavigationRail extends StatelessWidget {
final FloatingActionButton floatingActionButton;
final int currentIndex;
final Widget body;
final Widget title;
final ValueChanged<int> onTap;
final List<BottomNavigationBarItem> tabs;
final WidgetBuilder drawerHeaderBuilder, drawerFooterBuilder;
final Color bottomNavigationBarColor;
final double tabletBreakpoint, desktopBreakpoint, minHeight, drawerWidth;
final List<Widget> actions;
final BottomNavigationBarType bottomNavigationBarType;
final Color bottomNavigationBarSelectedColor,
bottomNavigationBarUnselectedColor;
final bool isDense;
const NavigationRail({
Key key,
@required this.currentIndex,
@required this.tabs,
@required this.onTap,
this.actions,
this.isDense = false,
this.floatingActionButton,
this.drawerFooterBuilder,
this.drawerHeaderBuilder,
this.body,
this.title,
this.bottomNavigationBarColor,
this.tabletBreakpoint = _tabletBreakpoint,
this.desktopBreakpoint = _desktopBreakpoint,
this.drawerWidth = _drawerWidth,
this.bottomNavigationBarType = BottomNavigationBarType.fixed,
this.bottomNavigationBarSelectedColor,
this.bottomNavigationBarUnselectedColor,
this.minHeight = _minHeight,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: Directionality.of(context),
child: LayoutBuilder(
builder: (_, dimens) {
final _direction = Directionality.of(context);
final isRtl = _direction == TextDirection.rtl;
if (dimens.maxWidth >= desktopBreakpoint &&
dimens.maxHeight > minHeight) {
return Material(
color: Theme.of(context).scaffoldBackgroundColor,
child: Row(
children: <Widget>[
Container(
width: _drawerWidth,
child: _buildDrawer(context, true),
),
Expanded(
child: Stack(
alignment: Alignment.topCenter,
children: <Widget>[
Positioned.fill(
child: Scaffold(
appBar: AppBar(
title: title,
actions: actions,
automaticallyImplyLeading: false,
),
body: body,
),
),
if (floatingActionButton != null) ...[
Positioned(
top: kToolbarHeight - kToolbarHeight / 2,
right: isRtl ? null : kToolbarHeight / 2,
left: !isRtl ? null : kToolbarHeight / 2,
child: floatingActionButton,
width: 50,
height: 50,
)
],
],
),
),
],
),
);
}
if (dimens.maxWidth >= tabletBreakpoint &&
dimens.maxHeight > minHeight) {
return Scaffold(
appBar: AppBar(
title: title,
actions: actions,
automaticallyImplyLeading: true,
),
drawer: drawerHeaderBuilder != null || drawerFooterBuilder != null
? _buildDrawer(context, false)
: null,
body: Row(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
if (floatingActionButton != null) ...[
Padding(
padding: const EdgeInsets.symmetric(
vertical: _tabletSpacingVertical,
horizontal: _tabletSpacingHorizontial,
),
child: floatingActionButton,
),
],
for (var tab in tabs) ...[
Padding(
padding: const EdgeInsets.symmetric(
vertical: _tabletSpacingVertical,
horizontal: _tabletSpacingHorizontial,
),
child: _buildTab(currentIndex == tabs.indexOf(tab),
context, tab),
),
],
],
),
),
Expanded(child: body),
],
),
);
}
return Scaffold(
appBar: AppBar(
title: title,
actions: actions,
automaticallyImplyLeading: true,
),
drawer: drawerHeaderBuilder != null || drawerFooterBuilder != null
? _buildDrawer(context, false)
: null,
body: body,
floatingActionButton: floatingActionButton,
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
bottomNavigationBar: BottomNavigationBar(
type: bottomNavigationBarType,
backgroundColor: bottomNavigationBarColor,
currentIndex: currentIndex,
onTap: onTap,
items: tabs,
unselectedItemColor: bottomNavigationBarUnselectedColor,
selectedItemColor: bottomNavigationBarSelectedColor,
),
);
},
),
);
}
Widget _buildTab(
bool selected, BuildContext context, BottomNavigationBarItem item) {
final _theme = Theme.of(context);
final _isDark = _theme.brightness == Brightness.dark;
final _color = selected
? _isDark ? Colors.tealAccent[200] : _theme.primaryColor
: Colors.grey;
final _iconTheme = IconThemeData(
color: _color,
size: _theme.iconTheme.size,
opacity: _theme.iconTheme.opacity,
);
final _icon = Align(
alignment: Alignment.topCenter,
heightFactor: 1.0,
child: IconTheme(
data: _iconTheme,
child: selected ? item.activeIcon : item.icon,
),
);
if (isDense) {
return InkWell(
onTap: () => onTap(tabs.indexOf(item)),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: _icon,
),
);
}
return InkWell(
onTap: () => onTap(tabs.indexOf(item)),
child: Column(
children: <Widget>[
_icon,
Container(height: 4.0),
DefaultTextStyle(
style: TextStyle(color: _color),
child: item?.title,
),
],
),
);
}
Widget _buildDrawer(BuildContext context, bool showTabs) {
return Drawer(
child: SafeArea(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
if (drawerHeaderBuilder != null) ...[
drawerHeaderBuilder(context),
],
if (showTabs) ...[
for (var tab in tabs) ...[
ListTile(
dense: isDense,
selected: currentIndex == tabs.indexOf(tab),
leading: tab?.icon,
title: tab?.title,
onTap: () => onTap(tabs.indexOf(tab)),
),
]
],
if (drawerFooterBuilder != null) ...[
drawerFooterBuilder(context),
],
],
),
),
),
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'NavigationRail Demo',
theme: _theme(ThemeData.light().copyWith(
accentColor: Colors.red,
)),
home: MyHomePage(title: 'Navigation Rail Demo'),
);
}
ThemeData _theme(ThemeData base) {
return ThemeData(
primarySwatch: Colors.blue,
appBarTheme: base.appBarTheme.copyWith(elevation: 0.0),
floatingActionButtonTheme: base.floatingActionButtonTheme.copyWith(
elevation: 2.0,
backgroundColor: base.accentColor,
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return NavigationRail(
drawerHeaderBuilder: (context) {
return Column(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: Text("Steve Jobs"),
accountEmail: Text("jobs@apple.com"),
),
],
);
},
drawerFooterBuilder: (context) {
return Column(
children: <Widget>[
ListTile(
leading: Icon(Icons.settings),
title: Text("Settings"),
),
ListTile(
leading: Icon(Icons.info_outline),
title: Text("About"),
),
],
);
},
currentIndex: _currentIndex,
onTap: (val) {
if (mounted)
setState(() {
_currentIndex = val;
});
},
title: Text(widget.title),
body: IndexedStack(
index: _currentIndex,
children: <Widget>[
Container(color: Colors.blue[300]),
Container(color: Colors.red[300]),
Container(color: Colors.purple[300]),
Container(color: Colors.grey[300]),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: Icon(Icons.add),
),
tabs: <BottomNavigationBarItem>[
BottomNavigationBarItem(
title: Text("Folders"),
icon: Icon(Icons.folder),
),
BottomNavigationBarItem(
title: Text("History"),
icon: Icon(Icons.history),
),
BottomNavigationBarItem(
title: Text("Gallery"),
icon: Icon(Icons.photo_library),
),
BottomNavigationBarItem(
title: Text("Camera"),
icon: Icon(Icons.camera),
),
],
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment