我想创建一个小部件,您可以在其中添加多个不同大小的小部件,并可以使用拖放技术更改它们的位置。类似于带有拖放功能的网格视图,您可以在其中水平和垂直更改位置。当您拖动选定的小部件时,其他小部件将四处移动以为其打开空间。
有没有人有任何建议从哪里开始,或者已经有一些例子正在实现我正在寻找的东西?
我想创建一个小部件,您可以在其中添加多个不同大小的小部件,并可以使用拖放技术更改它们的位置。类似于带有拖放功能的网格视图,您可以在其中水平和垂直更改位置。当您拖动选定的小部件时,其他小部件将四处移动以为其打开空间。
有没有人有任何建议从哪里开始,或者已经有一些例子正在实现我正在寻找的东西?
你也可以试试这个更简单的(它不包括反馈)
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body: HomePage()));
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Offset offset = Offset.zero;
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
left: offset.dx,
top: offset.dy,
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
offset = Offset(offset.dx + details.delta.dx, offset.dy + details.delta.dy);
});
},
child: Container(width: 100, height: 100, color: Colors.blue),
),
),
],
);
}
}
虽然这可能无法回答您的问题,但是那些正在寻找简单拖放小部件的人,那么这里是示例。
有关更简单的方法,请参阅我的第二个答案
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Drag app"),
),
body: HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
double width = 100.0, height = 100.0;
Offset position ;
@override
void initState() {
super.initState();
position = Offset(0.0, height - 20);
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
left: position.dx,
top: position.dy - height + 20,
child: Draggable(
child: Container(
width: width,
height: height,
color: Colors.blue,
child: Center(child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
),
feedback: Container(
child: Center(
child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
color: Colors.blue[300],
width: width,
height: height,
),
onDraggableCanceled: (Velocity velocity, Offset offset){
setState(() => position = offset);
},
),
),
],
);
}
}
我创建了一个名为reorderables的包来解决这个问题。你只需要告诉包你的函数在拖放完成时被调用onReorder(int oldIndex, int newIndex)
。
此示例在网格中有 9 个图标小部件 - 屏幕截图:ReorderableWrap
class _WrapExampleState extends State<WrapExample> {
final double _iconSize = 90;
List<Widget> _tiles;
@override
void initState() {
super.initState();
_tiles = <Widget>[
Icon(Icons.filter_1, key: ValueKey(1), size: _iconSize),
Icon(Icons.filter_2, key: ValueKey(2), size: _iconSize),
Icon(Icons.filter_3, key: ValueKey(3), size: _iconSize),
Icon(Icons.filter_4, key: ValueKey(4), size: _iconSize),
Icon(Icons.filter_5, key: ValueKey(5), size: _iconSize),
Icon(Icons.filter_6, key: ValueKey(6), size: _iconSize),
Icon(Icons.filter_7, key: ValueKey(7), size: _iconSize),
Icon(Icons.filter_8, key: ValueKey(8), size: _iconSize),
Icon(Icons.filter_9, key: ValueKey(9), size: _iconSize),
];
}
@override
Widget build(BuildContext context) {
void _onReorder(int oldIndex, int newIndex) {
setState(() {
Widget row = _tiles.removeAt(oldIndex);
_tiles.insert(newIndex, row);
});
}
return ReorderableWrap(
spacing: 8.0,
runSpacing: 4.0,
padding: const EdgeInsets.all(8),
children: _tiles,
onReorder: _onReorder
);
}
}
如果要限制列数,可以使用名为的可选参数maxMainAxisCount
由于我的声誉,我不能写评论,但我想从 CopsOnRoad 的回答中回答这个问题:
我不想显示反馈视图而不是我想拖动原始视图。可能吗?
如果有人也在寻找这个,你可以使用:childWhenDragging:Container()。您仍在拖动反馈,但原始子项将被隐藏。
...
child: Draggable(
child: Container(
width: width,
height: height,
color: Colors.blue,
child: Center(child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
),
feedback: Container(
child: Center(
child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
color: Colors.blue[300],
width: width,
height: height,
),
childWhenDragging: Container(), // <-- so it looks like the original view is beeing dragged
onDraggableCanceled: (Velocity velocity, Offset offset){
setState(() => position = offset);
},
),
...
这是可拖动文本的示例
class DraggableText extends StatefulWidget {
final Offset initialOffset;
final String text;
DraggableText(this.text, this.initialOffset);
@override
_DraggableTextState createState() => new _DraggableTextState();
}
class _DraggableTextState extends State<DraggableText> {
Offset position = new Offset(0.0, 0.0);
@override
void initState() {
super.initState();
position = widget.initialOffset;
}
@override
Widget build(BuildContext context) {
final item = new LabelBox(size: new Size.square(100.0), label: widget.text);
final avatar = new LabelBox(
size: new Size.square(150.0), label: widget.text, opacity: 0.4);
final draggable = new Draggable(
data: widget.text,
feedback: avatar,
child: item,
childWhenDragging: new Opacity(opacity: 0.0, child: item),
onDraggableCanceled: (velocity, offset) {
print('_DragBoxState.build -> offset ${offset}');
setState(() => position = offset);
});
return new Positioned(
left: position.dx, top: position.dy, child: draggable);
}
}
您可以在此处查看完整示例和更高级的示例https://github.com/rxlabz/flutter_dropcity
您也可以使用LongPressDraggable
,为此您需要长按您的小部件,然后只有您可以拖动它。
Offset _offset = Offset.zero;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: LayoutBuilder(
builder: (context, constraints) {
return Stack(
children: [
Positioned(
left: _offset.dx,
top: _offset.dy,
child: LongPressDraggable(
feedback: FlutterLogo(colors: Colors.orange, size: 100),
child: FlutterLogo(colors: Colors.green, size: 100),
onDragEnd: (details) {
setState(() {
final adjustment = MediaQuery.of(context).size.height - constraints.maxHeight;
_offset = Offset(details.offset.dx, details.offset.dy - adjustment);
});
},
),
),
],
);
},
),
);
}