A customized Side Menu DownSide with Flutter
SideMenuDownSide
This project is about a Customized Flutter Drawer.
Structure
This project is created with Dart
and Flutter 1.25.0-4.0.pre
.
Class SideMenuDownSide
is container of SideMenuContent
.
SideMenuDownSide
is a skeleton, hub to combine another component together. It is written fileside_menu_down_side.dart
SideMenuContent
contains logic about how to displayMenu Items
. It is written in fileside_menu_content.dart
SideMenuHolder
contains aDataSource
for screen list (a list of Widget). It is written in fileside_menu_holder.dart
SideMenuScreenContainer
will help us to relayout, trigger animation, display current screen. It is written in fileside_menu_screen_container.dart
How to use
⚠️⚠️⚠️ Outdated, will be updated soon! But I already added comment for each important block of codes.
- Update your
main.dart
. Here is your newmain.dart
content:
import 'package:flutter/material.dart';
import 'package:side_menu_down_side/navigation_center.dart';
import 'package:side_menu_down_side/side_menu_down_side/side_menu_down_side.dart';
// Another your desired import ...
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
// Update appContext for later usage
NavigationCenter.shared.appContext = context;
return MaterialApp(
title: 'SideMenu DownSide',
theme: ThemeData(
primarySwatch: Colors.green,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: SideMenuDownSide(), // <--- Here is our SideMenuDownSide
);
}
}
- Take a look at class
SideMenuHolder
(a singleton) inlib\side_menu_down_side\side_menu_holder.dart
,
this class has a list of_MenuItem
. That stores data of eachRootScreen
in menu.
List<_MenuItem> _menus = ... // This is place where we display menu items.
- Here is our
_MenuItem
andHeaderInfo
class to contain data ofSideMenuContent
class _MenuItem {
final String name; // Menu item's name
final IconData icon; // Icon of Menu item
final bool isHeader;
final Widget rootScreen; // Scren to be displayed (A Scaffold - Widget)
_MenuItem({this.name, this.icon, this.isHeader, this.rootScreen});
}
class HeaderInfo {
final IconData image;
final String name;
final String subInfo;
HeaderInfo({this.image, this.name, this.subInfo});
}
- You can adjust the
position, size
ofContent
(ourRootScreen
, that is displayed after click menu item).
In fileside_menu_down_side.dart
// These functions will be called every time widget(content/screen) has to be re-rendered
Matrix4 _getTransform(BuildContext ctx) {
var width = MediaQuery.of(context).size.width; // screenWidth
// if menu is displayed, move content to the RIGHT by 40% of screen width
_transform =
Matrix4.translationValues(width * (_isMenuOpened ? 0.4 : 0), 0.0, 0.0);
return _transform;
}
double _getHeight(BuildContext ctx) {
var height = MediaQuery.of(context).size.height;
// if menu is displayed, scale down content's height (of RootScreen) to 75% of screen height (original)
_height = height * (_isMenuOpened ? 0.75 : 1);
return _height;
}
double _getWidth(BuildContext ctx) {
var width = MediaQuery.of(context).size.width;
// if menu is displayed, scale down content's width (of RootScreen) to 75% of screen width (original)
_width = width * (_isMenuOpened ? 0.75 : 1);
return _width;
}
- You can adjust the Open-Close animation speed in
side_menu_down_side.dart
:
AnimatedContainer(
// Use the properties stored in the State class.
width: _getWidth(context),
height: _getHeight(context),
transform: _getTransform(context),
// Define how long the animation should take.
duration: Duration(milliseconds: 500), // <-- Here is animation time
// Provide an optional curve to make the animation feel smoother.
curve: Curves.fastOutSlowIn,
- Finally, check the example RootScreen(The screen which is opened from SideMenu, or placed in SideMenu), like below:
class RootScreen1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Update current text (a "context" of root screen) to perform a navigation like real drawer
NavigationCenter.shared.currentContext = context; // <- Here to keep a refer to your current "Root Screen"'s Build Context for later usage
return Scaffold(
appBar: AppBar(
leading:
// You can make this button become a Customized Button for Appbar
FlatButton(
onPressed: () {
SideMenuHolder.shared.onMenuButtonClickListener(); // <- You can call this function anywhere to open the SideMenu
},
child: Icon(
Icons.menu,
color: Colors.white,
),
),
title: Text('RootScreen1'),
titleSpacing: 0,
),
body: Center(
child: FlatButton(
onPressed: () {
NavigationCenter.shared.navigate(SubScreen1()); // <- This is how I navigate in this project
},
child: Text('Screen 1'),
),
),
);
}
}
- And, this is how your SubScreen Appbar is:
class SubScreen1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: FlatButton(
onPressed: () {
NavigationCenter.shared.back(); // <- This how I navigate back, or pop,... in this project
},
child: Icon(
Icons.arrow_back,
color: Colors.white,
),
),
title: Text('Subscreen1'),
),
body: Center(
child: Text('Sub Screen 1'),
),
);
}
}
- You can also change the logic of Navigation in class NavigationCenter to has "real" push animation when navigate to new screen.
import 'package:flutter/cupertino.dart'; // for this CupertinoPageRoute
import 'package:flutter/material.dart'; // for this MaterialPageRoute
class NavigationCenter {
BuildContext appContext;
BuildContext currentContext;
StatefulWidget currentScreen;
void navigate(Widget newScreen, [bool fromAppContext = false]) {
if (currentContext == null) return;
Navigator.push(
fromAppContext ? appContext : (currentContext ?? appContext),
// MaterialPageRoute(builder: (context) => newScreen), // (1) - Slide Upward
CupertinoPageRoute(builder: (context) => newScreen)); // (2) - Push Left
}
...
}