tmaihoff
tmaihoff

Reputation: 3560

Share pubspec.yaml package dependency versions along multiple local flutter and dart packages

My flutter project depends on several local flutter and dart packages to keep things separated and clean. My folder structure is like this:

main-flutter-project
│  lib
|  test
│  pubspec.yaml
│
└── local-packages
│   └── dart-package-1
│   │     pubspec.yaml
│   │
│   └── flutter-package-1
│   │     pubspec.yaml
│   │
│   └── flutter-package-2
│         pubspec.yaml
...

Each local package is self contained and can be maintained without touching the main project.

This structure means that I have many pubspec.yaml files where I have to keep the dependencies updated. When I use e.g. the bloc libaray bloc: ^7.2.1 in say 5 packages, I have to update the version in each pubspec file separately when a new version is released.

Is there a possibility to specify those shared package dependency versions in only one place where the other pubspec.yaml files refer to?

I've seen this e.g. with Maven where you can specify a property <junit.version>4.12</junit.version> and access it from somewhere else <version>${junit.version}</version>.

Upvotes: 10

Views: 2814

Answers (1)

Kirill Bubochkin
Kirill Bubochkin

Reputation: 6343

We were solving a similar problem.

AFAIK, there's no built-in or recommended way to do this, so we were inventing some hacks.

In our case, we have core package that has some shared functionality and common dependencies, if you don't have it, you can still create an artificial one, let's say, shared_dependencies package, and specify all the shared dependencies there.

Now, let's say, package foo depends on shared_dependencies package, and there's dependency bar defined in shared_dependecies package that foo needs to use. There are some ways to do that:

  1. Import dependency directly. Since foo depends transitively on bar, you can just write import package:bar/bar.dart and it will work. It's not the best way though:

    • it's bad practice to import transitive dependency (there's even a linter rule for that);
    • auto-import won't work;
  2. Export package in shared_dependencies package. I.e. shared_dependencies.dart can contain the following lines:

    export 'package:bar/bar.dart'
    

    That means that in your foo package you can just write import 'shared_dependencies/shared_dependencies.dart' and get access to bar content.

    Pros:

    • auto-imports work.

    Contras:

    • if you export several packages, there can be name conflicts (you'll have to hide some names in export);
    • if foo package depends on one bar package only, it could be weird to import all shared_dependencies.
  3. Export in separate libraries of shared_dependencies package. You can group some related packages together in different files, e.g.:

    bar.dart:

    export 'package:bar/bar.dart'
    

    bloc.dart:

    export 'package:bloc_concurrency/bloc_concurrency.dart';
    export 'package:flutter_bloc/flutter_bloc.dart';
    

    In that case, if you need bar package in foo, you write import 'package:shared_dependencies/bar.dart'; if you need bloc, you write import 'package:shared_dependencies/bloc.dart'. Auto-imports work as well.

  4. Add direct dependency to foo package, but don't specify version constraints:

    bar:
    

    This basically means that you need any bar package, but since foo also depends on shared_dependencies, its constraints will be taken into account. This may be needed if you're using some executables from bar package, as there's a limitation in Dart SDK that doesn't allow to run executables in transitive dependencies.

In our project, we ended up using 2 for the most commonly used packages, 3 for other packages, and 4 for packages with executables that we need to run.

Upvotes: 23

Related Questions