当前位置:网站首页>Flitter imitates wechat long press pop-up copy recall paste collection and other custom customization
Flitter imitates wechat long press pop-up copy recall paste collection and other custom customization
2022-07-26 08:26:00 【Peanut - Summer】

according to https://blog.csdn.net/qq_23756803/article/details/99519441
The code here realizes the function , But there are still problems in many places , Pictures need to be configured , You also need to deal with style arrows , And multiline bug
So I modified the code in many places by myself , Encapsulates a
import 'package:flutter/material.dart';
import 'package:lvsongguo/utils/widget_w_popup/triangle_painter.dart';
const double _kMenuScreenPadding = 8.0;
const List<String> wPopupMenuActions = [
' Copy ',
' forward ',
' Collection ',
' Delete ',
' withdraw ',
' remind ',
' translate ',
' Mark ',
];
class WPopupMenu extends StatefulWidget {
WPopupMenu({
Key key,
@required this.onValueChanged,
@required this.actions,
@required this.child,
this.pressType = PressType.longPress,
this.pageMaxChildCount = 5,
this.backgroundColor = Colors.black,
this.menuWidth = 225,
this.menuHeight = 42,
});
final ValueChanged<int> onValueChanged;
final List<String> actions;
final Widget child;
final PressType pressType; // Click through Long press Or click
final int pageMaxChildCount;
final Color backgroundColor;
final double menuWidth;
final double menuHeight;
@override
_WPopupMenuState createState() => _WPopupMenuState();
}
class _WPopupMenuState extends State<WPopupMenu> {
double width;
double height;
RenderBox button;
RenderBox overlay;
OverlayEntry entry;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((call) {
width = context.size.width;
height = context.size.height;
button = context.findRenderObject();
overlay = Overlay.of(context).context.findRenderObject();
});
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
if (entry != null) {
removeOverlay();
}
return Future.value(true);
},
child: GestureDetector(
child: widget.child,
onTap: () {
if (widget.pressType == PressType.singleClick) {
onTap();
}
},
onLongPress: () {
if (widget.pressType == PressType.longPress) {
onTap();
}
},
onDoubleTap: () {
if (widget.pressType == PressType.doubleClick) {
onTap();
}
},
),
);
}
void onTap() {
Widget menuWidget = _MenuPopWidget(
context,
height,
width,
widget.actions,
widget.pageMaxChildCount,
widget.backgroundColor,
widget.menuWidth,
widget.menuHeight,
button,
overlay,
(index) {
if (index != -1) widget.onValueChanged(index);
removeOverlay();
},
);
entry = OverlayEntry(builder: (context) {
return menuWidget;
});
Overlay.of(context).insert(entry);
}
void removeOverlay() {
entry.remove();
entry = null;
}
}
enum PressType {
// Long press
longPress,
// single click
singleClick,
// double-click
doubleClick,
}
class _MenuPopWidget extends StatefulWidget {
final BuildContext btnContext;
final List<String> actions;
final int _pageMaxChildCount;
final Color backgroundColor;
final double menuWidth;
final double menuHeight;
final double _height;
final double _width;
final RenderBox button;
final RenderBox overlay;
final ValueChanged<int> onValueChanged;
_MenuPopWidget(
this.btnContext,
this._height,
this._width,
this.actions,
this._pageMaxChildCount,
this.backgroundColor,
this.menuWidth,
this.menuHeight,
this.button,
this.overlay,
this.onValueChanged,
);
@override
_MenuPopWidgetState createState() => _MenuPopWidgetState();
}
class _MenuPopWidgetState extends State<_MenuPopWidget> {
int _curPage = 0;
final double _arrowWidth = 40;
final double _separatorWidth = 1;
final double _triangleHeight = 10;
RelativeRect position;
@override
void initState() {
super.initState();
position = RelativeRect.fromRect(
Rect.fromPoints(
widget.button.localToGlobal(Offset.zero, ancestor: widget.overlay),
widget.button.localToGlobal(Offset.zero, ancestor: widget.overlay),
),
Offset.zero & widget.overlay.size,
);
}
@override
Widget build(BuildContext context) {
// It's calculated here Current page child How many in all
int _curPageChildCount = (_curPage + 1) * widget._pageMaxChildCount > widget.actions.length
? widget.actions.length % widget._pageMaxChildCount
: widget._pageMaxChildCount;
double _curArrowWidth = 0;
int _curArrowCount = 0; // There are several arrows
if (widget.actions.length > widget._pageMaxChildCount) {
// Data length greater than widget._pageMaxChildCount
if (_curPage == 0) {
// If it's the first page
_curArrowWidth = _arrowWidth;
_curArrowCount = 1;
} else if ((_curPage + 1) * widget._pageMaxChildCount >= widget.actions.length) {
// If it's not the first page You also need to display the left arrow
_curArrowWidth = _arrowWidth;
_curArrowCount = 2;
} else {
_curArrowWidth = _arrowWidth * 2;
_curArrowCount = 2;
}
}
double _curPageWidth =
widget.menuWidth + (_curPageChildCount - 1 + _curArrowCount) * _separatorWidth + _curArrowWidth;
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
widget.onValueChanged(-1);
},
child: MediaQuery.removePadding(
context: context,
removeTop: true,
removeBottom: true,
removeLeft: true,
removeRight: true,
child: Builder(
builder: (BuildContext context) {
var isInverted = (position.top +
(MediaQuery.of(context).size.height - position.top - position.bottom) / 2.0 -
(widget.menuHeight + _triangleHeight)) <
(widget.menuHeight + _triangleHeight) * 2;
return CustomSingleChildLayout(
delegate: _PopupMenuRouteLayout(position, widget.menuHeight + _triangleHeight,
Directionality.of(widget.btnContext), widget._width, widget.menuWidth, widget._height),
child: SizedBox(
height: widget.menuHeight + _triangleHeight,
width: _curPageWidth,
child: Material(
color: Colors.transparent,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
isInverted
? CustomPaint(
size: Size(_curPageWidth, _triangleHeight),
painter: TrianglePainter(
color: widget.backgroundColor,
position: position,
isInverted: true,
size: widget.button.size,
screenWidth: MediaQuery.of(context).size.width,
),
)
: Container(),
Expanded(
child: Stack(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5)),
child: Container(
color: widget.backgroundColor,
height: widget.menuHeight,
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_curPage == 0
? Container(
height: widget.menuHeight,
)
: InkWell(
onTap: () {
setState(() {
_curPage--;
});
},
child: Container(
width: _arrowWidth,
height: widget.menuHeight,
child: Icon(Icons.arrow_left, color: Colors.white),
),
),
_curPage == 0
? Container(
height: widget.menuHeight,
)
: Container(
width: 1,
height: widget.menuHeight,
color: Colors.grey,
),
_buildList(_curPageChildCount, _curPageWidth, _curArrowWidth, _curArrowCount),
_curArrowCount > 0
? (_curPage + 1) * widget._pageMaxChildCount >= widget.actions.length
? Container(
height: widget.menuHeight,
)
: Container(
width: 1,
color: Colors.grey,
height: widget.menuHeight,
)
: Container(
height: widget.menuHeight,
),
_curArrowCount > 0
? (_curPage + 1) * widget._pageMaxChildCount >= widget.actions.length
? Container(
height: widget.menuHeight,
)
: InkWell(
onTap: () {
if ((_curPage + 1) * widget._pageMaxChildCount < widget.actions.length)
setState(() {
_curPage++;
});
},
child: Container(
width: _arrowWidth,
height: widget.menuHeight,
child: Container(
width: _arrowWidth,
height: widget.menuHeight,
child: Icon(Icons.arrow_right, color: Colors.white),
),
),
)
: Container(
height: widget.menuHeight,
),
],
),
],
),
),
isInverted
? Container()
: CustomPaint(
size: Size(_curPageWidth, _triangleHeight),
painter: TrianglePainter(
color: widget.backgroundColor,
position: position,
size: widget.button.size,
screenWidth: MediaQuery.of(context).size.width,
),
),
],
),
),
),
);
},
),
),
);
}
Widget _buildList(int _curPageChildCount, double _curPageWidth, double _curArrowWidth, int _curArrowCount) {
return ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
itemCount: _curPageChildCount,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
widget.onValueChanged(_curPage * widget._pageMaxChildCount + index);
},
child: SizedBox(
width: (_curPageWidth - _curArrowWidth - (_curPageChildCount - 1 + _curArrowCount) * _separatorWidth) /
_curPageChildCount,
height: widget.menuHeight,
child: Center(
child: Text(
widget.actions[_curPage * widget._pageMaxChildCount + index],
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
);
},
separatorBuilder: (BuildContext context, int index) {
return Container(
width: 1,
height: widget.menuHeight,
color: Colors.grey,
);
},
);
}
}
// Positioning of the menu on the screen.
class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
_PopupMenuRouteLayout(
this.position, this.selectedItemOffset, this.textDirection, this.width, this.menuWidth, this.height);
// Rectangle of underlying button, relative to the overlay's dimensions.
final RelativeRect position;
// The distance from the top of the menu to the middle of selected item.
//
// This will be null if there's no item to position in this way.
final double selectedItemOffset;
// Whether to prefer going to the left or to the right.
final TextDirection textDirection;
final double width;
final double height;
final double menuWidth;
// We put the child wherever position specifies, so long as it will fit within
// the specified parent size padded (inset) by 8. If necessary, we adjust the
// child's position so that it fits.
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
// The menu can be at most the size of the overlay minus 8.0 pixels in each
// direction.
return BoxConstraints.loose(
constraints.biggest - const Offset(_kMenuScreenPadding * 2.0, _kMenuScreenPadding * 2.0));
}
@override
Offset getPositionForChild(Size size, Size childSize) {
// size: The size of the overlay.
// childSize: The size of the menu, when fully open, as determined by
// getConstraintsForChild.
// Find the ideal vertical position.
double y;
if (selectedItemOffset == null) {
y = position.top;
} else {
y = position.top + (size.height - position.top - position.bottom) / 2.0 - selectedItemOffset;
}
// Find the ideal horizontal position.
double x;
// If menu Width Less than child Width , Then directly put menu Put it in child middle
if (childSize.width < width) {
x = position.left + (width - childSize.width) / 2;
} else {
// If you keep to the right
if (position.left > size.width - (position.left + width)) {
if (size.width - (position.left + width) > childSize.width / 2 + _kMenuScreenPadding) {
x = position.left - (childSize.width - width) / 2;
} else {
x = position.left + width - childSize.width;
}
} else if (position.left < size.width - (position.left + width)) {
if (position.left > childSize.width / 2 + _kMenuScreenPadding) {
x = position.left - (childSize.width - width) / 2;
} else
x = position.left;
} else {
x = position.right - width / 2 - childSize.width / 2;
}
}
if (y < _kMenuScreenPadding)
y = _kMenuScreenPadding;
else if (y + childSize.height > size.height - _kMenuScreenPadding)
y = size.height - childSize.height;
else if (y < childSize.height * 2) {
y = position.top + height;
}
return Offset(x, y);
}
@override
bool shouldRelayout(_PopupMenuRouteLayout oldDelegate) {
return position != oldDelegate.position;
}
}
边栏推荐
- 2022/7/11 exam summary
- Basic music theory rhythm connection problem, very important
- 2022 national vocational college skills competition "network security" competition question file upload penetration test answer flag
- 数组的介绍--Array
- 2022-7-6 personal qualifying 3 competition experience
- NLP (natural language processing) natural language processing learning
- 22-07-14 personal training match 2 competition experience
- Burp suite Chapter 4 advanced options for SSL and proxy
- 2022/7/1
- Dev gridcontrol 捕获按键事件
猜你喜欢

