Post

Flutter's Cross Platform Dependency Hell - Part I

Issue with the way flutter handles federated plugins

1
2
3
4
5
6
7
8
9
10
11
Because no versions of flutter_secure_storage match >9.0.0 <9.1.0 and flutter_secure_storage >=9.1.0 <9.1.1 depends on flutter_secure_storage_web ^1.2.0, flutter_secure_storage >9.0.0 <9.1.1 requires flutter_secure_storage_web ^1.2.0.
And because flutter_secure_storage >=9.1.1 depends on flutter_secure_storage_web ^1.2.1, flutter_secure_storage >9.0.0 requires flutter_secure_storage_web ^1.2.0.
And because facebook_auth_desktop >=1.0.1 depends on flutter_secure_storage ^9.0.0 and flutter_secure_storage 9.0.0 depends on flutter_secure_storage_web ^1.1.1, facebook_auth_desktop >=1.0.1 requires flutter_secure_storage_web ^1.1.1.
Because no versions of flutter_facebook_auth match >6.2.0 <7.0.0 and flutter_facebook_auth 6.2.0 depends on facebook_auth_desktop ^1.0.3, flutter_facebook_auth ^6.2.0 requires facebook_auth_desktop ^1.0.3.
Thus, flutter_facebook_auth ^6.2.0 requires flutter_secure_storage_web ^1.1.1.
And because every version of flutter_secure_storage_web depends on js ^0.6.3 and mixpanel_flutter >=2.3.0 depends on js ^0.7.1, flutter_facebook_auth ^6.2.0 is incompatible with mixpanel_flutter >=2.3.0.
So, because my_app depends on both mixpanel_flutter ^2.3.1 and flutter_facebook_auth ^6.2.0, version solving failed.


You can try the following suggestion to make the pubspec resolve:
* Consider downgrading your constraint on mixpanel_flutter: flutter pub add mixpanel_flutter:^2.2.0

It’s difficult to resist the urge to bang your head on the desk when you encounter an error like this. This is how Flutter reports a dependency conflict. A dependency conflict, while frustrating, is nothing uncommon. Even more so when using a cross-platform framework, where you have to depend on third-party implementations of some popular libraries. But this one is incredibly ridiculous. But, before diving into it, Lets take a brief look at how packages work in Flutter.

There are two ways to develop a flutter package that relies on native APIs:

1) The simple approach:

Put the platform-specific implementations in the package under the platform’s folder. See mixpanel-flutter.

1
2
3
4
5
6
7
8
9
10
   mixpanel_flutter 2.3.1
    ├── flutter_web_plugins 0.0.0
    │   ├── characters...
    │   ├── collection...
    │   ├── flutter...
    │   ├── material_color_utilities...
    │   ├── meta...
    │   └── vector_math...
    ├── flutter...
    └── js...

2) The recommended way:

Separate out the platform-specific implementations into different packages. This is called a federated plugin in Flutter. It consists of the following:

  • The app-facing package, which the plugin’s user adds to their pubspec.
  • The platform-specific package(s) which has the native code, the app-facing package depeneds on these packages.
  • And the platform interface package, which declares a common interface that any platform package must implement to support the app-facing package.

For example, See the dependency tree of google sign in package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
├── google_sign_in 6.2.1
│   ├── google_sign_in_android 6.1.23
│   │   ├── flutter...
│   │   └── google_sign_in_platform_interface...
│   ├── google_sign_in_ios 5.7.6
│   │   ├── flutter...
│   │   └── google_sign_in_platform_interface...
│   ├── google_sign_in_platform_interface 2.4.5
│   │   ├── flutter...
│   │   └── plugin_platform_interface...
│   ├── google_sign_in_web 0.12.4
│   │   ├── google_identity_services_web 0.3.1+1
│   │   │   ├── meta...
│   │   │   └── web...
│   │   ├── web 0.5.1
│   │   ├── flutter...
│   │   ├── flutter_web_plugins...
│   │   ├── google_sign_in_platform_interface...
│   │   └── http...
│   └── flutter...

Now, let’s go back to the original issue. my_app is a flutter app targeting only Android & iOS. It depends on two packages flutter_facebook_auth & mixpanel_flutter . The full dependency tree looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
my_app 1.0.0+1
├── flutter_facebook_auth 6.2.0
│   ├── facebook_auth_desktop 1.0.3
│   │   ├── flutter_secure_storage 9.1.1
│   │   │   ├── flutter_secure_storage_linux 1.2.1
│   │   │   │   ├── flutter...
│   │   │   │   └── flutter_secure_storage_platform_interface...
│   │   │   ├── flutter_secure_storage_macos 3.1.0
│   │   │   │   ├── flutter...
│   │   │   │   └── flutter_secure_storage_platform_interface...
│   │   │   ├── flutter_secure_storage_platform_interface 1.1.1
│   │   │   │   ├── flutter...
│   │   │   │   └── plugin_platform_interface...
│   │   │   ├── flutter_secure_storage_web 1.2.1
│   │   │   │   ├── flutter...
│   │   │   │   ├── flutter_secure_storage_platform_interface...
│   │   │   │   ├── flutter_web_plugins...
│   │   │   │   └── js...
│   │   │   ├── flutter_secure_storage_windows 3.1.2
│   │   │   │   ├── ffi 2.1.2
│   │   │   │   ├── path_provider 2.1.3
│   │   │   │   │   ├── path_provider_android 2.2.4
│   │   │   │   │   └── ....
│   │   │   │   └── path...
│   │   │   ├── flutter...
│   │   │   └── meta...
│   │   ├── http 1.2.1
│   │   │   ├── http_parser 4.0.2
│   │   │   └── meta...
│   │   ├── flutter...
│   │   └── flutter_facebook_auth_platform_interface...
│   ├── flutter_facebook_auth_platform_interface 5.0.0
│   │   ├── plugin_platform_interface 2.1.8
│   │   │   └── meta...
│   │   └── flutter...
│   ├── flutter_facebook_auth_web 5.0.1
│   │   ├── flutter...
│   │   ├── flutter_facebook_auth_platform_interface...
│   │   ├── flutter_web_plugins...
│   │   └── js...
│   └── flutter...
└── mixpanel_flutter 2.3.1
    ├── flutter_web_plugins 0.0.0
    │   ├── characters...
    │   ├── collection...
    │   ├── flutter...
    │   ├── material_color_utilities...
    │   ├── meta...
    │   └── vector_math...
    ├── flutter...
    └── js...

Notice how adding a single package leads to a cascade of redundant dependencies meant for different platforms, which are not even the targets of my_app!

Observe that mixpanel_flutter depends on js ^0.7.0. While my_app also has a transitive dependency on facebook_auth_desktop which in turn has a transitive dependency on flutter_secure_storage_web! And since flutter_secure_storage_web uses js ^0.6.3, it’s incompatible with mixpanel_flutter. Thus causing the dependency conflict.

By now, you must have realized why this situation is so ridiculous. Why does my_app need to depend on facebook_auth_desktop and flutter_secure_storage_web at all, when it doesn’t even target desktop or web platforms? Because of this behavior, I am left to solve a dependency conflict arising out of redundant dependencies that will never be used in the project.

Thankfully, I found a hack to fix the issue in the package’s GitHub repo. Adding the latest version of the js package in the pubspec under dependency_overrides fixes the issue. Nevertheless, the problem should have never arisen; Flutter could have intelligently pruned the dependency tree by ignoring platform-specific dependencies not meant for my target platforms.

This post is licensed under CC BY 4.0 by the author.