Flutter - 将小部件拖出初始屏幕 space

Flutter - Drag widget out of the initial screen space

我需要围绕“无限”拖动项目 canvas。 该代码允许拖动项目并在容器内移动。

但是,如果我在容器内移动并将项目拖出原始屏幕 space(从打火机容器中拖出),它们就会消失。

我添加了一个较轻的容器来帮助可视化问题: Items in limit of the original screen view

import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' as vector;
import 'package:matrix_gesture_detector/matrix_gesture_detector.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Monitor',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        //canvasColor: Color.fromRGBO(42, 48, 68, 100),
      ),
      home: const MyHomePage(title: 'Monitor'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  GlobalKey _paintKey = GlobalKey();
  Offset _offset = const Offset(0,0);
  List<Widget> widgets = [];

  Matrix4 matrix = Matrix4.identity();

  void _addElement()
  {
    setState(() {
      widgets.add(PositionedDraggableIcon(top: 0, left: 0, offset: matrix));
    });
  }

  @override
  Widget build(BuildContext context) {
    return MatrixGestureDetector(
        onMatrixUpdate: (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) {
          setState(() {
            matrix = m;
            for (Widget w in widgets) {
              (w as PositionedDraggableIcon).updateMatrix(matrix);
            }
          });
        },
        clipChild: false,
        child: Scaffold(
          appBar: AppBar(
            // Here we take the value from the MyHomePage object that was created by
            // the App.build method, and use it to set our appbar title.
            title: Text(widget.title),
          ),
          body: Container(
              color: Colors.grey,
              child: Transform(
                transform: matrix,
                child: Container(
                  color: Colors.grey[400],
                  child: Stack(
                      children: widgets,
                    ),
                ),
              ),
            ),
          floatingActionButton: FloatingActionButton(
            onPressed: _addElement,
            tooltip: 'Add icon',
            child: const Icon(Icons.add),
          ),
        )
    );
  }
}

class PositionedDraggableIcon extends StatefulWidget {
  final double top;
  final double left;
  Matrix4 offset;

  void updateMatrix(Matrix4 matrix)
  {
    offset = matrix;
    /*setState(() {
      matrix = matrix;
    });*/
  }

  PositionedDraggableIcon({Key? key,
    required this.top,
    required this.left,
    required this.offset,}) : super(key: key);

  @override
  _PositionedDraggableIconState createState() => _PositionedDraggableIconState();
}

class _PositionedDraggableIconState extends State<PositionedDraggableIcon> {
  GlobalKey _key = GlobalKey();
  late double top, left;
  late double xOff, yOff;

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
    top = widget.top;
    left = widget.left;
    super.initState();
  }

  void _getRenderOffsets() {
    final RenderBox renderBoxWidget = _key.currentContext?.findRenderObject() as RenderBox;
    vector.Vector3 lTrans = widget.offset.getTranslation();
    final offset = renderBoxWidget.localToGlobal(Offset(-lTrans.x, -lTrans.y));

    yOff = offset.dy - top;
    xOff = offset.dx - left;
  }

  void _afterLayout(_) {
    _getRenderOffsets();
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
      key: _key,
      top: top,
      left: left,
      child: Draggable(
        child: Icon(Icons.input),
        feedback: Icon(Icons.input),
        childWhenDragging: Container(),
        onDragEnd: (drag) {
          setState(() {
            top = drag.offset.dy - yOff - widget.offset.getTranslation().y;
            left = drag.offset.dx - xOff - widget.offset.getTranslation().x;
          });
        },
      ),
    );
  }
}

我想出了办法解决我的问题:clipBehavior: Clip.none 在 Stack 中。 并且图标可以拖动到原始屏幕视图之外而不会被剪裁。