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

532

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.

Share:
532
tmaihoff
Author by

tmaihoff

Updated on January 01, 2023

Comments

  • tmaihoff
    tmaihoff over 1 year

    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>.

  • tmaihoff
    tmaihoff over 2 years
    Thank you very much for that detailed answer. That definetly helps a lot! Smart approach, providing it via a shared dependencies package. I will reward you the bounty as soon as possible
  • tmaihoff
    tmaihoff over 2 years
    one question to 3.: Just putting a comment above one or more export statements groups them together by the comment name? Did I understand that correctly? You say you could import all bloc dependencies by importing shared_dependencies/bloc.dart but you don't export bloc.dart in that example. You only put bloc.dart as a comment above the bloc-related exports. That seems odd and I feel like I haven't understood 3. it correctly
  • Kirill Bubochkin
    Kirill Bubochkin over 2 years
    @tmaihoff I meant it to be in different files, probably the formatting was confusing. I've updated this point, hopefully, it's more clear now.
  • tmaihoff
    tmaihoff over 2 years
    ah I see, yeah that makes much more sense. Got it now, thanks!
  • vander2675
    vander2675 almost 2 years
    @KirillBubochkin How to get method 4: running with build_runner? I have a local package for my dev_dependencies and can import it in the pubspec.yaml file of the parent like: dev_dependencies: my_dev_dependency_package: path: '../path/to/package' But this gives me the Cannot run executables in transitive dependencies. Error. I'm not really getting how to apply Method 4. there.
  • Kirill Bubochkin
    Kirill Bubochkin almost 2 years
    @vander2675 did you add the dependency to the main packages as well?
  • vander2675
    vander2675 almost 2 years
    @KirillBubochkin no only to the dev_dependencies
  • Kirill Bubochkin
    Kirill Bubochkin almost 2 years
    @vander2675 yeah, with dev_dependencies it won't work, since they apply only to the package that declared them, they are not added to other packages even as transitive dependencies.