Isa*_*lla 3 flutter flutter-layout
我正在开发一个需要根据点击坐标定位的自定义叠加层的应用程序。然后,覆盖层应填充该位置的其余空间。填充空间方向基于从单击点到顶部或底部的距离,使用距离较大的方向,并且应在其表面外部单击时关闭。
我已经查看了Overlay Widget,但是我似乎不明白如何使用它来实现所描述的属性。
到目前为止,我已经创建了一个 StatefulWidget,其中包含 Overlay 描述。
class ScriptureDisplay extends StatefulWidget {
@override
_ScriptureDisplayState createState() => _ScriptureDisplayState();
}
class _ScriptureDisplayState extends State<ScriptureDisplay> {
OverlayEntry _overlayEntry;
OverlayEntry _createOverlayEntry() {
RenderBox renderBox = context.findRenderObject();
var size = renderBox.size;
var offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (context) => Positioned(
left: offset.dx,
top: offset.dy + size.height + 5.0,
width: size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
nip(),
body(context, offset.dy),
],
),
));
}
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("Click Me"),
onPressed: (){
this._overlayEntry = this._createOverlayEntry();
Overlay.of(context).insert(this._overlayEntry);
},
);
}
Widget body(BuildContext context, double offset) {
return Material(
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
// color: this.color,
elevation: 4.0,
child: Container(
width: MediaQuery.of(context).size.width,
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children: ["First", "Second", "Third"]
.map((String s) => ListTile(
subtitle: Text(s),
))
.toList(growable: false),
),
),
);
}
Widget nip() {
return FittedBox(
child: Container(
height: 20.0,
width: 20.0,
margin: EdgeInsets.only(left:10),
child: CustomPaint(
painter: OpenPainter(),
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
OverlayEntry
确实是实现你提到的一个不错的选择。您可以包装您的小部件,它应该在例如长按时创建此覆盖,并带有GestureRecognizer
. 许多回调可以与这个小部件一起使用,它也包含像这样的位置LongPressStartDetails
。OverlayEntry
您可以在那里计算基于globalPosition
设备尺寸 ( )的位置和尺寸MediaQuery.of(context).size
。
提示:要删除OverlayEntry
您必须调用remove()
小OverlayEntry
部件本身!所以保留对它的引用。
详细解答
在对评论中的回复进行一些拼凑之后,我想出了以下解决方案。
我使用了一个Stack Widget,其初始容器的最大宽度和高度包裹在GestureDetector中,以检测实际小部件之外的任何点击。单击此容器将调用_overlayEntry.remove();
来关闭叠加层。
然后,我将点击小部件与屏幕中心的距离进行比较,以确定叠加层应该位于其上方还是下方。
我最终使用了一个Positioned Widget ,其顶部经过计算以适合所选的 Overlay 位置。最后我计算了覆盖体高度为从水龙头到屏幕末端的距离。
enum OVERLAY_POSITION { TOP, BOTTOM }
class ScriptureDisplay extends StatefulWidget {
@override
_ScriptureDisplayState createState() => _ScriptureDisplayState();
}
class _ScriptureDisplayState extends State<ScriptureDisplay> {
TapDownDetails _tapDownDetails;
OverlayEntry _overlayEntry;
OVERLAY_POSITION _overlayPosition;
double _statusBarHeight;
double _toolBarHeight;
OverlayEntry _createOverlayEntry() {
RenderBox renderBox = context.findRenderObject();
var size = renderBox.size;
var offset = renderBox.localToGlobal(Offset.zero);
var globalOffset = renderBox.localToGlobal(_tapDownDetails.globalPosition);
_statusBarHeight = MediaQuery.of(context).padding.top;
// TODO: Calculate ToolBar Height Using MediaQuery
_toolBarHeight = 50;
var screenHeight = MediaQuery.of(context).size.height;
var remainingScreenHeight = screenHeight - _statusBarHeight - _toolBarHeight;
if (globalOffset.dy > remainingScreenHeight / 2) {
_overlayPosition = OVERLAY_POSITION.TOP;
} else {
_overlayPosition = OVERLAY_POSITION.BOTTOM;
}
return OverlayEntry(builder: (context) {
return Stack(
children: <Widget>[
GestureDetector(
onTap: () {
_overlayEntry.remove();
},
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.blueGrey.withOpacity(0.1),
),
),
Positioned(
left: 10,
top: _overlayPosition == OVERLAY_POSITION.TOP
? _statusBarHeight + _toolBarHeight
: offset.dy + size.height - 5.0,
width: MediaQuery.of(context).size.width - 20,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// ignore: sdk_version_ui_as_code
if (_overlayPosition == OVERLAY_POSITION.BOTTOM)
nip(),
body(context, offset.dy),
// ignore: sdk_version_ui_as_code
if (_overlayPosition == OVERLAY_POSITION.TOP)
nip(),
],
),
)
],
);
});
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 400, left: 10, right: 100),
child: GestureDetector(
child: Text("C"),
onTapDown: (TapDownDetails tapDown) {
setState(() {
_tapDownDetails = tapDown;
});
this._overlayEntry = this._createOverlayEntry();
Overlay.of(context).insert(this._overlayEntry);
},
),
);
}
Widget body(BuildContext context, double offset) {
return Material(
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
elevation: 4.0,
child: Container(
width: MediaQuery.of(context).size.width,
height: _overlayPosition == OVERLAY_POSITION.BOTTOM
? MediaQuery.of(context).size.height -
_tapDownDetails.globalPosition.dy -
20
: _tapDownDetails.globalPosition.dy -
_toolBarHeight -
_statusBarHeight -
15,
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children: [
"First",
"Second",
"Third",
"First",
"Second",
"Third",
"First",
"Second",
"Third"
]
.map((String s) => ListTile(
subtitle: Text(s),
))
.toList(growable: false),
),
),
);
}
Widget nip() {
return Container(
height: 10.0,
width: 10.0,
margin: EdgeInsets.only(left: _tapDownDetails.globalPosition.dx),
child: CustomPaint(
painter: OpenPainter(_overlayPosition),
),
);
}
}
class OpenPainter extends CustomPainter {
final OVERLAY_POSITION overlayPosition;
OpenPainter(this.overlayPosition);
@override
void paint(Canvas canvas, Size size) {
switch (overlayPosition) {
case OVERLAY_POSITION.TOP:
var paint = Paint()
..style = PaintingStyle.fill
..color = Colors.white
..isAntiAlias = true;
_drawThreeShape(canvas,
first: Offset(0, 0),
second: Offset(20, 0),
third: Offset(10, 15),
size: size,
paint: paint);
break;
case OVERLAY_POSITION.BOTTOM:
var paint = Paint()
..style = PaintingStyle.fill
..color = Colors.white
..isAntiAlias = true;
_drawThreeShape(canvas,
first: Offset(15, 0),
second: Offset(0, 20),
third: Offset(30, 20),
size: size,
paint: paint);
break;
}
canvas.save();
canvas.restore();
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
void _drawThreeShape(Canvas canvas,
{Offset first, Offset second, Offset third, Size size, paint}) {
var path1 = Path()
..moveTo(first.dx, first.dy)
..lineTo(second.dx, second.dy)
..lineTo(third.dx, third.dy);
canvas.drawPath(path1, paint);
}
void _drawTwoShape(Canvas canvas,
{Offset first, Offset second, Size size, paint}) {
var path1 = Path()
..moveTo(first.dx, first.dy)
..lineTo(second.dx, second.dy);
canvas.drawPath(path1, paint);
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
10092 次 |
最近记录: |