Dart 在卡中任意定位小部件(基于相对于父级的x-y坐标)

Dart 在卡中任意定位小部件(基于相对于父级的x-y坐标),dart,flutter,Dart,Flutter,我想在卡片上放置一个点,可以在卡片内部任意移动 到目前为止,这是我的解决方案 class RoomCard extends StatefulWidget { final Room room; RoomCard({ @required this.room, }) : assert(room != null); @override _RoomCardState createState() => _RoomCardState(); } class _RoomCa

我想在卡片上放置一个点,可以在卡片内部任意移动

到目前为止,这是我的解决方案

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(),
    );
  }
}
class RoomCard扩展StatefulWidget{
最后一个房间;
房卡({
@需要这个房间,
}):assert(房间!=null);
@凌驾
_RoomCardState createState();
}
类RoomCardState扩展了状态{
双x=0.0;
双y=0.0;
@凌驾
小部件构建(构建上下文){
返回大小框(
高度:400.0,
宽度:400.0,
儿童:手势检测器(
onPanUpdate:(p){
设置状态(){
x+=p.delta.dx;
y+=p.delta.dy;
});
},
孩子:卡片(
子:堆栈(
儿童:[
标记(
x:x,
y:y,
),
],
),
),
),
);
}
}
类标记扩展了无状态小部件{
最后的双x;
最后双y;
标记({this.x:0.0,this.y:0.0});
@凌驾
小部件构建(构建上下文){
打印(“x:$x,y:$y”);
返回填充(
填充:仅限边设置(左:x,上:y),
child:CircleAvatar(),
);
}
}
除了使用Padding小部件之外,我找不到任何其他方法基于x,y位置将标记放置在卡中。如果有其他更好的方法,请告诉我

其次,这是第一次(第一次移动它)。之后移动时出现问题。我在这里有没有遗漏什么逻辑

我想进一步扩展这一点,在卡片上有多个这样的点,可以易怒地放置和移动


如果您能推荐任何第三方软件包,我很高兴。

您想要的可能是或

使用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扩展StatefulWidget{
@凌驾
_RoomCardState createState();
}
类RoomCardState扩展了状态{
偏移位置=偏移0.0;
@凌驾
小部件构建(构建上下文){
返回大小框(
高度:400.0,
宽度:400.0,
儿童:手势检测器(
onPanUpdate:(p){
设置状态(()=>位置+=p.delta);
},
子:CustomSingleChildLayout(
代表:MarkerLayout代表(职位),
子项:标记(),
),
),
);
}
}
类CallableNotifier扩展了ChangeNotifier{
作废通知(){
this.notifyListeners();
}
}
类MarkerLayoutDelegate使用ChangeNotifier扩展SingleChildLayoutDelegate{
偏置位置;
MarkerLayoutDelegate(该职位);
@凌驾
BoxConstraints getConstraintsForChild(BoxConstraints约束){
返回约束;
}
@凌驾
偏移量getPositionForChild(大小,大小childSize){
返回偏移量(最小值(position.dx,size.width-childSize.width),最小值(position.dy,size.height-childSize.height);
}
@凌驾
bool shouldrellayout(MarkerLayoutDelegate-oldDelegate){
返回位置!=oldDelegate.position;
}
}
类标记扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回容器(
宽度:30,
身高: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(),
    );
  }
}
class RoomCard扩展StatefulWidget{
@凌驾
_RoomCardState createState();
}
类RoomCardState扩展了状态{
双x=0.0;
双y=0.0;
MarkerLayoutDelegate=MarkerLayoutDelegate(relayout:CallableNotifier());
@凌驾
小部件构建(构建上下文){
返回大小框(
高度:400.0,
宽度:400.0,
儿童:手势检测器(
onPanUpdate:(p){
delegate.position+=p.delta;
},
子:CustomSingleChildLayout(
代表:代表,
子项:标记(),
),
),
);
}
}
类CallableNotifier扩展了ChangeNotifier{
作废通知(){
this.notifyListeners();
}
}
类MarkerLayoutDelegate使用ChangeNotifier扩展SingleChildLayoutDelegate{
偏移位置;
CallableNotifier\u通知程序;
MarkerLayoutDelegate({CallableNotifier relayout,Offset initialPosition=Offset.zero})
:_位置=初始位置,
_通知程序=重新启动,
超级(重新播放:重新播放);
设置位置(偏移位置){
_位置=位置;
_notifyListeners();
}
偏移量获取位置=>\u位置;
@凌驾
BoxConstraints getConstraintsForChild(BoxConstraints约束){
返回约束;
}
@凌驾
偏移量getPositionForChild(大小,大小childSize){
返回偏移量(最小值(_position.dx,size.width-childSize.width),最小值(_position.dy,size.height-childSize.height));
}
@凌驾
bool shouldrellayout(MarkerLayoutDelegate-oldDelegate){
返回_位置!=oldDelegate._位置;
}
}
类标记扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回容器(
宽度:30,
身高:30,
child:CircleAvatar(),
);
}
}

您可以像下面这样使用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());
        }
      }
导入“包装:颤振/材料.省道”;
导入“package:flatter/rendering.dart”;
void main(){
runApp(新材料)PP(
家:新脚手架(
正文:RoomCard(房间:room()),
),
));
}
教室{}
类RoomCard扩展StatefulWidget{
最后一个房间;
房卡({
@需要这个房间,
}):assert(房间!=null);
@凌驾
_RoomCardState createState();
}
类_roomcardstateexte