2022-7-5 personal qualifying 2 competition experience

Burp suite Chapter 6 how to use burp spider

全网最全:Mysql六种约束详解

Code cloud change remote warehouse command

Nodejs2day(nodejs的模块化,npm下载包,模块加载机制)

Burp Suite-第三章 如何使用Burp Suite代理

mysql函数汇总之日期和时间函数

Guitar staff link Jasmine
![[GUI] swing package (window, pop-up window, label, panel, button, list, text box)](/img/05/8e7483768a4ad2036497cac136b77d.png)
[GUI] swing package (window, pop-up window, label, panel, button, list, text box)

C# 获取选择文件信息
随机推荐
外卖小哥,才是这个社会最大的托底
How WPS sets page headers page by page
Write common API tools swagger and redoc
Summary of common skills
Burp suite Chapter 5 how to use burp target
Dev gridcontrol 捕获按键事件
Guitar staff link Jasmine
vim跨行匹配搜索
Add in the registry right click to open in vscode
Flex three column layout
Burp Suite-第一章 Burp Suite 安装和环境配置
全网最全:Mysql六种约束详解
Seq2seq and attention model learning notes
2022/7/9 exam summary
Software engineering -- dental clinic -- demand acquisition
SPSS用KMeans、两阶段聚类、RFM模型在P2P网络金融研究借款人、出款人行为规律数据
OSPF总结
Bee guitar score high octave and low octave
The first ide overlord in the universe, replaced...
2022/7/12 exam summary