Flutter自定义弹出菜单插件custom_pop_up_menu_fork的使用
Flutter自定义弹出菜单插件custom_pop_up_menu_fork的使用
特性
- 手势:点击或长按
- 可自定义菜单:可以设置任何样式的菜单,常见的样式可以在示例代码中查看,只需实现
menuBuilder
- 预设两种样式:gridview 和 listview,支持深色模式和浅色模式
- 自动计算菜单的位置,也支持手动调整
- 修复了原始库中无法隐藏菜单的错误
Demo
使用此插件,你可以自由地构建类似于微信的弹出菜单。
预设样式
menuBuilder: () {
return GridViewPopMenuLight(menuItems: menuItems, dataObj: message.content, controller: menuController);
},
menuBuilder: () => ListViewPopMenuLight(menuItems: menuItems, dataObj: "添加按钮", controller: _controller),
快速使用API
推荐使用此API:
QuickPopUpMenu({
Key? key,
required this.child,
required this.menuItems,
required this.dataObj,
this.darkMode,
this.pressType,
this.useGridView,
this.showArrow,
})
这是一个简单的封装:
Widget build(BuildContext context) {
return CustomPopupMenu(
child: child,
menuBuilder: () {
if(useGridView ?? false) {
if(darkMode ?? false) {
return GridViewPopMenu(menuItems: menuItems, dataObj: dataObj, controller: menuController);
} else {
return GridViewPopMenuLight(menuItems: menuItems, dataObj: dataObj, controller: menuController);
}
} else {
if(darkMode ?? false) {
return ListViewPopMenu(menuItems: menuItems, dataObj: dataObj, controller: menuController);
} else {
return ListViewPopMenuLight(menuItems: menuItems, dataObj: dataObj, controller: menuController);
}
}
},
controller: menuController,
barrierColor: Colors.transparent,
showArrow: showArrow ?? false,
pressType: pressType ?? PressType.singleClick,
verticalMargin: 0,
);
}
使用示例
QuickPopUpMenu(
child: Text(message.content),
pressType: PressType.longPress,
menuItems: menuItems,
useGridView: true,
dataObj: message.content,
)
原始库中的Bug修复
当在listview/gridview/scrollview中使用popmenu时,调用controller.hideMenu()
无法关闭菜单,因为控制器在滚动/重用项目时会改变。
因此,我们不得不通过不同的控制器传递事件:
_updateView() {
if(widget.controller != null && widget.controller != _controller) {
if(!widget.controller!.hasListeners) {
widget.controller!.addListener(() {
_controller!.toggleMenu();
});
}
}
bool menuIsShowing = _controller?.menuIsShowing ?? false;
widget.menuOnChange?.call(menuIsShowing);
if (menuIsShowing) {
_showMenu();
} else {
_hideMenu();
}
}
示例代码
import 'package:custom_pop_up_menu_fork/custom_pop_up_menu.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class ChatModel {
String content;
bool isMe;
ChatModel(this.content, {this.isMe = false});
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'CustomPopupMenu',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
[@override](/user/override)
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late List<ChatModel> messages;
late List<PopMenuItemModel> menuItems;
CustomPopupMenuController _controller = CustomPopupMenuController();
[@override](/user/override)
void initState() {
messages = [
ChatModel('在吗?'),
ChatModel('咋了?找我有事吗?', isMe: true),
ChatModel('没啥就像看看你在不在'),
ChatModel('到底啥事你说啊,我还在工作呢', isMe: true),
ChatModel('?', isMe: true),
ChatModel('下面开始介绍Flutter'),
ChatModel(
'Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。',
),
ChatModel('就这???', isMe: true),
ChatModel('在吗?'),
ChatModel('咋了?找我有事吗?', isMe: true),
ChatModel('没啥就像看看你在不在'),
ChatModel('到底啥事你说啊,我还在工作呢', isMe: true),
ChatModel('?', isMe: true),
ChatModel('下面开始介绍Flutter'),
ChatModel(
'Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。',
),
ChatModel('就这???', isMe: true),
];
menuItems = [
PopMenuItemModel(title: '发起群聊', icon: Icons.chat_bubble, callback: (data) { debugPrint("data: $data"); }),
PopMenuItemModel(title: '添加朋友', icon: Icons.group_add, callback: (data) { debugPrint("data: $data"); }),
PopMenuItemModel(title: '扫一扫', icon: Icons.settings_overscan, callback: (data) { debugPrint("data: $data"); }),
];
super.initState();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CustomPopupMenu'),
actions: <Widget>[
CustomPopupMenu(
child: Container(
child: Icon(Icons.add_circle_outline, color: Colors.white),
padding: EdgeInsets.all(20),
),
menuBuilder: () => ListViewPopMenu(menuItems: menuItems, dataObj: "添加按钮", controller: _controller),
pressType: PressType.singleClick,
verticalMargin: -10,
controller: _controller,
),
],
),
body: LayoutBuilder(
builder: (context, constraint) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraint.maxHeight),
child: Column(
children: messages
.map(
(message) => MessageContent(
message,
),
)
.toList(),
),
),
);
},
),
);
}
}
class MessageContent extends StatelessWidget {
MessageContent(this.message);
final ChatModel message;
List<PopMenuItemModel> menuItems = [
PopMenuItemModel(title: '复制', icon: Icons.content_copy, callback: (data) { debugPrint("data: $data"); }),
PopMenuItemModel(title: '转发', icon: Icons.send, callback: (data) { debugPrint("data: $data"); }),
PopMenuItemModel(title: '收藏', icon: Icons.collections, callback: (data) { debugPrint("data: $data"); }),
PopMenuItemModel(title: '删除', icon: Icons.delete, callback: (data) { debugPrint("data: $data"); }),
PopMenuItemModel(title: '多选', icon: Icons.playlist_add_check, callback: (data) { debugPrint("data: $data"); }),
PopMenuItemModel(title: '引用', icon: Icons.format_quote, callback: (data) { debugPrint("data: $data"); }),
PopMenuItemModel(title: '提醒', icon: Icons.add_alert, callback: (data) { debugPrint("data: $data"); }),
PopMenuItemModel(title: '搜一搜', icon: Icons.search, callback: (data) { debugPrint("data: $data"); }),
];
Widget _buildLongPressMenu(CustomPopupMenuController menuController) {
return ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Container(
width: 220,
color: const Color(0xFF4C4C4C),
child: GridView.count(
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 10),
crossAxisCount: 5,
crossAxisSpacing: 0,
mainAxisSpacing: 10,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: menuItems
.map((item) => GestureDetector(
onTap: () {
debugPrint("click-->$item.title");
menuController.hideMenu();
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
item.icon,
size: 20,
color: Colors.white,
),
Container(
margin: EdgeInsets.only(top: 2),
child: Text(
item.title,
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
],
),
))
.toList(),
),
),
);
}
Widget _buildAvatar(bool isMe, double size) {
return ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Container(
color: isMe ? Colors.blueAccent : Colors.pinkAccent,
width: size,
height: size,
child: Icon(
isMe ? Icons.face : Icons.tag_faces,
color: Colors.white,
),
),
);
}
[@override](/user/override)
Widget build(BuildContext context) {
bool isMe = message.isMe;
double avatarSize = 40;
return Container(
margin: EdgeInsets.all(10),
child: Row(
textDirection: isMe ? TextDirection.rtl : TextDirection.ltr,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(right: isMe ? 0 : 10, left: isMe ? 10 : 0),
child: CustomPopupMenu(
child: _buildAvatar(isMe, avatarSize),
menuBuilder: () => GestureDetector(
child: _buildAvatar(isMe, 100),
onLongPress: () {
debugPrint("onLongPress");
},
onTap: () {
debugPrint("onTap");
},
),
barrierColor: Colors.transparent,
pressType: PressType.singleClick,
arrowColor: isMe ? Colors.blueAccent : Colors.pinkAccent,
position: PreferredPosition.top,
),
),
QuickPopUpMenu(
child: Container(
padding: EdgeInsets.all(10),
constraints: BoxConstraints(maxWidth: 240, minHeight: avatarSize),
decoration: BoxDecoration(
color: isMe ? Color(0xff98e165) : Colors.white,
borderRadius: BorderRadius.circular(3.0),
),
child: Text(message.content),
),
menuBuilder: () => _buildLongPressMenu(menuController),
controller: menuController,
barrierColor: Colors.transparent,
showArrow: false,
pressType: PressType.longPress,
menuItems: menuItems,
useGridView: true,
dataObj: message.content,
)
],
),
);
}
}
更多关于Flutter自定义弹出菜单插件custom_pop_up_menu_fork的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter自定义弹出菜单插件custom_pop_up_menu_fork的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用custom_pop_up_menu_fork
插件的示例代码。这个插件允许你创建自定义的弹出菜单。假设你已经将custom_pop_up_menu_fork
添加到了你的pubspec.yaml
文件中,并且已经运行了flutter pub get
。
1. 添加依赖
首先,确保你的pubspec.yaml
文件中包含以下依赖:
dependencies:
flutter:
sdk: flutter
custom_pop_up_menu_fork: ^最新版本号
2. 导入插件
在你的Dart文件中,导入插件:
import 'package:custom_pop_up_menu_fork/custom_pop_up_menu_fork.dart';
3. 使用插件
下面是一个完整的示例,展示如何在Flutter中使用custom_pop_up_menu_fork
插件:
import 'package:flutter/material.dart';
import 'package:custom_pop_up_menu_fork/custom_pop_up_menu_fork.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// 菜单项点击事件处理函数
void _onMenuItemClicked(String item) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("You clicked: $item")),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Custom Pop Up Menu Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Long press to show custom pop up menu',
),
SizedBox(height: 20),
GestureDetector(
onLongPress: () {
showMenu(
context: context,
position: RelativeRect.fromLTRB(50, 100, 50, 0),
items: [
PopupMenuItem(
child: Text('Item 1'),
value: 'Item 1',
),
PopupMenuItem(
child: Text('Item 2'),
value: 'Item 2',
),
PopupMenuItem(
child: Text('Item 3'),
value: 'Item 3',
),
],
onSelected: _onMenuItemClicked,
);
},
child: Container(
width: 200,
height: 50,
color: Colors.grey.withOpacity(0.5),
alignment: Alignment.center,
child: Text(
'Long Press Here',
style: TextStyle(color: Colors.black),
),
),
),
],
),
),
);
}
}
解释
- 依赖添加:确保在
pubspec.yaml
文件中添加了custom_pop_up_menu_fork
依赖。 - 导入插件:在需要使用的Dart文件中导入插件。
- 创建菜单:
- 使用
GestureDetector
的onLongPress
事件监听长按操作。 - 使用
showMenu
函数显示弹出菜单,并设置菜单项和点击事件处理函数。
- 使用
这个示例展示了如何在Flutter中使用custom_pop_up_menu_fork
插件来创建一个自定义的弹出菜单,并在用户选择菜单项时执行相应的操作。你可以根据需要自定义菜单项的样式和点击事件的处理逻辑。