SideMenuDownSide
该项目是关于一个自定义的 Flutter 抽屉。

结构
该项目使用Dart和Flutter 1.25.0-4.0.pre创建。
SideMenuDownSide类是SideMenuContent的容器。
SideMenuDownSide是一个骨架,是组合其他组件的中心。它写在side_menu_down_side.dart文件中。SideMenuContent包含如何显示菜单项的逻辑。它写在side_menu_content.dart文件中。SideMenuHolder包含一个屏幕列表的DataSource(Widget列表)。它写在side_menu_holder.dart文件中。SideMenuScreenContainer将帮助我们重新布局,触发动画,显示当前屏幕。它写在side_menu_screen_container.dart文件中。
如何使用
⚠️⚠️⚠️ 已过时,即将更新!但我已经为每个重要的代码块添加了注释。
- 更新您的
main.dart。这是您新的main.dart内容
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
);
}
}
- 请查看
lib\side_menu_down_side\side_menu_holder.dart中的SideMenuHolder类(一个单例),
这个类有一个_MenuItem列表。它存储菜单中每个RootScreen的数据。
List<_MenuItem> _menus = ... // This is place where we display menu items.
- 这是我们的
_MenuItem和HeaderInfo类,用于包含SideMenuContent的数据。
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});
}
- 您可以调整
Content(我们的RootScreen,在点击菜单项后显示)的位置、大小。
在side_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;
}
- 您可以在
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,
- 最后,检查示例RootScreen(从SideMenu打开的屏幕,或放置在SideMenu中),如下所示。
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'),
),
),
);
}
}
- 而且,这就是您的子屏幕应用栏的样子。
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'),
),
);
}
}
- 您还可以更改
NavigationCenter类中的导航逻辑,以实现“真实”的推送动画来导航到新屏幕。
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
}
...
}