Skip to content

Reorderable list widget and Material demo#18374

Merged
DaveShuckerow merged 96 commits intoflutter:masterfrom
DaveShuckerow:shuffle-list
Jul 30, 2018
Merged

Reorderable list widget and Material demo#18374
DaveShuckerow merged 96 commits intoflutter:masterfrom
DaveShuckerow:shuffle-list

Conversation

@DaveShuckerow
Copy link
Contributor

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.

Dave Shuckerow added 30 commits April 20, 2018 14:07
@DaveShuckerow
Copy link
Contributor Author

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.

@DaveShuckerow
Copy link
Contributor Author

@HansMuller
@jonahwilliams
@alanrussian

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.

@alanrussian
Copy link
Contributor

@DaveShuckerow there's one issue we have. I sent you an email.

Copy link
Contributor

@HansMuller HansMuller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will chat with you about this.

setState(() {
_itemType = type;
});
_bottomSheet?.setState(() { });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. I've updated and added comments.

}

void _showConfigurationSheet() {
final PersistentBottomSheetController<Null> bottomSheet = scaffoldKey.currentState.showBottomSheet((BuildContext bottomSheetContext) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just set _bottomSheet here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

);
});

setState(() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a _setState needed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NICE

}

@override
void dispose() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want this widget at all, but this override clearly isn't needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd mentioned this before. CreateState methods like this are boilerplate and the convention for their format is:

_ReorderableListViewState createState() => new _ReorderableListViewState();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@HansMuller
Copy link
Contributor

LGTM and hooray!

@DaveShuckerow DaveShuckerow merged commit f844fad into flutter:master Jul 30, 2018
@knopp
Copy link
Member

knopp commented Jul 30, 2018

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?

@knopp
Copy link
Member

knopp commented Jul 30, 2018

Also, the moment shadows appears the items below are shifted down a bit (probably due to the shadow). That doesn't feel right.

@DaveShuckerow
Copy link
Contributor Author

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.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 10, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Built-in List Drag/Drop Reordering

6 participants