Reorderable list widget and Material demo#18374
Reorderable list widget and Material demo#18374DaveShuckerow merged 96 commits intoflutter:masterfrom
Conversation
|
I met up with Jonah about the accessibility tests we ran over the widget.
Mulligan also reported that they needed support for a non-reorderable List Header. I've added that into this list. Landing the fix to #18432 is currently the last known blocker to landing this widget. |
|
@HansMuller The only remaining blocker I'm aware of is to add support for custom accessibility actions (#18882). Please let me know if there is anything else you believe this PR needs. |
|
@DaveShuckerow there's one issue we have. I sent you an email. |
HansMuller
left a comment
There was a problem hiding this comment.
Will chat with you about this.
| setState(() { | ||
| _itemType = type; | ||
| }); | ||
| _bottomSheet?.setState(() { }); |
There was a problem hiding this comment.
These two lines are confusing. If _bottomSheet can be null, then don't both references need ?.. And why rebuild the bottom sheet if we're going to close it? A comment would be helpful here.
There was a problem hiding this comment.
Correct. I've updated and added comments.
| } | ||
|
|
||
| void _showConfigurationSheet() { | ||
| final PersistentBottomSheetController<Null> bottomSheet = scaffoldKey.currentState.showBottomSheet((BuildContext bottomSheetContext) { |
There was a problem hiding this comment.
Why not just set _bottomSheet here?
| ); | ||
| }); | ||
|
|
||
| setState(() { |
There was a problem hiding this comment.
I've applied your suggested change.
The setState() is still here. What's it for?
| // Garbage collect the bottom sheet when it closes. | ||
| bottomSheet.closed.whenComplete(() { | ||
| if (mounted) { | ||
| setState(() { |
There was a problem hiding this comment.
Why is a _setState needed here?
There was a problem hiding this comment.
There was a comment I had left that's hidden by the commit history now.
I applied the change, but not setting state led to strange behavior: the menu in the corner doesn't update to reflect that you can open the bottom sheet again.
| assert(onReorder != null), | ||
| assert(children != null), | ||
| assert( | ||
| children.every((Widget w) => w.key != null), |
| } | ||
|
|
||
| @override | ||
| void dispose() { |
There was a problem hiding this comment.
I don't think we want this widget at all, but this override clearly isn't needed.
There was a problem hiding this comment.
We discussed this offline, and agreed we can compute the size of the dragging widget when it's first picked.
Now doing this with the global keys I added for semantics compatibility.
|
|
||
| @override | ||
| State<StatefulWidget> createState() { |
There was a problem hiding this comment.
I'd mentioned this before. CreateState methods like this are boilerplate and the convention for their format is:
_ReorderableListViewState createState() => new _ReorderableListViewState();
| @@ -312,6 +312,13 @@ class _ReorderableListContentState extends State<_ReorderableListContent> with T | |||
| // Handles up the logic for dragging and reordering items in the list. | |||
| Widget _wrap(Widget toWrap, int index, BoxConstraints constraints) { | |||
There was a problem hiding this comment.
If I provide a list of three stateful children with keys A, B, and C then if I rebuild the ReorderableList with the same children ordered C, B , A then I'd expect that the state of C, B, and A will have been preserved. Because they appear to be siblings, even though they're not actually siblings. In other words, I would not expect any of the the A, B, C widgets' state to be disposed.
Since each GlobalKey includes the child's index, I don't think child state will be preserved if the ReorderableList is rebuilt with the same children, but in a different order.
We should probably have a test that checked this.
|
LGTM and hooray! |
|
The reordering threshold is still wrong. It is different from both android and material (at least what I tried in Google Chrome settings on Android). When reordering up the behavior is same as on iOS (threshold being top border touching middle of item above), different from Material (where the threshold is top border touching the top border of item above). When reordering down the behavior is different from both of them. on iOS the bottom border of dragged item needs to touch middle of item below, in Material the bottom border of dragged item needs to touch bottom of item below, in this implementation the top border of dragged item needs to touch middle of item below, which means it needs to be dragged way too far. Any plans addressing this? |
|
Also, the moment shadows appears the items below are shifted down a bit (probably due to the shadow). That doesn't feel right. |
|
Hi Matej, thanks for chiming in! The discerning eye of our community members is extremely valuable as we plan future work. If you look at the issue that this PR fixes, you can see we do have other open issues related to this widget. If you think something is off in the implementation that those issues don't discuss, please feel free to file an issue of your own so that we don't lose track of it. |
Fixes #16763.
This introduces a widget, ReorderableList, which wraps its children inside of a LongPressDraggable, and constrains the drag to a single axis.
It also uses Scrollable.ensureVisible to allow the user to scroll through the reorder operation.