Here’s a little snippet that I made a couple of years ago that seems to be following me from a Flutter project to another.

It’s a little class that I call AdaptiveImageProvider:


import 'package:flutter/painting.dart';

class AdaptiveImageProvider extends ImageProvider {
  AdaptiveImageProvider(String url) : _delegate = _resolve(url);
  final ImageProvider _delegate;

  static ImageProvider _resolve(String url) {
    final uri = Uri.parse(url);
    switch (uri.scheme) {
      case 'asset':
        final path = uri.toString().replaceFirst('asset://', '');
        return AssetImage(path);
      case 'file':
        final file = File.fromUri(uri);
        return FileImage(file);
      case 'http':
      case 'https':
        return NetworkImage(url);
      default:
        throw ArgumentError('Unsupported scheme: ${uri.scheme}');
    }
  }

  @override
  ImageStreamCompleter load(Object key, DecoderCallback decode) =>
      _delegate.load(key, decode);

  @override
  Future<Object> obtainKey(ImageConfiguration configuration) =>
      _delegate.obtainKey(configuration);
}

You can give this class pretty much any URL and it will poop out an ImageProvider that knows how to display an image using that URL.

“That’s cool, I guess. But why?”

Let’s imagine we have a widget called Avatar. We give it a url that points to an image, and it will be displayed in the shape of a circle:

class Avatar extends StatelessWidget {
  const Avatar({required this.url});
  final String url;

  @override
  Widget build(BuildContext context) {
    return ClipOval(
      child: Image(
        image: AdaptiveImageProvider(url),
        width: 56,
        height: 56,
        fit: BoxFit.cover,
      ),
    );
  }
}

In our app, users can upload their own pictures to set as their avatars.

As such, there are three possible URLs that the Avatar widget has to handle:

  1. user hasn’t uploaded their avatar image - use a preloaded image, e.g. assets://images/person.png
  2. the user is offline - display a locally cached image, e.g. file://path/to/image.png
  3. someone is viewing the users’ profile for the first time - display an image from the web, e.g. https://example.com/images/abc123.png

The AdaptiveImageProvider class makes this easy - just provide a url and it will figure out how to display it. And since it’s extending from the ImageProvider class that comes from Flutter, you can use it with popular libraries, such as cached_network_image and others.

Other use cases

Although the above use cases are the more common ones, the list of possible applications doesn’t end there.

Here are some more:

  • making an in-memory mode of your app where nothing goes to server, where server sync is a premium feature
  • running your app in a “lorem ipsum” mode with placeholder asset images to automate taking app store screenshots in multiple languages
  • creating a “retail demo” version of your app for Apple, so that they can showcase it in Apple Stores (this actually happened at my previous workplace)

I’m sure there are more possible applications, but here are the ones from the top of my head.