I am wondering how Dart package versions are resolved in my Flutter application.
Say, I have a dependency foo
and declare a dependency like this:
dependencies:
foo: ^1.2.3
How does Dart Pub know what version to resolve / what happens when a new version is available?
Furthermore, if I want to publish my Flutter plugin to pub.dev, how do I decide when to increase what part of the version?
All of this goes back to Dart package versioning, i.e. there is no distinction for Flutter plugins.
It is based on SemVer 2.0.0-rc.1
(Semantic Versioning). For Dart packages, the gist of the covention is as follows:
<1.0.0 |
>=1.0.0 |
---|---|
0.major.minor+patch |
major.minor.patch |
Note that Pub also supports pre-release versions denoted by:
<1.0.0 |
>=1.0.0 |
---|---|
0.major.0-prerelease.patch |
major.0.0-prerelease.patch |
Example versions might be 0.6.0-nullsafety.0
(with a patch version of 0.6.0-nullsafety.1
) or 2.0.0-dev.1
.
Learn more about Dart's package versioning.
To get a basic understanding of what problem the version resolving tries to solve, you can read through the Resolving shared dependencies section of Dart's package versioning article. The aspect of version resolving that I will look at here is the caret syntax.
Caret ^
syntax is the standard syntax for resolving versions in Dart and closely connected to the versioning tables above. Whenever you define a dependency using foo: ^1.0.0
, there is a bit of logic involved to determine which versions can be resolved from that string.
In general, ^
matches up to major versions. This means that whenever you have a breaking change in your API, you will want to bump the major version because then dependants will not automatically be upgraded to your next package version. I will try to illustrate the matching in a table again:
^0.4.2+1 |
^1.3.0 |
---|---|
>=0.4.2+1 <0.5.0 |
>=1.3.0 <2.0.0 |
As you can see, caret syntax will match any version that is greater than or equal to the current version but smaller than the next major version.
Note that pre-release versions are handled differently than normal (not exactly according to the SemVer spec) in Dart. You can find the source code for Pub's pub_semver
tool on GitHub. It declares that pre-release versions are not matched by the caret syntax (and neither by the traditional syntax):
^1.0.0 |
matches version? |
---|---|
1.4.2 |
yes |
1.5.0-beta |
yes |
2.0.0-alpha |
no |
2.0.0 |
no |
Learn more about Dart's package dependencies.
pubspec.lock
I quickly want to mention the role of the Pubspec lock file for dependency resolving in Dart.
There is a straight-forward way that I think defines how package versions are fetched:
pub get
without an exiting pubspec.lock
(initial fetch) will fetch the latest possible versions (according to above rules and satisfying all direct and transitive constraints of shared dependencies).pub get
with an existing pubspec.lock
file will prefer the locked versions in the lock file over latest versions (if they still satisfy constraints by potentially newly introduced dependencies since the last fetch).pub upgrade
throws away the existing pubspec.lock
file and then acts the same way as the initial pub get
.Consequently, running pub get
in your app will not unexpectedly fetch new versions. And when working with colleagues, everyone will fetch the same versions according to the pubspec.lock
file when running pub get
and having included the lock file in version control.
Learn more about lockfiles in Dart.
Because the Dart team recommends some best practices for versioning, I want to make sure to include them in here directly:
Use caret syntax
Specifying dependencies with version ranges is such as^1.6.3
is a good practice because it allows the pub tool to select newer versions of the package when they become available. Also, it places an upper limit on the allowed version, based on an assumption that packages use semantic versions, where any version of path versioned1.x
is compatible, but where a new version2.x
would be a major upgrade that isn’t semantically compatible with1.x
versions.Depend on the latest stable package versions
Usepub upgrade
to update to the latest package versions that your pubspec allows. To identify dependencies in your app or package that aren’t on the latest stable versions, usepub outdated
.Test whenever you update package dependencies
If you runpub upgrade
without updating your pubspec, the API should stay the same and your code should run as before — but test to make sure. If you modify the pubspec and update to a new major version, then you might encounter breaking changes, so you need to test even more thoroughly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With