Synchronous BuildContexts | Decoding Flutter

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Such a nasty little edge case. Great that the compiler can catch it now!

👍︎︎ 6 👤︎︎ u/esDotDev 📅︎︎ Jul 12 2022 🗫︎ replies
Captions
[MUSIC PLAYING] TIYA CHOWDHURY: If you use the Flutter lint package, which as of its 2.0 release added the use BuildContexts synchronously rule, you may be seeing some new red squigglies in your Dart code. But what does this mean, use BuildContexts synchronously? If you include this rule to your linting setup, either by adding a dependency to your Flutter lint 2.0 or by specifying it yourself, your editor will start complaining about situations like this. Flutter knows that BuildContexts are unreliable after an asynchronous gap and it wants you to know that, too. The first follow-up question in better understanding this is, what are you talking about? Remember that the BuildContext passed to a widget is that widget's corresponding element in the element tree. Also remember that while lots of our application code is asynchronous, Flutter's build process is 100% synchronous. There are no awaits, no futures anywhere in the assembly of your widget tree. And finally, recall the unique relationship between elements and their widgets. As Flutter developers, we mostly write widgets. So it's easy to think that they are the primary object in the relationship. But widgets are ephemeral, living as briefly as one 1/20 of a second if your app is animating something across a high refresh rate screen. We see here that widgets come and go, but where possible, Flutter keeps elements around. This is a huge driver of Flutter's incredible performance, despite how wasteful it can seem to throw all these widgets away after only using them for a few milliseconds. Now I said Flutter keeps elements around where possible, and this distinction is a part of why asynchronous use of BuildContexts can be so unpredictable. There are many different scenarios that can make Flutter decide that some of your elements need to be refreshed, from scrolling, to navigation events, to dragging and dropping. But it's not worth trying to memorize them all. For our purposes today, let's just use the following rule of thumb. The more dramatically your widget tree changes from one frame to the next, the more likely Flutter is in need to update portions of your element tree. And for every new frame, it's always possible that a given widget's element has been removed from the tree. Unpredictable events from your user can force Flutter to reassemble the element tree at any moment. So the only safe way to build your widget is to avoid asynchronous usage of a BuildContext. But this is just a new lint. So if your code is anything like mine, when this lint arrived, you would have found some places where you were breaking the rule and probably your app wasn't catching on fire. So what's the deal here? Part of the issue is that after an asynchronous gap, your widget's BuildContext could be a ticking time bomb waiting to explode the second you touch it, or it could be totally fine. Let's look at some code. Consider this situation where a button's callback submits a form and then pops the current page. The new lint rule will tell you that this line is risky. But why? If you look up the lint online, you'll see a suggestion to return early if your widget is no longer mounted. And this gets to the crux of the issue, because certain events like user scrolling, navigation, and several others may have already removed your widget's element from the tree. It's unsafe to call the famous .of method, as passing stale BuildContexts to those methods can crash your app. Back to that lint. The recommended fix is to do this. The reliable way to confirm your BuildContext is still valid is to check the mounted property on a state class. And the fix works for this scenario, too, because if this widget is no longer mounted, something else may have already removed this page. But does this always work? Imagine this scenario, where instead of removing the page, you want to show a snack bar that informs the user of an action's outcome. Does the if not mounted return check still serve our purposes? Probably not, because even if the element has been disposed, we still want the user to see confirmation of their action. In that case, don't conditionally return early if the widget is unmounted, but instead hold on to the resources you need ahead of time and then use them after the asynchronous gap. Here's a tricky one. What about this code, which crosses an asynchronous gap then uses an [? at-risk ?] BuildContext to read the MediaQuery, update some layout calculations, and call setState. First and foremost, we know that if somehow the user has navigated away from this screen or scrolled past its widget in a list view that uses the builder constructor, then our MediaQuery.of call is doomed. Also, if this widget is no longer mounted, then we definitely don't care about updating any measurements for its children. So it's safe to use the mounted check. But what if it is still mounted? Can an [? age ?] BuildContext safely read the media query and give us up-to-date values? What if the user resized the window during the await? Good news. In this case, Flutter will do the correct thing, and you don't need to worry. Now it is worth noting that this code is a pinch contrived. Normally code like this lives in the main area of your build method. But if you watched the previous "Decoding Flutter" episode about didChangeDependencies, you may be spotting an opportunity to improve this widget's performance, just like we talked about in that episode. Because this code will only produce new values when the media query changes and the media query changing will always activate your widget's didChangeDependencies method, you can certainly move this code into that method and ensure it's only ever run when it can possibly produce new values. And this pattern is great, because it allows us to only perform calculations and cache values once, precisely when our old values grow stale. And our BuildContext is always synchronous and fresh. Asynchronous use of BuildContexts is one of those things that's fine until it isn't, which is the reasoning behind the new lint. For more info on Flutter, head to Flutter.dev. [MUSIC PLAYING]
Info
Channel: Flutter
Views: 38,004
Rating: undefined out of 5
Keywords: developer tutorials, Flutter, Flutter tutorials, Flutter developers, flutter app development, Flutter course, how Flutter works, Flutter best practices, Decoding Flutter, Google developers, flutter 101, get started with flutter, flutter apps, flutter widgets, build apps with flutter, flutter developer, flutter developers
Id: bzWaMpD1LHY
Channel Id: undefined
Length: 6min 15sec (375 seconds)
Published: Tue Jul 12 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.