SideMenuDownSide

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

SideMenuDownSideAnimate

结构

该项目使用DartFlutter 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.
  • 这是我们的_MenuItemHeaderInfo类,用于包含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
  }
...
}

GitHub

https://github.com/quytm2239/SideMenuDownSide