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 中。
并且图标可以拖动到原始屏幕视图之外而不会被剪裁。
我需要围绕“无限”拖动项目 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 中。
并且图标可以拖动到原始屏幕视图之外而不会被剪裁。