Boost your Flutter apps to the max with these 6 performance tips

Flutter category image

Get smoother scrolling and animations, less memory consumption, and more execution speed with these performance tips for Flutter apps.

Here are some easy-to-use best practices for Flutter apps when it’s all about performance. Use them if you notice any problems but don’t optimize prematurely. Flutter apps are fast by default and you won’t have any trouble usually.

Also, make sure to download my free ebook about Flutter performance tips. You can find the link at the end of this article.

Prefer StatelessWidget over StatefulWidget

A StatelessWidget is faster than a StatefulWidget because it doesn’t need to manage state as the name implies. That’s why you should prefer it if possible. Choose a StatefulWidget when…

▶ you need a preparation function with initState()
▶ you need to dispose of resources with dispose()
▶ you need to trigger a widget rebuild with setState()
▶ your widget has changing variables (non-final) In all other situations, you should prefer a StatelessWidget.

⛔ Bad example

import 'package:flutter/widgets.dart';

class MyWidget extends StatefulWidget {
	final String header;
	final String subheader;

	const MyWidget({Key? key, required this.header, required this.subheader}): super(key: key);

	@override
	State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
	@override
	Widget build(BuildContext context) {
		return Column(children: [Text(widget.header), Text(widget.subheader)]);
	}
}

✅ Good example

import 'package:flutter/widgets.dart';

class MyWidget extends StatelessWidget {
	final String header;
	final String subheader;

	const MyWidget({Key? key, required this.header, required this.subheader}): super(key: key);
	
	@override
	Widget build(BuildContext context) {
		return Column(children: [Text(header), Text(subheader)]);
	}
}

Use builder constructors on long lists or grids

When dealing with lists or grids with many items, it is recommended to use the builder constructors. Their advantage is that only visible elements are rendered. So if your list or grid is bigger than one device screen, you should choose this approach. For smaller lists and grids, the default constructors can also be used. This applies to all types of lists and grids like ListView, ReorderableListView, or GridView. Always check for a builder constructor and use it if possible.

✅ Good example

final List<int> _listItems = <int>[1, 2, 3, 4, 5, 6, 7, 8, 9];

@override
Widget build(BuildContext context) {
	return ListView.builder(
			itemCount: _listItems.length,
			itemBuilder: (context, index) {
		var item = _listItems[index];
		return SizedBox(
			height: 300, child: Center(child: Text(item.toString())));
		}
	);
}

Don’t use OpacityWidget

The Opacity widget can cause performance issues when used with animations because all child widgets of the Opacity widget will also be rebuilt in every new frame. It is better to use AnimatedOpacity in this case. If you want to fade in an image, use the FadeInImage widget. If you want to have a color with opacity, draw a color with opacity.

⛔ Bad example

Opacity(opacity: 0.5, child: Container(color: Colors.red))

✅ Good example

Container(color: Color.fromRGBO(255, 0, 0, 0.5))

Precache images and icons

Flutter contains a function precacheImage() which can be used to reduce the loading time of images and icons. You can use it in the initState() or didChangeDependencies() methods to speed up image display, for example.

const List<CatImage> images = [
	CatImage(width: 150, height: 150, caption: 'Watch out'),
	CatImage(width: 150, height: 160, caption: 'Hmm'),
	CatImage(width: 160, height: 150, caption: 'Whats up'),
	CatImage(width: 140, height: 150, caption: 'Miaoo'),
	CatImage(width: 130, height: 150, caption: 'Hey'),
	CatImage(width: 155, height: 150, caption: 'Hello'),
];

@override
void didChangeDependencies() {
	super.didChangeDependencies();
	for (CatImage image in images) {
		precacheImage(NetworkImage(image.url), context);
	}
}

Reduce memory consumption of ListView

A ListView has two properties (addRepaintBoundary and addAutomaticKeepAlives) that can lead to a high memory consumption in certain cases. They tell the ListView to not dispose of items which are not visible on the screen at the moment. In terms of performance, this is good because scrolling is fast. But the invisible items still occupy memory space. If you have a long list of images, this can lead to a problem. The solution is to set the mentioned properties to false (they are set to true by default).

ListView.builder(
	addAutomaticKeepAlives: false,
	addRepaintBoundaries: false,
	…
);

Always use keys

Every widget has a key constructor parameter and creates an element in the widget tree. A key allows us to reuse an element instead of creating a new one. You can either use ValueKeys or GlobalKeys. GlobalKeys can be tricky because there can never be multiple widgets with the same key in the widget tree. ValueKeys on the other hand will lead to bloaty code as you need to define them for each widget.

?

Tip

Do you want more tips? Check out my free ebook!

Boost the speed of your Flutter apps!

Flutter Performance Tips Ebook Cover

This ebook contains solutions to problems which are about slow app performance and high memory consumption.

Related articles