Open Levaru opened 4 years ago
@Levaru I was trying something similar, here is a way to simply add it but only to the end of the list. I do want to end up being able to drag it to a certain position and also to drag out to remove.
import 'package:flutter/material.dart';
import 'dart:math';
import 'package:reorderables/reorderables.dart';
void main() => runApp(MyApp());
List<Widget> tiles = <Widget>[
Icon(
Icons.filter_1,
size: 90,
),
Icon(
Icons.filter_2,
size: 90,
),
Icon(
Icons.filter_3,
size: 90,
),
Icon(
Icons.filter_4,
size: 90,
),
Icon(
Icons.filter_5,
size: 90,
),
Icon(
Icons.filter_6,
size: 90,
),
Icon(
Icons.filter_7,
size: 90,
),
Icon(
Icons.filter_8,
size: 90,
),
Icon(
Icons.filter_9,
size: 90,
),
];
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
fontFamily: 'PressStart',
),
home: ColorGame(),
);
}
}
class ColorGame extends StatefulWidget {
ColorGame({Key key}) : super(key: key);
createState() => ColorGameState();
}
class ColorGameState extends State<ColorGame> {
List<String> droppableNames = ["DROP_A", "DROP_B", "DROP_C", "DROP_D"];
Map<String, dynamic> selected = {
"DROP_A": false,
"DROP_B": false,
"DROP_C": false,
"DROP_D": false
};
@override
void initState() {
super.initState();
print("DROPABLE: $droppableNames \n DROPPABLEMAP: ${selected.toString()}");
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DragTarget<String>(
builder: (BuildContext context, List<String> candidateData,
List<dynamic> rejectedData) {
return WrapExample();
},
onWillAccept: (data) => true,
// data == expecting.runtimeType, //condition to accept data
onAccept: (data) {
var newTile = Icon(Icons.ac_unit);
print("Wrap recieved data");
setState(() {
tiles.add(newTile);
});
},
),
// WrapExample(),
Container(
//dragables
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: droppableNames.map((name) {
return Draggable<String>(
data: name,
child: Text(name + "_DRAGGABLE"),
feedback: Text(name + "_IN_DRAG"),
childWhenDragging: Text(name + "_REMAINER"),
);
}).toList(),
),
)
],
),
),
);
}
Widget _buildDragAndDropTarget(String key, {bool remove: true}) {
return DragTarget<String>(
builder: (BuildContext context, List<String> incoming, List rejected) {
if ((!(this.selected[key] == false) && remove) ||
((this.selected[key] == false) && remove)) {
return Container(
color: Colors.white,
child: Draggable<String>(
data: key,
child: Text(key + "_DRAGGABLE"),
feedback: Text(key + "_IN_DRAG"),
childWhenDragging: remove ? Container() : Text(key + "_REMAINER"),
),
alignment: Alignment.center,
height: 80,
width: 80,
);
} else {
return Container(
color: Colors.grey,
height: 80,
width: 100,
child: Text("$key: EMPTY"));
}
},
onWillAccept: (data) => true,
// data == expecting.runtimeType, //condition to accept data
onAccept: (data) {
setState(() {
selected[key] = data.toString();
print(selected.toString());
});
},
);
}
}
//reordable bit
class WrapExample extends StatefulWidget {
@override
State<StatefulWidget> createState() => _WrapExampleState();
}
class _WrapExampleState extends State<WrapExample> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
void _onReorder(int oldIndex, int newIndex) {
setState(() {
Widget row = tiles.removeAt(oldIndex);
tiles.insert(newIndex, row);
});
}
var wrap = ReorderableWrap(
spacing: 8.0,
runSpacing: 4.0,
padding: const EdgeInsets.all(8),
children: tiles,
onReorder: _onReorder,
onNoReorder: (int index) {
//this callback is optional
debugPrint(
'${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
},
onReorderStarted: (int index) {
//this callback is optional
debugPrint(
'${DateTime.now().toString().substring(5, 22)} reorder started: index:$index');
});
var column = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
wrap,
ButtonBar(
alignment: MainAxisAlignment.start,
children: <Widget>[
IconButton(
iconSize: 50,
icon: Icon(Icons.add_circle),
color: Colors.deepOrange,
padding: const EdgeInsets.all(0.0),
onPressed: () {
var newTile = Icon(Icons.filter_9_plus);
setState(() {
tiles.add(newTile);
});
},
),
IconButton(
iconSize: 50,
icon: Icon(Icons.remove_circle),
color: Colors.teal,
padding: const EdgeInsets.all(0.0),
onPressed: () {
setState(() {
tiles.removeAt(0);
});
},
),
],
),
],
);
return SingleChildScrollView(
child: column,
);
}
}
@Levaru I thinking having two lists (the first only containing a single item) would provide the behavior you are wanting.
I also am wanting to do this. There is an issue for it but it's not been updated in a long time: https://github.com/hanshengchiu/reorderables/issues/1
@Levaru Did you managed to achieve 'drag out to remove' functionality?
@emvaized Yes, I did. It involves some modifications to the package source code though.
You need to remove this line or make it a comment. If you don't have LongPressDraggable functionality then remove/comment out the same line for the Draggable. This allows you to drag your items freely around the screen.
I created a "delete area" which is a DragTarget that accepts Keys (just like the one used for every item). The Draggables all carry Keys as their data, so when you drop one into your Delete-DragTarget the onAccept function should trigger. There you just find the same Key inside your item list and remove it.
(Optional) In my case, the "delete area" appears the moment I start dragging an item. To catch this event, you will need to modify the onDragStarted function and then somehow trigger your "delete area" to appear.
@Levaru
Well, I'm using ReordableWrap
widget, and in this case it simply doesn't have the line you mentioned...
And all the DragTarget
widgets just don't react to the dragged element for some reason, even onHover callback with print()
function is not called.
UPD: I figured it out -- just needed to change DragTarget to DragTarget<int>
Now it accepts icons correctly!
Thanks a lot.
The only question remaining is, how to achieve dragging items to list from the outside.
@emvaized
Well, I'm using ReordableWrap widget, and in this case it simply doesn't have the line you mentioned...
In that case I believe you don't have the movement restriction along the axis so you can skip it.
And all the DragTarget widgets just don't react to the dragged element for some reason, even onHover callback with print() function is not called.
You need to create the DragTarget the same way as in this line of code. So DragTarget<Key>
would be the correct way but it's interesting that DragTarget<int>
works too.
The only question remaining is, how to achieve dragging items to list from the outside.
I managed to add this functionality, but it involves some HEAVY modifications to the source code of the package and it's quite a messy workaround.
To drag something into the list you need a Draggable
. In my case, I created a Row
filled with Draggable<Key>
that have an Icon
as their child. The Draggables need the same functionality as the ones found in the source code.
The onDragStarted
function of your Draggable<Key>
first needs to create a new ReorderableWidget
with an UniqueKey
and then add it to the reorderable widget list. Afterwards it needs to set some variables the same way as the normal onDragStarted function but this time with the Key
and index (just use the last position in the list) of your newly created ReorderableWidget
.
That way the reorderable list can recognize it and treat your new Draggable
as part of the list that is just being reordered with all those reorder animations that come with it. In case that you change your mind while dragging and don't want to add it, you can just delete the Widget from the list inside the onDraggableCanceled
function.
The tricky part is how you manage to access those variables that are being set in the onDragStarted function. If you create the Row
with your Draggable<Key>
inside the reorderable_flex.dart and just add it as toolbar (for example) to the bottom of the widget tree, then you can access and modify the reorderable_list variables, no problem.
In my case, I had my toolbar as a separate widget and had to create an extra Provider class, move some of the code from the reorderable_list to it and manage it there. That includes the AnimationControllers _ghostController and _entranceController which I then provided back to the reorderable_list. It's quite messy honestly and maybe there is a better way but I'm quite new to Flutter so this is the only one I found.
Hope that helps you!
@Levaru Well, sounds really complicated 😅 Not sure if I can handle it, but thanks a lot for the idea!
We need examples for it
@Levaru Can you share your fork of the package? Or even just the edited files/sample?
Would be very helpful
Hi, first of all great package. I'm looking for the functionality of adding new items to the list by dragging them from outside (for example a bottom bar filled with icons that represent the desired item) and then dropping them at the desired position inbetween other items.
I was thinking of using a DraggableWidget to initiate the drag outside the list and then add the corresponding item to the reordable item list. Then I would somehow have to replace the DraggableWidget with the new ReordableWidget and initiate the drag of that so that the list would register it.
But looking at the source code I'm kinda lost on how to implement such a thing. Similar to this I'm also looking for a way to drag items between lists like for example in the Nested ReorderableWrap where items could be moved inbetween the wraps.