Want to see beautiful graphs like this that show what your app is building and painting on every frame in the Dart Observatory like this?
Turn on profiling of builds and paints by setting
debugProfileBuildsEnabled (from flutter/widgets.dart) and
debugProfilePaintsEnabled (from flutter/rendering.dart) to
NOTE: Doing this will only enable detailed profiling in debug builds. If you want to see the same details in a
flutter run --profile build, apply this patch to the flutter code you're using by running
git am --signoff < profiling.patch
Launch your app via
flutter run --hot or
flutter run --profile
Upon running flutter you should see something similar to the following trace:
The Observatory debugger and profiler is available at: http://127.0.0.1:53350/
Go to that link in your browser. This is the Dart Observatory for your flutter app.
On the middle left, click view timeline.
On the next page set Recorded Streams Profile to All
Now you're ready. Click clear in the top right, do the thing you want to see the performance of in your flutter app, then click Refresh.
You‘ll now see a bunch of lines. Navigate the chart like an FPS with WASD. You’ll likely need to pan over to where the action is then zoom in on it.
Once you're zoomed in, click on one of the solid colored boxes and press G. Red lines will show the frame boundaries. Your goal is to reduce all the build/layout/painting work done by flutter to happen in less width than the span between two red lines.
Building causes layout. Layout causes painting. Try to do as little rebuilding as possible. Tricks for doing that are as follows:
Localize state changes to just rebuilding those widgets that need to change due to the state change.
If there is a large widget tree between where the state changes and where its used use InheritedWidgets to pass that state down without requiring the entire tree to rebuild. For examples of how we do this, explore Model and its subclasses.
Using Opacity requires the system to do something slow. Things that can help with this:
Move Opacity widgets as far down the tree as you can so they are covering the smallest area possible.
WARNING! You can also use the Color.withOpacity trick with Text.style.color but if you are animating that opacity its cheaper to use an Opacity widget as relayouting text is expensive and changing a Text.style's color causes a relayout.
RepaintBoundary is useful to isolate a parent from its animating children. If you find more in your tree is being rebuilt and repainted than you expect try wrapping the changing child in a RepaintBoundary.
LayoutBuilders rebuild their children in the following situations:
Their parent is rebuilt.
The constraints given to the LayoutBuilder change.
Their descendant rebuilds and the LayoutBuilder doesn't have tight constraints.
Of those three, the fact a descendant of the LayoutBuilder can cause the entire LayoutBuilder tree to be rebuilt is the most surprising. To avoid this, ensure you are giving your LayoutBuilder tight constraints.