赞
踩
使用到的第三方库:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
get: ^4.6.5
flutter_zoom_drawer: ^3.0.4+1
参考资料:https://www.youtube.com/watch?v=Evu19gTKaFo&t=187s
建议先看视频。
其中,只有get和flutter_zoom_drawer是手动引入的,get用于状态管理,而 flutter_zoom_drawer就是实现动画抽屉效果的核心。
纯粹为了方便,我就把所有类都写到main.dart中了。(文末有源码)
首先构建四个基本的界面:
1、MyHomePage:用于承载各个页面;
2、MenuScreen:用于显示抽屉菜单;
3、MainScreen:抽屉菜单对应的界面一;
4、SecondScreen:抽屉菜单对应的界面二;
用最终的效果来说明各个界面的位置:
点击menu1就进入MainScreen;
点击menu2就进入SecondScreen;
(如果要让背景颜色统一,只需要修改一下对应界面的backgroundcolor即可。)
接下来开始代码部分:
MyHomePage作为承载页面的页面,要把它设置成APP的入口:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Zoom Drawer Demo',
home: MyHomePage(),
);
}
}
MyHomePage:
class MyHomePage extends StatelessWidget { MyDrawerController controller = Get.put(MyDrawerController()); @override Widget build(BuildContext context) { return GetBuilder<MyDrawerController>( builder: (_) => ZoomDrawer( //第三方库自带的属性 controller: _.zoomDrawerController, //getScreen方法见下方 mainScreen: getScreen(_.currentItem), menuScreen: Builder(builder: (context) { return MenuScreen( //构造函数需要一个currentItem,用于menuScreen的被选中项目的高亮(就是图标为绿色状态) currentItem: _.currentItem, ///还需要一个回调函数,用于更改要显示的页面,在menuScreen中点击某个菜单选项后,调用该回调,currentItem的值 ///被更新,由于setCurrentItem方法中手动调用了update(),界面会重新渲染,经过getScreen(_.currentItem)后,渲染currentItem指向的界面。 onItemSelected: (item) { _.setCurrentItem(item); //切换后需要手动调用方法关闭抽屉 ZoomDrawer.of(context)!.close(); }); }), //以下这些设置样式的属性可参考官方文档 borderRadius: 25.0, angle: 0, slideWidth: MediaQuery.of(context).size.width * 0.65, ), ); } //因为只有两个页面需要切换,所以就用if-else了,更多页面需要Switch去判断。 Widget getScreen(MenuItem item) { if (item == MenuItems.menu1) { return MainScreen(); } else { return SecondScreen(); } } }
注意一个小细节:
上面代码中有一行, menuScreen: Builder(builder: (context) {
之所以要用Builder额外包裹一层,是因为保证context的正确指向,必不可少
自定义menu菜单的样式:
class MenuItem {
String title;
IconData icon;
MenuItem(this.title, this.icon);
}
class MenuItems {
static MenuItem menu1 = MenuItem("menu1", Icons.info_outline);
static MenuItem menu2 = MenuItem("menu2", Icons.info_outline);
static var menuList = [menu1, menu2];
}
MainScreen,SecondScreen基本是一样的,直接贴代码了:
class MainScreen extends StatelessWidget { MyDrawerController controller = Get.put(MyDrawerController()); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey, body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ ElevatedButton( // onPressed: controller.toggleDrawer, onPressed: (){ controller.toggleDrawer(); }, child: Text("MainScreen的打开抽屉按钮"), ), ], ), ), ); } } class SecondScreen extends StatelessWidget { MyDrawerController controller = Get.put(MyDrawerController()); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ ElevatedButton( onPressed: controller.toggleDrawer, child: Text("SecondScreen的打开抽屉按钮"), ), ], ), ), ); } }
最后是控制器方法:
class MyDrawerController extends GetxController { MenuItem currentItem = MenuItems.menu1; //主要是使用其中的toogle(),close()方法,分别对应打开、关闭Drawer。 final zoomDrawerController = ZoomDrawerController(); //用于打开抽屉 void toggleDrawer() { zoomDrawerController.toggle!(); //注意一定要手动调用update!! update(); } //用于MenuScreen的选中状态回显 void setCurrentItem(MenuItem item) { currentItem = item; //注意一定要手动调用update!! update(); } }
最后的最后是完整代码:
import 'package:drawer_demo/teacher.dart'; import 'package:flutter/material.dart'; import 'package:flutter_zoom_drawer/config.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Zoom Drawer Demo', home: MyHomePage(), ); } } class MenuItem { String title; IconData icon; MenuItem(this.title, this.icon); } class MenuItems { static MenuItem menu1 = MenuItem("menu1", Icons.info_outline); static MenuItem menu2 = MenuItem("menu2", Icons.info_outline); static var menuList = [menu1, menu2]; } class MyHomePage extends StatelessWidget { MyDrawerController controller = Get.put(MyDrawerController()); @override Widget build(BuildContext context) { return GetBuilder<MyDrawerController>( builder: (_) => ZoomDrawer( //第三方库自带的属性 controller: _.zoomDrawerController, //getScreen方法见下方 mainScreen: getScreen(_.currentItem), menuScreen: Builder(builder: (context) { return MenuScreen( //构造函数需要一个currentItem,用于menuScreen的被选中项目的高亮(就是图标为绿色状态) currentItem: _.currentItem, ///还需要一个回调函数,用于更改要显示的页面,在menuScreen中点击某个菜单选项后,调用该回调,currentItem的值 ///被更新,由于setCurrentItem方法中手动调用了update(),界面会重新渲染,经过getScreen(_.currentItem)后,渲染currentItem指向的界面。 onItemSelected: (item) { _.setCurrentItem(item); //切换后需要手动调用方法关闭抽屉 ZoomDrawer.of(context)!.close(); }); }), //以下这些设置样式的属性可参考官方文档 borderRadius: 25.0, angle: 0, slideWidth: MediaQuery.of(context).size.width * 0.65, ), ); } //因为只有两个页面需要切换,所以就用if-else了,更多页面需要Switch去判断。 Widget getScreen(MenuItem item) { if (item == MenuItems.menu1) { return MainScreen(); } else { return SecondScreen(); } } } class MenuScreen extends StatelessWidget { final MenuItem currentItem; final ValueChanged<MenuItem> onItemSelected; const MenuScreen( {Key? key, required this.currentItem, required this.onItemSelected}) : super(key: key); Widget buildMenuItem(MenuItem item) { return ListTile( selectedTileColor: Colors.lightGreen, selectedColor: Colors.lightGreen, selected: currentItem == item, leading: Icon(item.icon), title: Text( item.title, style: TextStyle(fontSize: 25, color: Colors.black), ), onTap: () { onItemSelected(item); print("in MenuScreen: " + item.title); }, ); } @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(top: 30), color: Colors.white, child: Center( child: Column( children: [ ...MenuItems.menuList.map((item) => buildMenuItem(item)).toList(), ], ), ), ); } } class MainScreen extends StatelessWidget { MyDrawerController controller = Get.put(MyDrawerController()); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey, body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ ElevatedButton( // onPressed: controller.toggleDrawer, onPressed: (){ controller.toggleDrawer(); }, child: Text("MainScreen的打开抽屉按钮"), ), ], ), ), ); } } class SecondScreen extends StatelessWidget { MyDrawerController controller = Get.put(MyDrawerController()); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ ElevatedButton( onPressed: controller.toggleDrawer, child: Text("SecondScreen的打开抽屉按钮"), ), ], ), ), ); } } class MyDrawerController extends GetxController { MenuItem currentItem = MenuItems.menu1; //主要是使用其中的toogle(),close()方法,分别对应打开、关闭Drawer。 final zoomDrawerController = ZoomDrawerController(); //用于打开抽屉 void toggleDrawer() { zoomDrawerController.toggle!(); //注意一定要手动调用update!! update(); } //用于MenuScreen的选中状态回显 void setCurrentItem(MenuItem item) { currentItem = item; //注意一定要手动调用update!! update(); } }
不足的地方:
1、每个page都new了一个新的 MyDrawerController实例,有点浪费;
2、没有使用Obx()、GetX等等去包裹需要响应式渲染的部分,不知道性能会不会有问题;
当然,这只是demo,正式使用时要考虑更多。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。