Design Converter
Education
Software Development Executive - II
Last updated on Oct 31, 2023
Last updated on Aug 14, 2023
With the rising popularity of Flutter, understanding how to measure the performance of your Flutter application becomes crucial. Flutter profiling is a process that helps us understand the performance characteristics of our Flutter apps. As Flutter developers, we often need to make sure our apps run smoothly - that they start up quickly, render frames swiftly, and respond to user interactions without delay. To achieve this, we're going to explore how Flutter's powerful profiling tools can help us diagnose and solve performance issues.
When it comes to performance profiling in Flutter, running the app in Profile Mode on a physical device is recommended. This approach gives the most accurate performance characteristics of your app as it's closer to how your users interact with the application in real life.
Running a Flutter application on a physical device removes the performance disparities caused by the limitations or enhancements of simulators and emulators. It's critical to remember that these tools don't accurately emulate the hardware of real devices, leading to misleading performance results.
Simulators and emulators exist to provide a convenient environment for application development, not for measuring real performance. As developers, we need to understand that the performance experience on a real device can drastically differ from that on an emulator or simulator.
Flutter offers developers three modes: Debug Mode, Release Mode, and Profile Mode. However, Debug Mode introduces extra checks like assertions that are absent in the other modes, which can introduce jank or stalls in the app's performance. Hence, it's not ideal for performance measurements.
To launch your app in profile mode, open your launch.json file in VS Code and edit the flutterMode property as follows:
1 "configurations": [ 2 { 3 "name": "Flutter", 4 "request": "launch", 5 "type": "dart", 6 "flutterMode": "profile" 7 } 8 ] 9
In Android Studio and IntelliJ, you can use the Run > Profile... menu item.
From the command line, you can also easily start your Flutter app in Profile Mode:
1 flutter run --profile 2
Gain an edge in your Flutter development by understanding the different build modes that it provides. The mode you choose impacts the app's performance and can consequently affect the user experience.
Flutter Build Modes
The Profile and Release Modes signify the production readiness of your Flutter app. By running apps in Profile Mode, you can identify performance bottlenecks, while Release Mode gives the real-life performance attributes of your app.
Debug Mode is the first mode, which is great for development and testing, with Hot Reload being one of the key benefits. However, it is not suitable for performance profiling due to the additional checks and non-optimized code execution.
Profile Mode is the next and is similar to Release Mode but with some additional capabilities for profiling and tracing the app's performance. It doesn't have some of the debugging functionalities seen in Debug Mode, making it a great balance for profiling.
Release Mode represents the polished version of the app where all debugging information is stripped out, code is fully optimized, and we have the fast execution required for deployment.
Below is an example of setting the build mode in the Flutter command line:
1 flutter build --release 2
Flutter DevTools is a suite of debugging and performance profiling tools, delivered as a web app, which greatly assists us in improving our Flutter app’s performance. With the help of DevTools, we can delve into the intricacies of our Dart code and Flutter widgets to analyze performance issues.
DevTools offers a range of features, like a source-level debugger, a widget inspector, and a suite of additional tools that can efficiently profile the performance of a Flutter application. The use of DevTools provides visibility into how the Dart runtime is managing memory, what the current call stacks are, and many other pieces of information that we can use to optimize our Flutter apps.
Some of the features offered by DevTools include a detailed inspection of all Dart code, analyzing memory usage, viewing all widget rebuilds & repaints, logging diagnostics, and examining all existing widget trees. With these capabilities, we can ensure that our Flutter apps are streamlined and efficient.
One of the most useful tools provided by Flutter for profiling is the performance overlay. This tool graphically presents the performance of your Flutter application, thus allowing you to visually identify areas of your app that need performance optimization.
Performance Overlay in Flutter
Flutter's Performance Overlay is a graphical tool that displays statistics related to the current performance of your Flutter application, specifically in terms of the UI and raster threads. This allows developers to get a quick overview of the overall performance and spot potential areas needing optimization.
The Performance Overlay displays two graphs - one for the UI (User Interface) thread and one for the Raster thread. These graphs show where time is spent during each frame rendered by the Flutter engine. The vertical green bars represent the current frame.
1 Widget build(BuildContext context) { 2 return Scaffold( 3 appBar: AppBar( 4 title: Text('Flutter Profiling'), 5 ), 6 body: Center( 7 child: RaisedButton( 8 child: Text('Show Performance Overlay'), 9 onPressed: () { 10 debugPrint('Button pressed'); 11 SchedulerBinding.instance!.addPostFrameCallback((_) { 12 debugShowProfileModeDialog = true; 13 }); 14 }, 15 ), 16 ), 17 ); 18 } 19
In the Performance Overlay graphs, each bar signifies a rendered frame. When the graph goes over the white lines (denoting 16ms increments), this connotes that your app is lagging behind the recommended 60 frames per second.
Despite your direct interaction primarily with UI thread during the development process, Flutter app's performance greatly depends on the efficiency of the various threads it uses. Understanding these threads can provide additional insights into the app's performance.
Thread Structure in Flutter
Flutter makes use of multiple threads for executing different tasks to ensure smooth and fast execution. While the Dart code runs on the UI thread, other threads are in play in the background to maintain high performance.
Here are the roles of different threads in Flutter:
By understanding the role of each thread, we can pinpoint the cause of performance issues in our Flutter apps.
Getting a live visual representation of your app's performance can greatly aid in turning the tide on performance issues. By using the Performance Overlay, you can tackle performance issues one frame at a time.
Enable Performance Overlay: To toggle the display of the performance overlay, you can use the Flutter inspector, command line, or even enable it programmatically.
1 Widget build(BuildContext context) { 2 return MaterialApp( 3 home: Scaffold( 4 body: Center(child: Text('Hello, world!')), 5 ), 6 showPerformanceOverlay: true, //Turn on the performance overlay. 7 ); 8 } 9
This code example shows you how to enable performance overlay programmatically. It can sometimes be more convenient to have the code handle enabling the performance overlay, especially when working on a larger codebase or team.
Flutter’s performance overlay tool facilitates diagnosing performance issues in your Flutter app. By identifying issues in UI and GPU graphs, performance can be optimized effectively.
The UI graph in the performance overlay highlights the performance metrics related to your Dart code execution. If you see any red bar in the UI graph, it may indicate that some of your Dart code is computationally expensive. In such a scenario, it's recommended to profile your Dart code to identify the potential cause of the performance issue.
Performance issues can also stem from the raster thread (previously known as the GPU thread). A red bar in the raster thread on the performance overlay may indicate that the constructed scene is expensive to render, usually due to complex layer compositions.
By documenting and analysing the common performance issues and their solutions, Flutter has made it easier for us developers to optimize applications. Here we talk about a few practical approaches to solve such problems.
Solutions to Performance Issues
In cases where the UI graph is clean but the raster graph shows red bars, it implies your scenes may be easy to construct but expensive to render. Such workloads usually involve unnecessary saveLayer calls, complex clippings or shadows, or intersecting positive opacities with multiple objects.
If you identify the cause of the slowness to be an animation, Flutter offers a Slow Animations button that you can use to slow down animations by 5x, making it easier to analyze the performance.
1 // slow animations example 2 // slowing down a fading animation 3 4 FadeTransition( 5 opacity: controller, 6 child: const Icon(Icons.star), 7 ) 8
In this example, we have a fading animation on an icon. If we suspect this animation could be causing a performance issue, using the slow animations feature can let us conduct a more detailed analysis.
The efficiency of Flutter apps can also be influenced by offscreen layers and non-cached images. Let's learn how we can diagnose these issues.
Impact of SaveLayer Method on Performance: The saveLayer method is one of the most expensive operations in the Flutter framework. It's especially useful when applying post-processing effects to the scene, but it can significantly slow down your app if not used judiciously.
Techniques to Check SaveLayer Usage: You can check whether your scene uses saveLayer with the PerformanceOverlayLayer.checkerboardOffscreenLayers switch. Once the switch is enabled, run the app and check for any flickering boxes outlining images or groups of objects which indicate usage of saveLayer.
Caching plays an essential part in ensuring smooth performance, especially when dealing with images. RepaintBoundary helps in effective image caching in Flutter.
Importance of RepaintBoundary in Flutter Profiling: When a layout or painting change happens, the framework recursively reapplies these operations to the affected parts of the tree. A RepaintBoundary widget can be inserted at various points in the tree to prevent unnecessary repaints, which effectively saves the CPU and GPU time.
Situations When RepaintBoundary is Useful:
1 // A usage of RepaintBoundary in Flutter 2 Widget build(BuildContext context) { 3 return GestureDetector( 4 onTap: () { 5 setState(() { 6 xPosition += 10; 7 }); 8 }, 9 child: Container( 10 width: 200, 11 height: 200, 12 child: RepaintBoundary( 13 child: Stack( 14 children: [ 15 Image.asset('assets/img.jpg'), 16 Positioned( 17 left: xPosition, 18 top: 10, 19 child: Icon(Icons.circle, size: 20), 20 ), 21 ], 22 ), 23 ), 24 ), 25 ); 26 } 27
This example shows an image with an icon circling around when tapped. Here the RepaintBoundary prevents the entire tree from being repaint upon updating the position of the icon.
One of the easiest ways to boost the performance of your app is to limit the number of unnecessary widget rebuilds. The widget rebuild profiler helps you detect and tackle such performance problems.
Widget Rebuild Profiler
A widget rebuild is triggered every time you call setState. Some widgets rebuild more frequently than others, and these are the ones that can contribute to the "jank" in your app.
Importance of Widget Rebuild Profiler: The widget rebuild profiler is a tool that can assist you in identifying which widgets in your app are being rebuilt, and how often. This can be invaluable information when attempting to optimize your app's performance, as minimizing unnecessary widget rebuilds can have a significant impact on your app's speed.
Usage and Benefits of Widget Rebuild Profiler: You can find the count of rebuild widgets by using the Flutter plugin for Android Studio and IntelliJ. By reducing the count of widget rebuilds, you will be able to increase the frame rendering rate and reduce time complexity leading to improved performance.
Benchmarking is a helpful way of measuring the performance of your code over time. It is especially useful when you want to make sure that performance improvements or regressions don't go unnoticed.
Defining and Importance of Benchmarking: Benchmarking involves running a set of tests or procedures to assess the relative performance of your app, and identify potential areas for improvement. This process is critical as it provides quantifiable metrics that give you a clear understanding of where your application stands in terms of performance.
How to Create Benchmark Tests using Flutter Driver Library: Flutter Driver is a testing library that enables developers to write end-to-end integration tests and automate actions on the app. Using this, you can create and execute benchmark tests that can measure the performance of your app in terms of startup time, memory efficiency, etc.
1 // An example benchmark test 2 void main() { 3 IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 4 5 testWidgets('Benchmark test example', (WidgetTester tester) async { 6 await tester.pumpWidget(MyApp()); 7 8 final Timeline timeline = await tester.binding.traceTimeline; 9 final TimelineSummary summary = TimelineSummary.summarize(timeline); 10 11 // Save the summary to disk. 12 summary.writeSummaryToFile('scrolling_summary', pretty: true); 13 }); 14 } 15
In this simple example, we are running a benchmark test on 'MyApp' and generating a summary file that contains detailed insights about the performance.
As we cross the finish line of our detailed exploration of Flutter Profiling, let's pause to take stock of what we've learned and how we can implement these lessons to efficiently optimize our Flutter applications.
Through the lenses of Flutter run profile, Flutter build profile, and Flutter profile page, we've unlocked the diverse aspects of optimizing the performance of a Flutter application. From understanding the significance of Profile Mode, the magic of Flutter's multi-threading system, to actionable solutions for common performance issues and the power of benchmarking, Pandora's box of Flutter’s performance profiling has been laid bare for us.
Accelerating the performance of your Flutter application can seem a daunting task at first glance. But armed with the understanding of Flutter's profiling tools and techniques, you can comfortably rise to meet that challenge. Remember, achieving a smooth, efficient application is an iterative process, a continual dance of diagnosing problems and applying optimisations. But with the knowledge you now possess, you're well-equipped to manage this dance with grace and assurance.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.