Improve flutter app performance: Essential tips and examples for Every Developer

Zaib Bilawal
7 min readJan 11, 2024

--

Flutter’s appeal in cross-platform app development is undeniable, especially for its smooth UIs. Yet, performance optimization remains key. In this article, we’ll explore some advanced strategies, examples enriched with code snippets to improve the performance of your Flutter app.

why my flutter app is slow ?

1. State management mastery

Efficient state management is crucial for responsive Flutter apps. Instead of just using the traditional `setState`, there are alternatives. One good option is the ‘provider’ package. In the provided example, a `CounterModel` class is created to manage the app’s state. This class extends `ChangeNotifier` and is used with `ChangeNotifierProvider` in the main app. The `CounterWidget` then uses the `Consumer` widget to access and update the state, displaying the current count and a button to increment it. This approach makes state management in Flutter more organized and efficient, improving the overall app performance.”

// Define a model
class CounterModel with ChangeNotifier {
int _count = 0;

int get count => _count;

void increment() {
_count++;
notifyListeners();
}
}

// In your main app
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(),
),
);
}

// Accessing and updating state in a widget
class CounterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<CounterModel>(
builder: (context, counter, child) {
return Column(
children: [
Text('${counter.count}'),
ElevatedButton(
onPressed: counter.increment,
child: Text('Increment'),
),
],
);
},
);
}
}

2. Widget tree optimization with const constructors

Optimizing the widget tree is important for efficient Flutter apps, and using const constructors can help prevent unnecessary rebuilds of stateless widgets. In the provided example, a stateless widget, `MyStatelessWidget`, is created with a const constructor. This means that the widget and its subtree won’t be rebuilt unless the constructor parameters change. In the `build` method, a `Text` widget is returned with the message ‘Hello, Flutter!’ wrapped in the const keyword. This ensures that the widget is created only once, reducing unnecessary rebuilding and improving the overall performance of the Flutter app.

class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget(); // const constructor

@override
Widget build(BuildContext context) {
return const Text('Hello, Flutter!');
}
}

3. Implementing lazy loading and pagination

Enhancing performance for Flutter apps dealing with large datasets involves implementing lazy loading and pagination. The ListView.builder is a useful widget for this purpose. In the provided example, the `itemCount` parameter specifies the total number of items in the dataset, and the `itemBuilder` function generates the individual list items as needed. This on-demand creation reduces memory usage and speeds up the rendering process, especially when dealing with extensive datasets. By incorporating pagination logic, you can efficiently load and display only a subset of items at a time, ensuring a smoother and more responsive user experience.

ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(title: Text('Item $index'));
},
);

4. Image compression and resizing

The provided code snippet demonstrates the use of the ‘flutter_image_compress’ library to efficiently resize and compress images. The ‘compressImage’ function takes a file as input, determines the file type (PNG or JPG), and generates an output path for the compressed image. The ‘compressAndGetFile’ method from the library is then used to resize and compress the image with specified quality, minHeight, and minWidth parameters. This approach helps reduce the file size of images, leading to faster loading times and a more responsive user experience in your Flutter app.”

import 'package:flutter_image_compress/flutter_image_compress.dart';

Future<File> compressImage(File file) async {
final filePath = file.absolute.path;
final lastIndex = filePath.lastIndexOf(new RegExp(r'.png|.jpg'));
final splitted = filePath.substring(0, (lastIndex));
final outPath = "${splitted}_out${filePath.substring(lastIndex)}";

var result = await FlutterImageCompress.compressAndGetFile(
file.absolute.path,
outPath,
quality: 88,
minHeight: 1920,
minWidth: 1080,
);

return result;
}

5. Using flutter devtools for performance profiling

To analyze and improve the performance of your Flutter app, leverage Flutter DevTools by running the app in profile mode. Use the command ‘flutter run — profile’ to initiate the profiling process. This enables Flutter DevTools to gather detailed performance data during runtime, allowing you to identify bottlenecks, understand resource usage, and optimize your app for a smoother user experience. By utilizing Flutter DevTools in profile mode, developers can make informed decisions to enhance the overall performance of their Flutter applications.

flutter run --profile

6. Efficient networking with caching

Manage network requests in your Flutter app by incorporating the ‘http’ package with caching headers. In the provided code snippet, the ‘fetchData’ function utilizes the ‘http’ package to perform a GET request to ‘https://api.example.com/data'. The request includes a ‘Cache-Control’ header with a directive of ‘max-age=3600’, indicating a caching duration of 3600 seconds (1 hour). This allows the client to cache the response for a specified time, reducing the need for repeated network requests and enhancing overall app performance by fetching data from the cache when possible.”

import 'package:http/http.dart' as http;

Future<http.Response> fetchData() {
return http.get(
Uri.parse('https://api.example.com/data'),
headers: {'Cache-Control': 'max-age=3600'},
);
}

7. Simple and efficient animation

