Conditional imports across Flutter and Web

Antonello Galipò
Flutter Community
Published in
4 min readJul 18, 2020
http://www.theoutplay.com/wp-content/uploads/2017/05/crossroads-1280x471.png

Flutter Web is growing in popularity and it’s becoming more and more mature as the time passes. We’re not there with perfection, there are still some issues but hopefully they’re going to be solved soon!

If you’ve had the chance to fiddle around with a Flutter project that targets you might have found yourself trapped in this situation:

I need this feature, I could use this plugin that already does that, but it doesn’t support Web.

Then you answer yourself something like this:

Well I’ll wrap it into a factory, if kIsWeb is true then I’m on Web and return the Web-compatible instance!

Well, yes. And no. Let’s break this down.

Since I first encountered the problem when adding Auth0 authentication in my app, I’ll take this as our use case.

There is a plugin that allows to use Auth0 on mobile, but it doesn’t support Web. I’d tell you why it’s not that easy to develop a Web part for this, but I’m currently working on a dedicated article which explores the non-triviality of implementing auth0 on Flutter Web and how I’ve found a way to do it. I’ll update this article with the link when it’ll be ready, but for now, since those reasons are not in the scope of this article, let’s talk generally.

So the plugin provides an Auth0 class which allows us to perform the tasks.

We decide to wrap those tasks in another class in order to leverage the factory (and to encapsulate the plugin for architectural reasons, of course).

Our abstract class will be:

I didn’t use a factory but a bunch of if just to avoid creating a real factory that we’d throw away afterwards (trust me on it).

Our concrete classes will be then:

Auth0ManagerForWeb class

This is the manager for the web side, stripped of the “functional code” since we just need to know that its implementations use dart:js .

Auth0Manager class

And this is the manager for the mobile side, which uses the plugin.

If we try to compile this on mobile, the compiler will complain that it won’t be able to find dart:js for the target platform!

But how’s that possible! We used the if (kIsWeb) to build the correct manager!

Well there are some reasons for this not to work:

  • kIsWeb is not a compile time constant, this means that it will be evaluated at runtime and the code that uses dart:js will not be tree-shaked
  • mobile target platform does not have the js library, so our code will fail on the import, which won’t be found.

And this is where conditional imports come to the rescue! Dart lets us decide wether to import one thing or another by doing something like this:

import "something.dart" if (dart.library.io) "other.dart"

This means that we can choose what to import at compile time according to the target platform. This comes handy in cases we need to condition imports for mobile or web.

You can find more on conditional imports on this Daniel Varga’s article.

Let’s get back on our track: we’re going to use conditional imports to import the correct auth0 manager for our platform.

First of all, we’re going to create an import stub:

The file will only have a getter which returns an instance of AuthManager which is the abstract class our two managers inherit from.

Then we’re going to add the following instructions in our managers’ files:

And, lastly, our abstract class will become like this:

By doing this, we have defined a getManager top level function which has different implementations for different files.

The compiler will then say something like this:

Let’s import aut0_manager_stub.dart , but if the dart.library.io is available, import auth0_manager.dart . But hey, if dart.library.js is available, import auth0_manager_for_web.dart !

This will result in importing the correct file for our target platform without having to rely or the non constant kIsWeb and, instead of a factory (remember when I said that we’d have thrown the factory away?) we’re going to call that getManager function which will do the work depending on its implementation.

Note that since we know that at least one between dart.library.js and dart.library.io exists, we can be safe that we don’t fall into importing the stub.

It was quite a lot here, if you’re still with me here at the end of the article well…

That’s all and thank you!

--

--

Antonello Galipò
Flutter Community

Flutter and Android developer with an enormous love for Yoga and hipster habits.