♪ [intro music] ♪ As a Flutter developer, you're used to implementing features by wrapping parts
of your UI in new widgets. For example, maybe you start with a text widget. And then you think, "Well, actually this will eventually
have an image and a subtitle as well. Better use a <i>ListTile</i>. But, of course,
that's not quite right yet. A little elevation and rounded corners
are always nice. Time to wrap it in a <i>Card</i>. Better, but this also
needs to be <i>Dismissible</i>. Easy enough. There's a widget for that. Eventually, your UI looks pretty good, which is when you remember that this item will actually
be rendered inside of a loop. And now you've arrived at one of the time-honored
Flutter developer moments, breaking up an unwieldy build method. The first step in this process
requires you, the human developer to make a judgment call. You must identify which code blocks
are good candidates for re-use. Attributes that lend themselves
toward re-use are: repeated sequences of code and discrete sections of your actual UI. Often, these two things go hand in hand. In our example, it's the <i>Dismissible</i> widget and all of its children
that should be separated. It both repeats in our code and renders a contained section of our UI. Great! The second step is, of course,
to actually move the code. And this is the crux of what you and I
are chatting about right now, because there are
two possibilities before you with quite different implications. The first option is to move the code
into a <i>method</i> on the same widget. This is often called
the "helper method" option If you're anything like me, you probably started doing this when you first encountered
a big unwieldy built method. The second option is similar, but instead of moving your code block
into a method on the same widget, you move it into an entirely new widget. Similar but not the same. Given these two options,
which do you normally use? Helper methods or separate widgets? Again, if you're anything like me, you evaluated your options and thought, "Well, shoot, helper methods
pass all of my variables for free, and surely a helper method
is more performant than an entirely new class." I'll use helper methods. If that's you, I've got
good news and bad news. The good news is that
you're still a great developer and your users love your apps. The bad news, sadly, is that the answer here
has been counter-intuitive. And there are important reasons
to prefer separate widgets. Let's dive into it. First and foremost, remember that when <i>setState</i>
is called within a widget, its entire <i>build</i> method is rerun. This means that if a user
toggles a favorite icon in the corner of a large piece of your UI, having rendered that icon
in a helper method will require Flutter to rebuild
the entire wrapping widget. You may have heard that
there are multiple trees that Flutter uses to build your UIs, and the widget tree is only the top one. Creating a few more widgets
in that top tree is hardly ever going
to slow down your app, but unnecessarily rebuilding
whole sections of your UI can cause those deeper trees: the element tree
and the render object tree to waste precious CPU time. And what's worse, consider what happens
if you animate your icon between states. Now your app is unnecessarily redrawing
expensive UI chunks 60 times per second for the duration of the animation. when all it had to do was
animate the pixels inside the icon. So, instead of refactoring
that favorite icon into a helper method, use an entirely new widget up front, so that when it toggles states, Flutter is able to precisely target
what it re-renders. And for truly best results, use const constructors wherever possible as that allows Flutter to short-circuit
the most amount of unnecessary work. Congrats! Now your app is running faster
and everyone's happy. It's time to write some tests for your UI. So you open up that widgets file again to remind yourself what
to capture in a widget test. One obvious candidate is that the favorite icon
we've been talking about: changes color as expected. Which piece of code
would you rather write a test for? The helper method version? Or the separate widget version? The separate widget version
couldn't be simpler, whereas the helper method version requires you to reconstruct
every dependency needed by <i>_expensiveWidget1(),</i>
and <i>_expensiveWidget2(),</i> and they were expensive, so it's probably a lot. As if that's not enough,
there are other scenarios where you can accidentally
hold on to a stale <i>BuildContext</i>. Consider this code which uses a <i>Builder</i>. but accidentally uses a different name
for one of the builtContexts, allowing further nested code to use an old, unreliable builtContexts. Refactoring this into a separate widget
can make the bug impossible. Now our <i>MyIconButton</i>
only has access to one <i>BuiltContext</i>, and it's always the correct one. Yes, it's true that creating new widgets severs that free connection back
to all of the available attributes, Except, now that we've explored it more. It never really was free, was it? It always came at the cost
of performance, testability and occasionally, even accuracy. When I was speaking
about this issue with Remi Rousselet, the creator of Provider, Riverpod,
Freezed, and many other packages, he summarized a situation like this: "Classes have a better default behavior. the only benefit of methods is
having to write a tiny bit less code. There's no functional benefit." Thanks Remi, for helping draw attention to this issue
with some instructive Dart Pads. And that's it, folks. Next time you're breaking up
an unwieldy build method, don't be afraid of specialized widgets. You pressing a few more keys
during development, might just save your users
dropped frames during runtime. For more info on Flutter, head to <i>Flutter.dev</i> ♪ [outro music] ♪
This is actually really useful for a new developer.
Great to see a video about this