Create simple and efficient animations in your Flutter app with the AnimatedContainer widget. In the provided example, the AnimatedContainer smoothly transitions between two states, toggling its width and height based on the ‘_isExpanded’ variable. The animation has a duration of 500 milliseconds, providing a visually appealing effect. This approach simplifies the implementation of animations, allowing developers to achieve smooth transitions effortlessly in response to state changes. Improve your app’s user interface with the ease and elegance of the AnimatedContainer widget in Flutter.

AnimatedContainer(
duration: Duration(milliseconds: 500),
width: _isExpanded ? 100 : 200,
height: _isExpanded ? 100 : 200,
color: Colors.blue,
)

8. Responsive design with mediaquery

Create responsive and adaptive layouts in your Flutter app by leveraging MediaQuery. In this example, the Container’s width and height are dynamically set based on the device’s screen dimensions. The ‘MediaQuery.of(context).size’ retrieves the screen size, and multiplying it by 0.8 (width) and 0.2 (height) ensures the container is proportionally sized. This approach enables your UI components to adapt seamlessly to various screen sizes, providing a consistent and visually appealing user experience. Enhance your app’s responsiveness with MediaQuery for flexible and responsive design implementation in Flutter.

Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.2,
child: Text('Responsive Container'),
)

9. Optimize app startup time

Your Flutter app’s startup time with the ‘flutter_native_splash’ package, ensuring a seamless and engaging experience for users. In your ‘pubspec.yaml’ file, configure the package by specifying background color, splash image, and platform-specific settings. The ‘color’ parameter defines the background color, and ‘image’ points to the splash image. Setting ‘android’ and ‘ios’ to ‘true’ enables platform-specific configurations. By integrating ‘flutter_native_splash,’ you enhance the visual appeal during app launch, contributing to a more polished and efficient startup for a positive user impression.

# In your pubspec.yaml
flutter_native_splash:
color: "#42a5f5"
image: assets/splash.png
android: true
ios: true

10. Memory management and disposal

Effectively manage memory in your Flutter app by disposing of controllers and listeners to free up resources. In the provided example, the ‘_controller’ instance of ‘AnimationController’ is initialized in the ‘initState’ method and disposed of in the ‘dispose’ method. This ensures proper cleanup when the widget is no longer needed, preventing memory leaks. By incorporating this approach into your widgets, you enhance the efficiency of memory management, contributing to a more responsive and stable Flutter application. Prioritize proper disposal to optimize your app’s overall performance and resource utilization.

class MyStatefulWidget extends StatefulWidget {
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
late final AnimationController _controller;

@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
// Widget code
}
}

Why are flutter apps slow?

Flutter apps may experience slowness due to several factors:

1. Heavy Widgets and Complex UI: Overusing complex widgets or deeply nested widget trees can lead to slower rendering.
2. Inefficient State Management: Mismanagement of app state, leading to unnecessary rebuilds of widgets.
3. Large Assets and Images: Using high-resolution images without optimization can affect performance.
4. Improper Use of Animations: Excessive or poorly optimized animations can slow down the app.
5. Development Mode: Flutter apps run slower in debug mode compared to release mode due to additional checks and debugging features.

How to increase flutter app speed?

To enhance the speed of a Flutter app:

1. Optimize State Management: Use efficient state management solutions like Provider, Riverpod, or Bloc.
2. Optimize Widgets and Rendering: Simplify UIs, use const constructors, and avoid unnecessary widget rebuilds.
3. Image Optimization: Compress and cache images, and use appropriate sizes.
4. Efficient Animations: Use built-in animation widgets and avoid complex animations on critical paths.
5. Profile and Optimize: Regularly profile your app with Flutter DevTools and optimize based on findings.

Disadvantages of flutter app

Some disadvantages of Flutter include:

1. Large App Size: Flutter apps tend to be larger than native apps due to the inclusion of the Flutter engine and framework.
2. Limited Third-Party Libraries: While growing, Flutter’s ecosystem is not as extensive as native development environments.
3. Platform-Specific Features: Implementing native functionality requires additional work using platform channels.
4. UI Rendering Issues: Some complex UI elements might not render as smoothly as in native apps, especially on older devices.

Optimizing flutter APK size

To reduce the APK size of a Flutter app:

  1. Minimize Resource Files: Use smaller assets and compress images.
    2. Use Proguard/R8: Enable Proguard or R8 for Android to remove unused code.
    3. Split APK per ABI: Build separate APKs for different Android ABIs to reduce size.
    4.Tree Shaking:
    Flutter’s tree shaking removes unused code during the release build.
    5. Avoid Unnecessary Packages: Be selective with external packages to keep the app lightweight.

Conclusion:

Making your Flutter app work smoothly is very important for users to enjoy it. You can make your app work even better by following these tips. Remember, making your app perform well is an ongoing thing, so it’s important to regularly check and test it to keep it working really well.

Thanks, everyone! That’s all for now. If you found this helpful, give it a 👏 and share your thoughts in the comments.

--

--

Zaib Bilawal

𝗦𝗘𝗢 𝗘𝘅𝗽𝗲𝗿𝘁 with 3+ years of experience. I help businesses to rank on Google top