Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Import js.dart and html.dart for a mixed web / mobile flutter project

I'm using Flutter 2.5.2. I have a project which is to be used for both web and mobile (Android / iOS).

There is one particular widget where I need to use one version of the widget when deploying for web, which uses the JS and HTML packages. When deploying for mobile, I need to use a different version which just uses standard Flutter widgets. (The reasons for this are complex - I'm embedding Unity inside flutter).

So for example, I have this web_player.dart version for web:

import 'dart:html' as html;
import 'package:js/js.dart';
import 'package:flutter/material.dart';

@JS('loadPlayer')
external String loadPlayer();

class WebVersion extends StatelessWidget {
  const WebVersion({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Use the HTML package and return an HtmlElementView
  }
}

And this mobile_player.dart version for mobile:

class MobileVersion extends StatelessWidget {
  const MobileVersion({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text("Mobile version")
  }
}

Building for web is fine. The problem is that when I build for mobile, the build breaks with:

Error: Not found: 'dart:js'

and

Error: Not found: 'dart:html'

I'm aware that these packages do not exist in the mobile platform, and I'm trying to find a workaround. I initially tried to conditionally use the WebPlayer or MobilePlayer widgets depending on the kIsWeb flag, but this didn't make any difference (I guess because kIsWeb is not a complile-time constant?)

So then I attempted to work around this using conditional imports. So, I created a stub player_stub.dart:


import 'package:flutter/material.dart';


Widget getPlayer() => throw UnsupportedError("This is a stub");

I import this stub in a wrapper Player widget:

import 'package:mypackage/player_stub.dart'
  if (dart.library.io) 'package:mypackage/mobile_player.dart'
  if (dart.library.js) 'package:mypackage/web_player.dart';

class Player extends StatelessWidget {

  const Player ();

  @override
  Widget build(BuildContext context) {
    return getPlayer();
  }
}

And then added stub implementations in my mobile_player.dart:

Widget getPlayer() => MobilePlayer();

and in my web_player.dart:

Widget getPlayer() => WebPlayer();

I was hoping this would resolve the build error by compile time tree shaking or something, but it doesn't.

How do I solve this? I'm totally stumped.

like image 608
James Allen Avatar asked Oct 23 '25 19:10

James Allen


1 Answers

Just to answer my own question for others looking for an answer: the solution was moving the stub, the mobile widget and the web widget to a separate dart library.

The library uses conditional exports like this in it's root my_player_package.dart file:

export 'src/player_stub.dart'
  if (dart.library.js) 'src/web_player.dart'
  if (dart.library.io) 'src/mobile_player.dart';

So now I reference my separate library in my pubspec.yaml:

dependencies:

  ...

  my_player_package:
    path: ../my_player_package

And I can now import 'package:my_player_package/my_player_package.dart' and build for both web and mobile.

like image 118
James Allen Avatar answered Oct 26 '25 08:10

James Allen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!