I am implementing something similar to ios home screen’s dragging feature for realigning apps in Flutter. I am using Draggable and Dragtarget as the approach. Here’s the code:
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late final List<Widget> widgets = [
DragTarget(
key: UniqueKey(),
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Draggable(
key: UniqueKey(),
data: 0,
feedback: Container(
width: 100,
height: 100,
color: Colors.red,
),
child: GestureDetector(
onTap: () {
print('red tapped');
},
child: Container(
key: UniqueKey(),
color: Colors.redAccent,
),
),
);
},
onAcceptWithDetails: (DragTargetDetails<int> details) {
homePageWidgetSequence.swap(homePageWidgetSequence.indexOf(0),
homePageWidgetSequence.indexOf(details.data));
setState(() {
print(homePageWidgetSequence);
});
},
),
DragTarget(
key: UniqueKey(),
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Draggable(
data: 1,
key: UniqueKey(),
feedback: Container(
width: 100,
height: 100,
color: Colors.blue,
),
child: GestureDetector(
onTap: () {
print('blue tapped');
},
child: Container(
key: UniqueKey(),
color: Colors.blueAccent,
),
),
);
},
onAcceptWithDetails: (DragTargetDetails<int> details) {
homePageWidgetSequence.swap(homePageWidgetSequence.indexOf(1),
homePageWidgetSequence.indexOf(details.data));
setState(() {
print(homePageWidgetSequence);
});
},
),
DragTarget(
key: UniqueKey(),
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Draggable(
key: UniqueKey(),
data: 2,
feedback: Container(
width: 100,
height: 100,
color: Colors.green,
),
child: GestureDetector(
onTap: () {
print('green tapped');
},
child: Container(
key: UniqueKey(),
color: Colors.greenAccent,
),
),
);
},
onAcceptWithDetails: (DragTargetDetails<int> details) {
homePageWidgetSequence.swap(homePageWidgetSequence.indexOf(2),
homePageWidgetSequence.indexOf(details.data));
setState(() {
print(homePageWidgetSequence);
});
},
)
];
static List<int> homePageWidgetSequence = [0, 1, 2];
@override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.count(
crossAxisSpacing: 10,
mainAxisSpacing: 10,
crossAxisCount: 2,
children: [
for (int i = 0; i < 3; i++)
if (homePageWidgetSequence[i] == 0) widgets[i],
for (int i = 0; i < 3; i++)
if (homePageWidgetSequence[i] == 1) widgets[i],
for (int i = 0; i < 3; i++)
if (homePageWidgetSequence[i] == 2) widgets[i],
],
),
);
}
}
extension ListSwap on List {
void swap(int index1, int index2) {
RangeError.checkValidIndex(index1, this, 'index1');
RangeError.checkValidIndex(index2, this, 'index2');
var tmp = this[index1];
this[index1] = this[index2];
this[index2] = tmp;
}
}
However, sometimes the drag causes unexpected behavior. For example, dragging red to blue, and then drag blue to green should result in green-red-blue, but it displays blue-green-red instead.
By printing homePageWidgetSequence it seems that the homePageWidgetSequence appears in the correct order, and it’s the setState build process that messes up the widgets. I tried adding UniqueKeys() to key but it doesn’t seem to work.
Could someone help me with this. Thank you!