我要在卡上放置一个点,该点可以在卡内任意移动。
到目前为止,这是我的解决方法。
class RoomCard extends StatefulWidget {
final Room room;
RoomCard({
@required this.room,
}) : assert(room != null);
@override
_RoomCardState createState() => _RoomCardState();
}
class _RoomCardState extends State<RoomCard> {
double x = 0.0;
double y = 0.0;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 400.0,
width: 400.0,
child: GestureDetector(
onPanUpdate: (p) {
setState(() {
x += p.delta.dx;
y += p.delta.dy;
});
},
child: Card(
child: Stack(
children: <Widget>[
Marker(
x: x,
y: y,
),
],
),
),
),
);
}
}
class Marker extends StatelessWidget {
final double x;
final double y;
Marker({this.x: 0.0, this.y: 0.0});
@override
Widget build(BuildContext context) {
print("x: $x, y: $y");
return Padding(
padding: EdgeInsets.only(left: x, top: y),
child: CircleAvatar(),
);
}
}
除使用Padding小部件执行此操作外,我找不到其他方法可将标记根据x,y位置放置在卡中。让我知道是否还有其他更好的方法。
其次,这是第一次工作(第一次移动)。事后移动时出现问题。我在这里错过任何逻辑吗?
我想进一步扩展它,使其在卡中具有多个这样的点,可以很容易地放置和移动它们。
如果您可以建议执行此操作的任何第三方软件包,我都很高兴。
答案 0 :(得分:1)
您要寻找的可能是CustomSingleChildLayout
或CustomMultiChildLayout
。
使用CustomSingleChildLayout看起来像这样:
class RoomCard extends StatefulWidget {
@override
_RoomCardState createState() => _RoomCardState();
}
class _RoomCardState extends State<RoomCard> {
Offset position = Offset.zero;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 400.0,
width: 400.0,
child: GestureDetector(
onPanUpdate: (p) {
setState(() => position += p.delta);
},
child: CustomSingleChildLayout(
delegate: MarkerLayoutDelegate(position),
child: Marker(),
),
),
);
}
}
class CallableNotifier extends ChangeNotifier {
void notify() {
this.notifyListeners();
}
}
class MarkerLayoutDelegate extends SingleChildLayoutDelegate with ChangeNotifier {
Offset position;
MarkerLayoutDelegate(this.position);
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return constraints.loosen();
}
@override
Offset getPositionForChild(Size size, Size childSize) {
return Offset(min(position.dx, size.width - childSize.width), min(position.dy, size.height - childSize.height));
}
@override
bool shouldRelayout(MarkerLayoutDelegate oldDelegate) {
return position != oldDelegate.position;
}
}
class Marker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 30,
height: 30,
child: CircleAvatar(),
);
}
}
或者您可以使用侦听器来实现,使得每次更改点的位置时主小部件都不需要重建:
class RoomCard extends StatefulWidget {
@override
_RoomCardState createState() => _RoomCardState();
}
class _RoomCardState extends State<RoomCard> {
double x = 0.0;
double y = 0.0;
MarkerLayoutDelegate delegate = MarkerLayoutDelegate(relayout: CallableNotifier());
@override
Widget build(BuildContext context) {
return SizedBox(
height: 400.0,
width: 400.0,
child: GestureDetector(
onPanUpdate: (p) {
delegate.position += p.delta;
},
child: CustomSingleChildLayout(
delegate: delegate,
child: Marker(),
),
),
);
}
}
class CallableNotifier extends ChangeNotifier {
void notify() {
this.notifyListeners();
}
}
class MarkerLayoutDelegate extends SingleChildLayoutDelegate with ChangeNotifier {
Offset _position;
CallableNotifier _notifier;
MarkerLayoutDelegate({CallableNotifier relayout, Offset initialPosition = Offset.zero})
: _position = initialPosition,
_notifier = relayout,
super(relayout: relayout);
set position(Offset position) {
_position = position;
_notifier.notifyListeners();
}
Offset get position => _position;
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return constraints.loosen();
}
@override
Offset getPositionForChild(Size size, Size childSize) {
return Offset(min(_position.dx, size.width - childSize.width), min(_position.dy, size.height - childSize.height));
}
@override
bool shouldRelayout(MarkerLayoutDelegate oldDelegate) {
return _position != oldDelegate._position;
}
}
class Marker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 30,
height: 30,
child: CircleAvatar(),
);
}
}
答案 1 :(得分:1)
您可以使用如下所示的Transform
class Marker extends StatelessWidget {
final double x;
final double y;
Marker({this.x: 0.0, this.y: 0.0});
@override
Widget build(BuildContext context) {
print("x: $x, y: $y");
return Transform(
transform: Matrix4.translationValues(x, y, 0.0), child: CircleAvatar());
}
}
您需要检查x,y约束以将变换绑定到特定区域
编辑:
这是一个完整的工作代码,用于说明如何将标记限制在卡片的底部边缘
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(new MaterialApp(
home: new Scaffold(
body: RoomCard(room: Room()),
),
));
}
class Room {}
class RoomCard extends StatefulWidget {
final Room room;
RoomCard({
@required this.room,
}) : assert(room != null);
@override
_RoomCardState createState() => _RoomCardState();
}
class _RoomCardState extends State<RoomCard> {
double x = 0.0;
double y = 0.0;
@override
Widget build(BuildContext context) {
//This hight should be known or calculated for the Widget need to be moved
const double markerHight = 50.0;
double ymax = context.findRenderObject()?.paintBounds?.bottom ?? markerHight ;
return SizedBox(
height: 300.0,
width: 400.0,
child: GestureDetector(
onPanUpdate: (p) {
setState(() {
x += p.delta.dx;
y = (y+p.delta.dy) >ymax - markerHight ? ymax -markerHight : y+p.delta.dy;
});
},
child: Card(
child: Stack(
children: <Widget>[
Marker(
x: x,
y: y,
),
],
),
),
),
);
}
}
class Marker extends StatelessWidget {
final double x;
final double y;
Marker({this.x: 0.0, this.y: 0.0});
@override
Widget build(BuildContext context) {
print("x: $x, y: $y");
return Transform(
transform: Matrix4.translationValues(x, y, 0.0),
child: CircleAvatar());
}
}