Improving perceived performance with image placeholders, precaching, and disabled navigation transitions

Per Classon
May 26, 2020 · 4 min read

Perceived performance is how fast an application feels to the user. This article covers three strategies that you can use in your application to improve perceived performance: image placeholders, precaching images, and disabling navigation transitions.

Image placeholders to prevent content from jumping around

When a user is waiting for images to load, and then they eventually show up, the layout can shift around. By leaving space in the layout for image placeholders, you can avoid this shifting to ensure a better user experience.

See the following GIF for an example of how it can look without using any placeholders:

See full interactive example on DartPad.

If you already have a placeholder image cached and loaded in your application you can use the FadeInImage widget to show placeholders. If you want to use a widget instead of an image as a placeholder, you can achieve this with the Image.frameBuiler property.

The Image.frameBuilder property is responsible for building the Image widget and it has four arguments:

  1. The build context.

When implementing a placeholder widget, first check whether the image has already been loaded with wasSynchronouslyLoaded and, if so, return the child. If not, use AnimatedSwitcher to create a cross-fade between the placeholder and the image as it loads:

class ImageWidgetPlaceholder extends StatelessWidget {
const ImageWidgetPlaceholder({
Key key,
this.image,
this.placeholder,
}) : super(key: key);
final ImageProvider image;
final Widget placeholder;
@override
Widget build(BuildContext context) {
return Image(
image: image,
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded) {
return child;
} else {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: frame != null ? child : placeholder,
);
}
},
);
}
}

After adding placeholders, the layout no longer shifts around, and instead the images fade in as they load:

See full interactive example on DartPad.

Precaching images before they are displayed

If your app has a splash or welcome screen before images are shown, you can also precache those images by calling the precacheImage function.

precacheImage(NetworkImage(url), context);

The following GIF shows an example of precaching images on a Welcome screen:

See full interactive example on DartPad.

Disabling navigation transitions on Flutter web

Navigation transitions occur when a user moves between pages, and it can be a great way to let the user orient themselves in a mobile application. However, for web applications, it’s not something you would typically see. For a perceived performance improvement, you can disable the page transition animation.

By default, MaterialApp uses page transitions for routing relevant to the platform (slide in upwards for Android or from the side for iOS). To override this behavior, you can create your own PageTransitionsTheme class. To detect when the application runs on the web, use the kIsWeb constant. If it is on the web, disable the transition by returning the child:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class NoTransitionsOnWeb extends PageTransitionsTheme {
@override
Widget buildTransitions<T>(
route,
context,
animation,
secondaryAnimation,
child,
) {
if (kIsWeb) {
return child;
}
return super.buildTransitions(
route,
context,
animation,
secondaryAnimation,
child,
);
}
}

Set the pageTransitionsTheme for our MaterialApp:

MaterialApp(
theme: ThemeData(
pageTransitionsTheme: NoTransitionsOnWeb(),
),
)

The page transition without any animation:

See full interactive example on DartPad.

Conclusion

I hope you found some useful tips in this article for how to improve the perceived performance in a Flutter web application. For the Flutter Gallery, we disabled the page transitions on the web and added placeholders for images to avoid a layout shift while loading. The implementation is similar to what is described in this article, and if you want to see the code you can find it on GitHub.

Thank you for reading!

This post is a part of a series about what we learned when improving performance for the Flutter Gallery. Articles in the Creating performant Flutter web apps series:

Flutter

Flutter is Google's mobile UI framework for crafting…

Flutter

Flutter is Google's mobile UI framework for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source. Learn more at https://flutter.dev

Per Classon

Written by

Software Engineer 👨🏼‍💻

Flutter

Flutter is Google's mobile UI framework for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source. Learn more at https://flutter.dev

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store