You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add a way to know if a ComponentElement is executing its build method or not #49505
provider's upcoming feature, select, is a way to consume InheritedWidgets that works only inside the build method.
Trying to call select inside other life-cycles (including didChangeDependencies) is not supported.
To make sure that people do not mistakenly still use it in a place where it shouldn't be used, provider would like to add an "assert" that fails if the BuildContext is not in the progress of building.
Proposal
This would be implemented through a debug-only flag on ComponentElement.
Basically, within build that flag would be true, and outside of build it wouldn't.
This flag would be specific to a given element and represent StatelessWidget.build/StatefulWidget.build, which means it is false during initState/didChangeDependencies/...
changed the title [-]Add a way to know if a `ComponentElement` is building or not[/-][+]Add a way to know if a `ComponentElement` is executing its build method or not[/+]on Jan 26, 2020
Ahh ok. Why do you specifically need to know if you're in build though? Could you explain a bit more about the use case you want to support and what code won't work without this check (or what code is more dangerous without this check)?
This test showcase how without this flag, the library has no way of preventing users from calling select inside didChangeDependencies, which then fails silently.
It's based on a custom Inheritedwidget which works similarly to an InheritedModel.
But the "aspect" is a callback.
Since it's used almost always with a new closure instance, it means that the InheritedElement needs a way to somehow clear the outdated dependencies when the dependent rebuild.
Otherwise as the dependent keeps rebuilding, it'd end up stacking hundreds of different "Selector", which would all be executed whenever the Inheritedwidget may want to notify dependents.
I did it by clearing the previous callbacks if select is called in a new frame (tracked using addPostFrameCallback).
This works fine. But it means that dependencies added inside didChangeDependencies may get cleared with no way of being re-added.
It is not a problem (as the closure acts as a didChangeDependencies if needed) but may be unexpected to some users.
This could be summarised as "to work, select relies on a workaround to #12992 which makes it incompatible with didChangeDependencies".
To begin with, the reason #12992 can't be fixed easily is because of didChangeDependencies.
This feature purposefully gave up on that life-cycle to work.
The proposed reason to support this API seems dangerous to me. select looks like a cheap method on BuildContext to only rebuild when certain properties on an object you care about change, but it actually ends up adding more work each and every frame per property you select (?!), even if you use it correctly so that it is only called within build methods.
It's not every frame.
It's only on InheritedElement.notifyClient.
Ok - but that could get called every frame if you decide to update your model every frame for some reason, such as having it tied to some animated value.
Basically, it makes it very easy to end up doing something that's very expensive - and it seems like it could be rewritten in such a way where it could still deliver a lot of value but not make the very expensive thing so easy to do.
Ironically, the changes in #49527 kind of put this debug flag in an awkward position.
It wouldn't be possible to implement such a flag directly from ComponentElement anymore if State.didChangeDependencies is called from inside ComponentElement.build.
I suppose I'll just move it to StateElement instead
I'm still curious on how you'd implement such feature @dnfield though 😃
This is part of why it's not always ideal to expose flags like this - the change in #49527 is something that should help runtime performance.
As far as how to implement this - I would avoid encouraging people registering multiple callbacks Each select call right now triggers a whole bunch of notifications - instead it could either track state for the user or require that it only be called once with all of the relevant properties you want to subscribe to.
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.
Activity
dnfield commentedon Jan 26, 2020
I'm pretty sure you can already do this right?
this.owner.debugBuildingI think is what you want.rrousselGit commentedon Jan 26, 2020
No,
debugBuildingis global for the entire treeThis flag would be specific to a given element and represent StatelessWidget.build/StatefulWidget.build, which means it is false during
initState/didChangeDependencies/...dnfield commentedon Jan 26, 2020
But once you're
debugBuilding, all calls toinitStateanddidChangeDependencieshave to be finished anyway for all elements in the tree.You don't know if you're in the
buildfor your specific element, but you know you're after that phase of the lifecycle.dnfield commentedon Jan 26, 2020
Per the documentation on
buildScope, which setsdebugBuilding:lockState means no one can call setState. All state has to be finalized for this cycle by then.
rrousselGit commentedon Jan 26, 2020
But that is specifically what I want
Unrelated, but that's not true
I already use
debugBuildingfor another assertion, and it is true duringinitState/...rrousselGit commentedon Jan 26, 2020
A table of their difference:
[-]Add a way to know if a `ComponentElement` is building or not[/-][+]Add a way to know if a `ComponentElement` is executing its build method or not[/+]dnfield commentedon Jan 26, 2020
Ahh ok. Why do you specifically need to know if you're in build though? Could you explain a bit more about the use case you want to support and what code won't work without this check (or what code is more dangerous without this check)?
rrousselGit commentedon Jan 27, 2020
It is explained in the topmost comment.
It's also showcased in the following test of the branch that implement the said feature: https://github.com/rrousselGit/provider/blob/677173b78dbe9befb331d10b7438c8b04108e5a2/test/context_test.dart#L283
This test showcase how without this flag, the library has no way of preventing users from calling
selectinsidedidChangeDependencies, which then fails silently.dnfield commentedon Jan 27, 2020
All the top most comment says is that a new feature called select wants this. Why?
A quick look at the code doesn't really make it obvious to me . What about
selectmakes it unsafe to call from didChangeDependencies?rrousselGit commentedon Jan 27, 2020
For more information on the feature in itself, see https://twitter.com/remi_rousselet/status/1218180956139540480?s=19
It's based on a custom Inheritedwidget which works similarly to an InheritedModel.
But the "aspect" is a callback.
Since it's used almost always with a new closure instance, it means that the InheritedElement needs a way to somehow clear the outdated dependencies when the dependent rebuild.
Otherwise as the dependent keeps rebuilding, it'd end up stacking hundreds of different "Selector", which would all be executed whenever the Inheritedwidget may want to notify dependents.
I did it by clearing the previous callbacks if
selectis called in a new frame (tracked using addPostFrameCallback).This works fine. But it means that dependencies added inside didChangeDependencies may get cleared with no way of being re-added.
It is not a problem (as the closure acts as a didChangeDependencies if needed) but may be unexpected to some users.
rrousselGit commentedon Jan 27, 2020
This could be summarised as "to work,
selectrelies on a workaround to #12992 which makes it incompatible withdidChangeDependencies".To begin with, the reason #12992 can't be fixed easily is because of
didChangeDependencies.This feature purposefully gave up on that life-cycle to work.
4 remaining items
rrousselGit commentedon Jan 27, 2020
It's not every frame.
It's only on
InheritedElement.notifyClient.dnfield commentedon Jan 27, 2020
Ok - but that could get called every frame if you decide to update your model every frame for some reason, such as having it tied to some animated value.
Basically, it makes it very easy to end up doing something that's very expensive - and it seems like it could be rewritten in such a way where it could still deliver a lot of value but not make the very expensive thing so easy to do.
rrousselGit commentedon Jan 27, 2020
I don't see how that is more expensive than what InheritedModel does in general
It is almost identical to:
the extra code is because of #12992 and for some health checks
How? 😄
rrousselGit commentedon Jan 27, 2020
Ironically, the changes in #49527 kind of put this debug flag in an awkward position.
It wouldn't be possible to implement such a flag directly from
ComponentElementanymore ifState.didChangeDependenciesis called from insideComponentElement.build.I suppose I'll just move it to StateElement instead
I'm still curious on how you'd implement such feature @dnfield though 😃
dnfield commentedon Jan 28, 2020
This is part of why it's not always ideal to expose flags like this - the change in #49527 is something that should help runtime performance.
As far as how to implement this - I would avoid encouraging people registering multiple callbacks Each
selectcall right now triggers a whole bunch of notifications - instead it could either track state for the user or require that it only be called once with all of the relevant properties you want to subscribe to.rrousselGit commentedon Jan 31, 2020
The documentation is enough to discourage people using many selectors at once.
But having the ability to have multiple ones is critical and there's no reason to remove it.
Its better not to have such debug flag than to remove the ability to call
selectmultiple times.escamoteur commentedon Jan 31, 2020
Could other users besides provider benefit from this change?
rrousselGit commentedon Jan 31, 2020
flutter_hookneeded the exact same flag too. Although in that very specific case, I was able to implement it myself.rrousselGit commentedon Jan 31, 2020
Although now that I think about it, the changes in #49527 means flutter_hooks will need this flag too, as the one I hacked myself will no longer work.
github-actions commentedon Aug 22, 2021
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.