Flutter侧边栏菜单插件flutter_drawer_menu的使用
Flutter侧边栏菜单插件flutter_drawer_menu的使用
侧边栏菜单
侧边栏菜单是一种通常位于页面左侧的菜单,可用于导航或其他功能。菜单通过从主内容区域任何部分滑动来显示。主内容会以视差效果移动。它支持嵌套小部件的水平滚动,并拦截OverscrollNotification以相应地移动菜单。如果小部件宽度大于600dp,它还可以在平板模式下工作。
图片展示
Android | iOS | 自定义菜单背景 |
---|---|---|
![]() |
![]() |
![]() |
平板模式 |
---|
![]() |
使用说明
1. 添加依赖到你的项目pubspec.yaml文件
dependencies:
flutter_drawer_menu: ^0.1.2
运行 flutter packages get
在项目的根目录下。
2. 导入flutter_drawer_menu库
import 'package:flutter_drawer_menu/drawer_menu.dart';
现在你可以在代码中使用 DrawerMenu
作为小部件。
3. 使用DrawerMenu
如果你想要管理 DrawerMenu
的状态或订阅事件,你需要创建一个 DrawerMenuController
。
final _controller = DrawerMenuController();
创建菜单:
DrawerMenu(
controller: _controller,
menu: _buildMenu(),
body: _buildBody(),
);
如果你想为Android配置透明导航栏(与示例相同),你需要调用以下方法(例如,在 initState
中):
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
并设置 SystemUiOverlayStyle
字段:
systemNavigationBarColor: Colors.transparent,
systemNavigationBarContrastEnforced: false
如何管理DrawerMenu:
_controller.open(animated: true);
_controller.close(animated: true);
_controller.toggle(animated: true);
你可以通过控制器订阅 isOpen
, scrollPosition
, isTablet
状态变化事件。
ValueListenableBuilder<bool>(
valueListenable: _controller.isOpenNotifier,
builder: (context, value, _) {
return Text(value ? "open": "closed");
}
)
DrawerMenu属性
属性名 | 类型 | 描述 |
---|---|---|
animationDuration | Duration | 切换动画持续时间(默认300ms) |
tabletModeMinScreenWidth | double | 激活平板模式的最小宽度(显示侧边菜单) |
tabletModeSideMenuWidth | double | 平板模式下的侧边菜单宽度 |
rightMargin | double | 菜单的右边缘距离。默认为70。 |
menuOverlapWidth | double | 菜单覆盖在主体上的宽度。此设置用于菜单装饰(移动阴影和蒙版层)。默认为0。 |
controller | DrawerMenuController? | 控制DrawerMenu行为的工具。它还允许订阅DrawerMenu状态变化的事件。 |
scrimColor | Color? | 设置在抽屉打开时遮蔽主要内容的颜色。默认为Color(0x44ffffff)。 |
scrimBlurEffect | bool | 打开菜单时应用模糊效果。默认为False。 |
shadowColor | Color? | 右侧菜单阴影的颜色。默认为Color(0x22000000)。 |
shadowWidth | double | 右侧菜单阴影的宽度。默认为35。 |
bodyParallaxFactor | double | 当菜单打开时应用于主体的视差效果乘数。0 - 主体与菜单一起移动。1 - 主体保持不动。默认为0.5。 |
useRepaintBoundaries | bool | 使用RepaintBoundary来隔离菜单和主体小部件的渲染以提高重绘性能。默认为True。 |
backgroundColor | Color | 菜单和主体下方的背景颜色。默认为Colors.white。 |
dragMode | DragMode | 拖动模式设置(never, always, onlyFling)。onlyFling - 菜单仅通过手势打开。 |
完整示例
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_drawer_menu/flutter_drawer_menu.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: '侧边栏菜单Demo',
theme: ThemeData.light(useMaterial3: false).copyWith(
appBarTheme: const AppBarTheme(
backgroundColor: Colors.black,
foregroundColor: Colors.white,
systemOverlayStyle: SystemUiOverlayStyle(
// Android部分。
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarContrastEnforced: false,
systemNavigationBarIconBrightness: Brightness.light,
// iOS部分。
// 当Android设置为暗色时,iOS设置为亮色。奇怪。
statusBarBrightness: Brightness.dark,
)),
),
home: const MyHomePage(title: '侧边栏菜单Demo'),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
[@override](/user/override)
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _controller = DrawerMenuController();
/// 如果_selectedContent是偶数,则显示不带滚动且通过滑动手势打开菜单的页面。
/// 否则,显示可滚动列表。
int _selectedContent = 0;
final double _rightMargin = 70.0;
final double _menuOverlapWidth = 20;
[@override](/user/override)
void initState() {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
super.initState();
}
[@override](/user/override)
Widget build(BuildContext context) {
return DrawerMenu(
controller: _controller,
menu: _buildMenu(),
body: _buildBody(),
rightMargin: _rightMargin,
menuOverlapWidth: _menuOverlapWidth,
shadowWidth: _rightMargin + _menuOverlapWidth,
shadowColor: const Color(0x66000000),
dragMode:
_selectedContent % 2 != 0 ? DragMode.always : DragMode.onlyFling,
);
}
Widget _buildMenu() {
final listView = ListView.builder(itemBuilder: (context, index) {
return InkWell(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text("内容 $index"),
),
onTap: () {
_controller.close();
setState(() {
_selectedContent = index;
});
},
);
});
Widget menu = WaveBorder(
waveWidth: _menuOverlapWidth,
child: SafeArea(
child: Material(color: Colors.transparent, child: listView),
));
// 应用状态栏和导航栏主题设置。
// 如果你想为Android配置透明导航栏(与示例相同),你需要调用以下方法(例如,在initState中):
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
// 并设置SystemUiOverlayStyle字段:
// systemNavigationBarColor: Colors.transparent,
// systemNavigationBarContrastEnforced: false,
menu = AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle(
// Android部分。
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarContrastEnforced: false,
systemNavigationBarIconBrightness: Brightness.dark,
// iOS部分。
// 当Android设置为暗色时,iOS设置为亮色。奇怪。
statusBarBrightness: Brightness.light,
),
child: menu,
);
return menu;
}
Widget _buildBody() {
// 菜单按钮订阅菜单模式(平板|手机)的变化。
Widget leadingWidget = ValueListenableBuilder<bool>(
valueListenable: _controller.isTabletModeNotifier,
builder: (context, value, _) {
if (value) {
return const SizedBox();
}
return IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
_controller.open();
},
);
});
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
centerTitle: true,
leading: leadingWidget,
),
body: _buildContent(context, _selectedContent),
);
}
Widget _buildContent(BuildContext context, int index) {
/// PageView部分
Widget pageView = Container(
color: Colors.black12,
height: 150,
child: PageView.builder(
physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) => Center(
child: Text(
"嵌套PageView\n页面 $index",
textAlign: TextAlign.center,
),
),
),
);
/// 内容部分
Widget content = Container(
color: Colors.black.withOpacity(0.05),
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("内容 $_selectedContent"),
// 订阅scrollPosition(0-1)
ValueListenableBuilder<double>(
valueListenable: _controller.scrollPositionNotifier,
builder: (context, value, _) {
return Text(value.toStringAsFixed(2));
}),
// 订阅isOpen
ValueListenableBuilder<bool>(
valueListenable: _controller.isOpenNotifier,
builder: (context, value, _) {
return Text(value ? "打开" : "关闭");
}),
],
),
),
);
if (index % 2 == 0) {
return Column(
children: [pageView, Expanded(child: content)],
);
} else {
return ListView(
children: [
pageView,
for (int i = 0; i < 100; i++) content,
],
);
}
}
}
class WaveBorder extends StatelessWidget {
final Widget child;
final double waveWidth;
const WaveBorder({Key? key, required this.child, required this.waveWidth}) : super(key: key);
[@override](/user/override)
Widget build(BuildContext context) {
return ClipPath(
clipper: WaveClipper(waveWidth: waveWidth),
child: Container(
color: Colors.white,
child: child,
),
);
}
}
class WaveClipper extends CustomClipper<Path> {
final double waveWidth;
WaveClipper({required this.waveWidth});
[@override](/user/override)
Path getClip(Size size) {
final path = Path()
..lineTo(size.width, 0)
..quadraticBezierTo(size.width - waveWidth, size.height * 0.25,
size.width - waveWidth / 2, size.height * 0.5)
..quadraticBezierTo(size.width, size.height * 0.75,
size.width - waveWidth / 2, size.height)
..lineTo(0, size.height)
..close();
return path;
}
[@override](/user/override)
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
更多关于Flutter侧边栏菜单插件flutter_drawer_menu的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter侧边栏菜单插件flutter_drawer_menu的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用flutter_drawer_menu
插件来实现侧边栏菜单的示例代码。请注意,这个插件可能并不是官方或广泛使用的插件,因此示例代码是基于假设该插件存在并具有类似功能的情境下编写的。如果flutter_drawer_menu
插件的具体API有所不同,请根据官方文档进行调整。
首先,确保在pubspec.yaml
文件中添加对flutter_drawer_menu
的依赖:
dependencies:
flutter:
sdk: flutter
flutter_drawer_menu: ^x.y.z # 替换为实际版本号
然后运行flutter pub get
来获取依赖。
接下来是示例代码,展示如何使用flutter_drawer_menu
插件来实现侧边栏菜单:
import 'package:flutter/material.dart';
import 'package:flutter_drawer_menu/flutter_drawer_menu.dart'; // 假设插件提供此导入路径
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Drawer Menu Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Drawer Menu Demo'),
),
body: Center(
child: Text('Home Page Content'),
),
drawer: FlutterDrawerMenu( // 使用FlutterDrawerMenu插件
header: DrawerHeader(
decoration: BoxDecoration(color: Colors.blue),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Drawer Header', style: TextStyle(color: Colors.white)),
),
),
menuItems: [
DrawerMenuItem(
icon: Icons.home,
title: 'Home',
onTap: () {
Navigator.pop(context); // 关闭抽屉菜单
// 可以添加跳转到主页的逻辑
},
),
DrawerMenuItem(
icon: Icons.settings,
title: 'Settings',
onTap: () {
Navigator.pop(context); // 关闭抽屉菜单
// 可以添加跳转到设置页的逻辑
},
),
// 可以添加更多菜单项
],
),
);
}
}
// 假设DrawerMenuItem是插件提供的一个组件,用于表示菜单项
// 如果插件没有提供,可以自己定义一个
class DrawerMenuItem extends StatelessWidget {
final IconData icon;
final String title;
final VoidCallback onTap;
DrawerMenuItem({required this.icon, required this.title, required this.onTap});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(icon),
title: Text(title),
onTap: onTap,
);
}
}
在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个主屏幕和一个使用FlutterDrawerMenu
插件实现的侧边栏菜单。侧边栏菜单包含两个菜单项:“Home”和“Settings”,点击菜单项时会关闭抽屉菜单(通过Navigator.pop(context)
),你可以根据需要添加跳转到相应页面的逻辑。
请注意,由于flutter_drawer_menu
可能不是一个真实存在的插件,因此上述代码中的FlutterDrawerMenu
和DrawerMenuItem
是基于假设的API设计的。如果flutter_drawer_menu
插件的实际API有所不同,请查阅其官方文档并根据实际情况进行调整。如果插件不存在,你可以考虑使用Flutter内置的Drawer
组件来实现类似的功能。