Flutter performance is a crucially important aspect of any modern cross-platform mobile app written with the SDK. Skipped frames and freezes hurt app’s usability and leave a bad impression on users. And if a slow e-commerce app is just a bad app, a stuttering game is a completely unusable app. To feel smooth, the app should maintain 60 FPS (frames per second) most of the time, or in other words, change frames every 16.66 milliseconds (for 120 Hz screens that are supported, the value is 8 milliseconds). Modern apps use many visual elements at once, making it a heavy load on the device’s hardware. Improving the app’s code and architecture is the main way to make it perform better.
We have already proven this in practice, since our company became a pioneer in the field of cross-platform Flutter development in 2019. And today we have 45+ successful projects under our belt in such industries as fintech, retail, healthtech, corporate applications.
Since the first release, Google has never stopped developing Flutter. The third version has been a game-changer in frontend development at the required stability level. Now, Flutter allows using a single code base to create applications for 6 major digital platforms: iOS, Android, web, Linux, macOS, Windows.
From our experience, we are confident in Flutter as the optimal technology for creating a multiplatform app (mobile, web,desktop). Here are some points in favor
- Transparent benefit for business is a cost-saving option: fewer developers are involved in the project that results in fewer development hours and reduced expenses.
- From the point of the flow, another benefit is easier and more transparent communication: a single development team needs less management as compared to two native teams or two native and one web team.
- Adaptability is another point worth mentioning: it is possible to configure PWA out of the box, while some of the features can be taken from a ready-made solution and adapted to the specific business needs.
- Testing goes easier and faster as the code shall be checked once for all platforms.
In this article we are going to dwell upon Flutter app performance and the ways to improve it.
Flutter performance vs other platforms
Developed by Google, Flutter is a cross-platform framework that runs on Dart coding language and is one of the most powerful in terms of performance. Let’s briefly overview how its performance compares to other mobile app technologies.
Flutter vs React Native performance
For more details read our comparison of Flutter and React Native.
Flutter vs Ionic performance
The Ionic cross-platform framework uses Web View, a system component responsible for opening web pages in apps. It means that for UI development we use typical web technologies, such as html and css. In simple words, we just create a browser inside our mobile app, and it displays the UI. That’s why UX in mobile apps created with Ionic differs much from those powered by Flutter or React Native. And low performance is just one of the eventual issues.
To learn more about the differences between the two technologies, check our Flutter vs Ionic article.
Xamarin vs Flutter performance
As we tell in our Xamarin vs Flutter overview, under close monitoring both frameworks offer nearly native performance. However, Xamarin performance depends highly on the type of Xamarin framework used. If Xamarin.Android and Xamarin.iOS with more platform-specific code provide great performance, Xamarin.Forms that shares more code across platforms, performs worse. Also, with Xamarin, a developer might need to develop many UI components separately for iOS and Android, meaning that the development of UI-heavy apps might be significantly slower.
If we consider the previous two technologies in the context of the present day, Xamarin and Ionic have lost their popularity. While Flutter confidently leads among the most often used cross-platform frameworks.
Flutter vs native performance
When it comes to native platforms (apps written on Swift for iOS and Kotlin for Android), their performance tends to be better than of any cross-platform technology. The reason why is in contests ‘Flutter vs Kotlin performance’ or ‘Flutter vs Swift performance’ (here we want to highlight that Flutter isn’t a programming language but a framework. And it will be more reasonable to compare Dart, a language used in Flutter, with Swift and Kotlin) the native platform will always be a winner: its code is compiled in the same format as the device’s native one, and it uses less memory in virtually similar apps. However, thanks to the AOT (ahead of time) compiler and highly-optimized Skia and Impeller rendering engines, the performance of most Flutter apps is usually on par with native ones, so users won’t notice much of a difference.
Surf case study: For one of our clients we’ve created their own video streaming platform successful enough to compete with YouTube. In this project Flutter proved to be suitable for developing high-performing services: it works with plenty of complex animations and is processing a routine load of 25 thousand requests and had no trouble handling a load of 50 thousand requests.
To learn what better suits your project: Flutter or native frameworks, read our dedicated article.
How to measure Flutter performance
We measure performance in the Profile mode using a real device (preferably a low-end one). Performance testing in Flutter apps can be done in several ways, since the framework provides a wide array of options for tests and performance measurement. Below, we overview the most popular ones.
One way to conduct Flutter performance tests is to use the Performance widget. The overlay widget displays two graphs on top of the app. The top ‘GPU’ graph shows the raster thread performance, in other words, communication between the app’s layer tree and the device’s GPU. Despite being named ‘GPU’, the graph shows how CPU resources are utilized. The lower ‘UI’ graph shows the UI thread, which includes the written code executed by Flutter’s framework.
If a frame is shown for more than 16.6 milliseconds (meaning the app’s performance drops below 60 FPS), the blue graph will drop down and reveal white background and you’ll see a red vertical line. If this occurs in the ‘FPS’ graph, that means the visual elements on the screen are too complicated to render in time, if it occurs in the ‘UI’ graph — the Dart code is too expensive to execute in time. If both graphs display a red bar, start by examining the UI thread for possible issues.
The performance overlay can be launched in several ways, including:
- The Flutter inspector. Launch the app in the Profile mode, open DevTools and toggle the Inspector view. There you’ll find the Performance Overlay button.
- The command line. Use flutter run –profile command and then the P key to turn on the performance widget.
Learn more about Flutter performance profiling from the official Flutter documentation.
With the performance view, which is accessible from DevTools, you can learn about the app performance via three tools.
- Flutter frames chart
- Timeline events chart
- CPU profiler
- Rebuild Stats. Contents the list of widgets reused in the app for multiple times. UI low performance in Flutter apps is usually caused by a case when the widget is redrawn too often, hundreds or even thousands of times in a few seconds. This is indicative of some code problems. Rebuild stats helps detect what widget causes the performance problem.
Data from the Performance view can be exported and imported. We also suggest checking out Flutter documentation to learn more about the Performance view capabilities.
The described tools are rather complicated, so we recommend using them when you’ve already checked all the simple aspects, but still have problems with the performance of the app.
The app performance can also be measured with Flutter performance benchmark tests, which are done via integration testing. The tests show such metrics as startup time, battery usage and jank (skipped frames).
How to optimize Flutter performance
Avoid overloading build method
The repetitive and costly build() method consumes excessive CPU power. This can happen if you use a large Widget with a large build() function. It is better to split such a Widget into smaller ones based on encapsulation and how they change. For example, localize the setState() call to the part of the subtree (in other words, a child of a node) that requires UI changes. If setState() is called too high in the tree, it rebuilds all descendent widgets of the tree.
Use const Widgets
Try to apply const widgets wherever possible. In this case, one object will be created for const widgets with the same configuration, which will be reused in the application.
Use Opacity only when necessary
Opacity Widget makes the widget rebuild every frame, which may cause Flutter performance issues, especially if there’s animation present. Also, if the Opacity parameter is equal to or very close to zero, it makes no sense to display such a widget, because it will not be visible to the user, but the framework will still display it, consuming additional resources. Opacity applied directly to an image consumes fewer resources compared to the Opacity widget. Also, go for TransparentImage, AnimatedOpacity, FadeInImage or FadeInTransition widgets instead of the Opacity widget to improve performance.
Avoid calls to savelayer
Calling saveLayer() is taxing on the hardware and should be avoided when possible. Widgets that can potentially trigger saveLayer() operation are: Text (if an overflowShader is used); Chip (if disabledColorAlpha != 0xff); ColorFilter and ShaderMask. Also, to avoid calling saveLayer() tweak the borderRadius widget’s property to round corners of a rectangle, instead of using ClipRRect.
Build and render in 16ms
If you notice the app’s skipping frames, review which frames take more than 16 milliseconds to build and render. Because there are separate threads for building and rendering, to achieve 16ms or less in total, you should aim at each frame to be built in 8ms or less and rendered in 8ms or less.
If frames are rendered in less than 16ms it won’t make a significant visual change for users, but might improve battery life and cause less heating of the device. Read more on rendering best practices on Flutter documentation portal.
Choose SizedBox instead of Container
To create a box or whitespace of specified dimensions use the SizedBox widget, which is responsible only for the size of the component, rather than Container, which contains all kinds of widgets of layout, size, color, shape, etc.
Use const SizedBox.shrink() to display the void.
Use pre-built child subtree with AnimatedBuilder
If you use the AnimatedBuilder widget, it’s not recommended to put a child of a node that doesn’t depend on animation in the builder function, because it will rebuild the subtree on every tick of animation. Better build the subtree once and pass it as a child parameter to the widget.
Do not use ListsView for long lists
If a list is not fully present at once on a screen, create it with the ListView.builder constructor, rather than ListView() or Column(). Only currently displayed list elements are stored in the memory. If the element is not displayed on the screen, it doesn’t occupy any memory space. This results in memory optimization and raises the performance.
Say ‘no’ to splitting widgets into methods
If you have a large building method with multiple nesting levels, the first idea might be to split it into separate methods. However, this will force Flutter to rebuild all child widgets every time the parent widget rebuilds, even if some of them are completely static. To prevent wasting CPU power on repetitive rebuilding, split complex widgets into smaller StatelessWidgets.
To sum things up
Flutter performance optimization has a good reputation and the framework is known among developers for how it provides nearly native performance even in apps with heavy visual effects. One of the examples is an app developed by Surf for The Hole video-streaming platform — despite our doubts, Flutter handled animations easily and provided smooth and responsive playback controls. Nevertheless, it is always recommended to keep monitoring and follow best performance practices to reduce risks of app stuttering, errors or skipped frames to the minimum. Hope our short guide will help you in achieving your development goals!If you’re currently thinking about building your mobile app, why not handle the task to professionals? We at Surf have extensive experience building apps for various businesses and will be glad to work on your project. We created the first Flutter-powered banking app in Europe.