Conversation
There was a problem hiding this comment.
I have analyzed all the scenarios of inserting RO, it is safe to do so and can fix this bug
There was a problem hiding this comment.
There are three situations where this function will be called,
- In the
_TableElementmount phase,_lastRemovedParentDataInThisFrameis null at this time; - The
_TableElementupdate phase. For one row update, thevoid update(Table newWidget)will first insert the new RO, then remove the old RO, and then update the next Row. The first RO inserted coordinations must be the last deletion of the previous row(if any). The Updata will also call_updateRenderObjectChildrenat the end, so the operation is safe. - In the scene of the issue problem,
_lastRemovedParentDataInThisFramewill record the last delete the coordinates of RO. If there is an insertion in this frame, we can find the position to be inserted.
|
@Hixie Hey, please take a look at this in case you have time, I think it will take your short time this time! |
There was a problem hiding this comment.
let's put this before the first method that uses it, to aid readability
There was a problem hiding this comment.
this wouldn't work for cases where this is done on a PipelineOwner that isn't being run by the binding...
There was a problem hiding this comment.
Could you let me know in what scenarios the situation you mentioned will be used(or some documentations)?
I am not familiar with this and want to learn it.
|
I admit this is a short solution. I'm not convinced it would work reliably (solutions involving passing information out-of-band like this and scheduling post-frame cleanup are always a bit suspicious), but it might be an improvement over the current code regardless. Did you try using the cc @goderbauer. I'm leaning towards us just landing this since it does fix the problem, though I'd probably want to make sure we filed a bug to revisit this and consider making Table work with a real |
I totally agree, nor do I think it’s a perfect solution. In fact, the last PR #69670 tried to use the |
I meant using the X,Y version of slot (rather than the before/after version of slot like most multi-child widgets). |
|
If you are running into a problem with this bug in your app, it's probably fine to land this as-is (assuming @goderbauer agrees). It's better to have a fix than no fix and this fix is not particularly more complicated than what we have now. |
I will try it! |
|
yes, the x,y version slot seems to work! I will update the new solution ASAP |
|
@Hixie Hey, |
| @override | ||
| void insertRenderObjectChild(RenderObject child, dynamic slot) { | ||
| renderObject.setupParentData(child); | ||
| if (slot is _TableSlot) |
There was a problem hiding this comment.
I think the problem here is that we are using updateChildren and that uses _IndexedSlot. We probably need to create our own version of updateChildren that doesn't mess with the slot and does things by honoring the x,y coordinate, and then we can reliably use _TableSlot everywhere, including in mount.
There was a problem hiding this comment.
Truly, I considered this matter. There is an intermediate state of indexedSlot that is not so perfect, but if we overrideupdateChildren, there may be a lot of duplicate code. Do you have any suggestions for this problem?
There was a problem hiding this comment.
Another question, if we do this, there will be no way to distinguish the three scenarios here, because we cannot add RO through this method in the first two scenarios.
There was a problem hiding this comment.
The current implementation uses void setFlatChildren to change the dimensions of RO._children, which is different from other multiChildRO.
There was a problem hiding this comment.
Now I ignore the slot during updating, any thoughts? :)
There was a problem hiding this comment.
@goderbauer Hi,
There are three scenarios that will call 'insertRenderObjectChild',
- mount
- update
- The related issue scenario
If we initialize the slots in themountandupdatephases totableslot, we cannot distinguish between these three scenarios, because the current implementation does not allow us to insert RO through this function, and RO needs to be inserted only in the scenario of the issue. Otherwise, we need to refactor the 'RenderTable' again!
There was a problem hiding this comment.
I was playing around with this a little bit, what do you think about the approach in https://github.com/flutter/flutter/pull/78081/files?
There was a problem hiding this comment.
So appreciate that your being able to continue this work. I have left some comments there.
|
Ping @Hixie : ) |
| int get hashCode => hashValues(x, y); | ||
|
|
||
| @override | ||
| String toString() => '${super.toString()}; "x" : "$x"; "y" : "$y"; }'; |
There was a problem hiding this comment.
| /// This function is a convenience wrapper around [updateChild], which updates | ||
| /// each individual child. When calling [updateChild], this function uses an | ||
| /// [IndexedSlot<Element>] as the value for the `newSlot` argument. | ||
| /// [IndexedSlot<Element>] as the value for the `newSlot` argument if [ignoreSlot] |
There was a problem hiding this comment.
nit: document what it does if ignoreSlot is true.
| /// is `false`. | ||
| /// [IndexedSlot.index] is set to the index that the currently processed |
| @protected | ||
| List<Element> updateChildren(List<Element> oldChildren, List<Widget> newWidgets, { Set<Element>? forgottenChildren }) { | ||
| List<Element> updateChildren( | ||
| List<Element> oldChildren, List<Widget> newWidgets, {Set<Element>? forgottenChildren, bool ignoreSlot = false} |
There was a problem hiding this comment.
The ignoreSlot argument is a little awkward. Could RenderObjectElement have a createSlotForChild method that by default returns an IndexSlot and the Table could override that to return its own slot version? Or something like that?
There was a problem hiding this comment.
Make sense.
Fixed as your suggestion!
| // This class ignores the child's slot during [_TableElement.mount] and [_TableElement.update] | ||
| // phase, and then updates the slot to [_TableSlot] after completing the update of the child list. | ||
| @override | ||
| Object? createSlotForChild(int index, Element? previousChild) => null; |
There was a problem hiding this comment.
I was actually hoping that this could create the TableSlot?
| @@ -370,7 +403,10 @@ class _TableElement extends RenderObjectElement { | |||
|
|
|||
There was a problem hiding this comment.
In the mount above, we should be able to calculate the right TableSlot and just pass it into inflateWidget, no?
There was a problem hiding this comment.
So, the only remaining problem is the use of updateChildren in update then? It looks like we could figure out the appropriate slot value for each child we pass to updateChildren right there. Maybe updateChildren could take an optional list of slots (with the same length as the newWidgets list) or a function that generates a slot based on the index of the widget - and when provided the slot from that list or function is used instead of the default IndexSlot?
There was a problem hiding this comment.
Please let me know if did not explain it clearly.
There was a problem hiding this comment.
I am not sure I understand what you mean...
|
Continue working by #78081 |
Re-work of #69670
See #69670 for more process details.
@Hixie Hi,
I admit that the solution looks a bit unconventional, but I think this is due to the peculiarities of the table widget and has to do it.
In fact, this is a bug with a very clear root cause, but we cannot solve it easily, the
IndexedSlotis designed to solve the scene of this issue, but we cannot use this.1, In order to be compatible with the old API, we cannot introduce
IndexedSlot,2, should not match keys at different rows also causes us to be unable to introduce the
IndexedSlotLooking forward to discussing a perfect solution.