Performance Optimizations

How to see Performance Tracing

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?

  1. Turn on profiling of builds and paints by setting debugProfileBuildsEnabled (from flutter/widgets.dart) and debugProfilePaintsEnabled (from flutter/rendering.dart) to true.

    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

  2. Launch your app via flutter run --hot or flutter run --profile

  3. 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.

  1. On the middle left, click view timeline.

  2. On the next page set Recorded Streams Profile to All

  3. 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.

  4. 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.

  5. 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.

  6. Profit!


Build as little as possible

Building causes layout. Layout causes painting. Try to do as little rebuilding as possible. Tricks for doing that are as follows:

  1. Localize state changes to just rebuilding those widgets that need to change due to the state change.

  2. 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.


Reduce Opacity usage

Using Opacity requires the system to do something slow. Things that can help with this:

  1. Move Opacity widgets as far down the tree as you can so they are covering the smallest area possible.

  2. If you‘re opacitizing a solid color image, use Image.color with Color.withOpacity to set that image’s opacity instead of using an Opacity.

  3. Similarly, if you're opacitizing any other widget with a solid color, using Color.withOpacity to set its color without using an Opacity.

  4. 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.


Reduce Clipping

Try to reduce your use of ClipRect, ClipRRect and ClipPath as much as possible.


Try not to draw Paths

Just a curiousity I discovered in trying to implement RoundedCornerDecoration: Canvas.drawPath is much slower than Canvas.drawDRRect.


Use RepaintBoundary

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.


Be careful with LayoutBuilder

LayoutBuilders rebuild their children in the following situations:

  1. Their parent is rebuilt.

  2. The constraints given to the LayoutBuilder change.

  3. 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.