Mobile Development 8 min read

Improving Image Loading Experience in Flutter Mobile Apps

This article explains how to enhance image loading in Flutter mobile applications by using placeholders, skeleton screens, error handling, thumbnail generation, and the BlurHash library, providing code examples and practical recommendations for a smoother user experience.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Improving Image Loading Experience in Flutter Mobile Apps

Images are the most eye‑catching visual elements in mobile applications; colorful and attractive pictures can stimulate user clicks and improve conversion rates, but poor loading experiences can significantly degrade usability.

When network speed is slow, image areas may appear blank, making the UI unpredictable; a good user experience should present an expected result rather than leaving users guessing.

One common solution is to use the CachedNetworkImage plugin in Flutter and its placeholder property to display a placeholder widget while the image loads. The following code demonstrates this approach:

class TextImageListItem extends StatelessWidget {
  final String imageUrl;
  final String text;
  const TextImageListItem({
    Key? key,
    required this.imageUrl,
    required this.text,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(15.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          CachedNetworkImage(
            imageUrl: imageUrl,
            placeholder: (context, string) {
              return Container(
                width: 150,
                height: 100.0,
                color: Colors.grey[300],
                child: Icon(
                  Icons.image,
                  size: 20.0,
                  color: Colors.grey[400],
                ),
              );
            },
            width: 150,
            height: 100.0,
          ),
          const SizedBox(width: 10.0),
          Expanded(
            child: Text(
              text,
              maxLines: 3,
              style: const TextStyle(
                overflow: TextOverflow.ellipsis,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

For skeleton screens, the skeletons plugin can be used to show a loading skeleton that matches the list view layout. The simplest usage is shown below:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text('图文列表'),
    ),
    body: Skeleton(
      isLoading: _isLoading,
      skeleton: SkeletonListView(),
      child: ListView.builder(
        itemBuilder: (context, index) {
          return const TextImageListItem(
            imageUrl: 'https://th.bing.com/th/id/OIP.dh9AWBD2vmtsJF6MLwxFdwE9DE?w=277&h=180&c=7&r=0&o=5&pid=1.7',
            text: '没有 UE 给交互效果怎么办?照着原型开发行不行?一张草图打天下?开发全凭项目经理一句话?这些情况下,如何保障用户体验?本专栏将通过实例来讲如何面向用户体验开发。',
          );
        },
        itemCount: 20,
      ),
    ),
  );
}

When an image fails to load due to network timeout or a missing file, the errorWidget property of CachedNetworkImage can display an error placeholder and optionally a retry button. The following snippet shows how to implement this:

CachedNetworkImage(
  imageUrl: imageUrl,
  placeholder: (context, string) {
    return Container(
      width: 150,
      height: 100.0,
      color: Colors.grey[300],
      child: Icon(
        Icons.image,
        size: 20.0,
        color: Colors.grey[400],
      ),
    );
  },
  errorWidget: (context, string, obj) {
    return Container(
      width: 150,
      height: 100.0,
      color: Colors.grey[300],
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            Icons.image_not_supported_outlined,
            size: 20.0,
            color: Colors.grey[400],
          ),
          Text(
            '图片加载失败',
            style: TextStyle(
              color: Colors.grey[400],
              fontSize: 12.0,
            ),
          ),
        ],
      ),
    );
  },
  width: 150,
  height: 100.0,
),

To avoid performance problems caused by loading large original images, it is recommended that the backend generate appropriately sized thumbnail images for list views, reducing bandwidth and preventing UI jank.

An advanced technique is to use the BlurHash library, which encodes an image into a short string (20‑30 characters) that can be decoded on the client to display a blurred placeholder, offering a more visually appealing alternative to plain placeholders.

Summary

Show placeholders or skeleton screens during loading; consider using BlurHash for a blurred preview.

Handle load failures gracefully by providing error widgets and a retry mechanism.

Control image file size by serving backend‑generated thumbnails to keep list performance smooth.

FlutterPlaceholderMobile UIError HandlingBlurHashImage LoadingSkeleton Screen
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.