Swiping to dismiss?
michelleran opened this issue · comments
I'm trying Flutter's sample swipe-to-dismiss code with DragAndDropList
, and though I am able to swipe items away, the program promptly complains about a Dismissible not being removed from the widget tree.
This is my build
function (inside a State
):
List<String> _items = ['Item 1', 'Item 2', 'Item 3'];
@override
Widget build(BuildContext context) {
print('Hey! $_items');
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new DragAndDropList<String>(
_items,
itemBuilder: (BuildContext context, item) {
print('Trying to build a widget for item $item');
return new Dismissible(
// Each Dismissible must contain a unique Key.
key: new Key(item),
onDismissed: (direction) {
setState(() {
_items.remove(item);
print('Hmmm... $_items');
});
},
// Show a red background as the item is swiped away
background: new Container(color: Colors.red),
child: new ListTile(
leading: new Text((_items.indexOf(item) + 1).toString()),
title: new Text(item),
),
);
},
onDragFinish: (before, after) {
String data = _items[before];
_items.removeAt(before);
_items.insert(after, data);
},
canBeDraggedTo: (one, two) => true,
dragElevation: 8.0,
),
);
}
which, after swiping Item 2
away, gives the output
Hey! [Item 1, Item 2, Item 3]
Trying to build a widget for item Item 1
Trying to build a widget for item Item 2
Trying to build a widget for item Item 3
Hmmm... [Item 1, Item 3]
Hey! [Item 1, Item 3]
Trying to build a widget for item Item 1
Trying to build a widget for item Item 2
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building Dismissible-[<'Item 2'>](dirty, state:
_DismissibleState#07bef(tickers: tracking 2 tickers)):
A dismissed Dismissible widget is still part of the tree.
Make sure to implement the onDismissed handler and to immediately remove the Dismissible
widget from the application once that handler has fired.
When the exception was thrown, this was the stack:
#0 _DismissibleState.build.<anonymous closure> (package:flutter/src/widgets/dismissible.dart:471:11)
#1 _DismissibleState.build (package:flutter/src/widgets/dismissible.dart:478:8)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:3713:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3625:15)
#4 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#5 StatefulElement.update (package:flutter/src/widgets/framework.dart:3760:5)
#6 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#7 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4340:32)
#8 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4726:17)
#9 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#10 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4618:14)
#11 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#12 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#13 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#14 StatefulElement.update (package:flutter/src/widgets/framework.dart:3760:5)
#15 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#16 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4618:14)
#17 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#18 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#19 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#20 StatefulElement.update (package:flutter/src/widgets/framework.dart:3760:5)
#21 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#22 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#23 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#24 StatelessElement.update (package:flutter/src/widgets/framework.dart:3685:5)
#25 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#26 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4618:14)
#27 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#28 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#29 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#30 StatelessElement.update (package:flutter/src/widgets/framework.dart:3685:5)
#31 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#32 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#33 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#34 ProxyElement.update (package:flutter/src/widgets/framework.dart:3870:5)
#35 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#36 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#37 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#38 StatefulElement.update (package:flutter/src/widgets/framework.dart:3760:5)
#39 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#40 SliverMultiBoxAdaptorElement.performRebuild (package:flutter/src/widgets/sliver.dart:702:34)
#41 SliverMultiBoxAdaptorElement.update (package:flutter/src/widgets/sliver.dart:671:7)
#42 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#43 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4618:14)
#44 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#45 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4340:32)
#46 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4726:17)
#47 _ViewportElement.update (package:flutter/src/widgets/viewport.dart:187:17)
#48 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#49 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#50 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#51 ProxyElement.update (package:flutter/src/widgets/framework.dart:3870:5)
#52 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#53 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4618:14)
#54 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#55 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4618:14)
#56 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#57 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4618:14)
#58 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#59 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4618:14)
#60 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#61 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#62 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#63 StatefulElement.update (package:flutter/src/widgets/framework.dart:3760:5)
#64 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#65 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4618:14)
#66 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#67 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#68 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#69 StatefulElement.update (package:flutter/src/widgets/framework.dart:3760:5)
#70 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#71 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
#72 Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
#73 StatelessElement.update (package:flutter/src/widgets/framework.dart:3685:5)
#74 Element.updateChild (package:flutter/src/widgets/framework.dart:2682:15)
#75 _LayoutBuilderElement._layout.<anonymous closure> (package:flutter/src/widgets/layout_builder.dart:118:18)
#76 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2205:19)
#77 _LayoutBuilderElement._layout (package:flutter/src/widgets/layout_builder.dart:107:11)
#78 RenderObject.invokeLayoutCallback.<anonymous closure> (package:flutter/src/rendering/object.dart:1666:58)
#79 PipelineOwner._enableMutationsToDirtySubtrees (package:flutter/src/rendering/object.dart:740:15)
#80 RenderObject.invokeLayoutCallback (package:flutter/src/rendering/object.dart:1666:13)
#81 _RenderLayoutBuilder.performLayout (package:flutter/src/widgets/layout_builder.dart:205:5)
#82 RenderObject.layout (package:flutter/src/rendering/object.dart:1570:7)
#83 MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:124:11)
#84 _ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:256:7)
#85 MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:194:7)
#86 RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:338:14)
#87 RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1445:7)
#88 PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:709:18)
#89 BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:270:19)
#90 BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:622:22)
#91 BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:208:5)
#92 BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
#93 BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
#94 BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
#95 _invoke (file:///b/build/slave/Mac_Engine/build/src/flutter/lib/ui/hooks.dart:120)
#96 _drawFrame (file:///b/build/slave/Mac_Engine/build/src/flutter/lib/ui/hooks.dart:109)
════════════════════════════════════════════════════════════════════════════════════════════════════
Trying to build a widget for item Item 3
Seems like itemBuilder
is ignoring changes to _items
. Any thoughts on how to fix this?
Fixed by modifying the build
function in DragAndDropListState
like so:
@override
Widget build(BuildContext context) {
return new LayoutBuilder(
builder: (BuildContext context3, constr) {
rows = (widget.rowsData).map((it) => new Data<T>(it)).toList(); // added this line
return new ListView.builder(
itemBuilder: (BuildContext context2, int index) {
return _getDraggableListItem(context2, index, context3);
},
controller: scrollController,
itemCount: rows.length,
padding: const EdgeInsets.all(0.0),
);
},
);
}
I'm guessing widget.rowsData
was updated but not rows
, since that was only set when the state is initialized.
Actually, on further investigation, this seems to break dragging to reorder...
Fixed by adding in a boolean flag itemWasDismissed
and checking for it in build
; if true, then reset the value of rows
.
On even further investigation, this seems to have slightly broken the animation. It works just fine up until after you dismiss an item; then it no longer shows the original list moving/reordering alongside the item you're dragging until you drop it. The animation also breaks after showing and dismissing a dialog.
Here are my modifications to drag_and_drop_list.dart
(actually not a lot):
// same; omitted
class DragAndDropList<T> extends StatefulWidget {
// same; omitted
bool listWasChanged; // added
DragAndDropList(this.rowsData,
{Key key, @required this.itemBuilder,
this.onDragFinish,
@required this.canBeDraggedTo,
this.dragElevation = 0.0,
this.tilt = 0.0,
this.listWasChanged = false}) // added
: providesOwnDraggable = false,
itemBuilderCustom = null, super(key: key);
// same; omitted
}
class _DragAndDropListState<T> extends State<DragAndDropList> {
// same; omitted
@override
Widget build(BuildContext context) {
return new LayoutBuilder(
builder: (BuildContext context3, constr) {
if (widget.listWasChanged) // added
rows = (widget.rowsData).map((it) => new Data<T>(it)).toList(); // added
return new ListView.builder(
itemBuilder: (BuildContext context2, int index) {
return _getDraggableListItem(context2, index, context3);
},
controller: scrollController,
itemCount: rows.length,
padding: const EdgeInsets.all(0.0),
);
},
);
}
// same; omitted
}
Any thoughts?
This should be related to this PR.
The _DragAndDropListState caches the widgets. There is a method called didUpdateWidget
which is called when the widget of the state is rebuilt. For some reason I removed refreshing the data there, in an earlier commit.
Adding rows = (widget.rowsData).map((it) => new Data<T>(it)).toList();
there should work. Could you try that out?
If that works, I'll do some additional testing and update the code.
Yes, adding that (and removing my changes) appears to fix it - thanks!