Skip to content

feat(package_info_plus): add opt-in compile-time package info accessor#3874

Closed
Ortes wants to merge 2 commits into
fluttercommunity:mainfrom
Fullphysio:package-info-compile-time-version
Closed

feat(package_info_plus): add opt-in compile-time package info accessor#3874
Ortes wants to merge 2 commits into
fluttercommunity:mainfrom
Fullphysio:package-info-compile-time-version

Conversation

@Ortes

@Ortes Ortes commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

On the web, PackageInfo.fromPlatform() resolves the version by fetching version.json from the server at runtime (with a cache buster). That file reflects whatever is currently deployed — not the bundle actually executing in the browser. The two states routinely diverge:

This makes the reported version actively misleading rather than merely missing — and the version is the whole point of the plugin. Its two canonical uses, showing the running version and checking whether an update is needed, are both defeated when the value is the server's. An update gate built on it can never fire for the very users it targets, since stale clients self-report as up to date.

This PR

Adds a small opt-in library, package_info_plus_environment.dart, exposing PackageInfoEnvironment.packageInfo:

import 'package:package_info_plus/package_info_plus_environment.dart';

final info = await PackageInfoEnvironment.packageInfo;
  • Web — returns a PackageInfo built from compile-time PACKAGE_INFO_PLUS_* defines (flutter build web --dart-define=PACKAGE_INFO_PLUS_VERSION=1.2.3 ...). These are embedded in the running bundle and cannot diverge from it. A web build that omits PACKAGE_INFO_PLUS_VERSION fails to compile (a kIsWeb-conditional const assert), so a misleading version can never ship silently.
  • All other platforms — delegates to PackageInfo.fromPlatform(), which reads the installed binary and is already reliable. The defines are not required there.

Design choices that keep this safe and non-breaking:

  • PackageInfo.fromPlatform() is untouched. No behavior change for existing users.
  • Opt-in via a separate library that the package barrel does not export. The compile-time guard is evaluated per compiled library, so it only affects code that explicitly imports package_info_plus_environment.dart. Apps that don't opt in are completely unaffected — no new build requirement is imposed package-wide.
  • Cross-platform by construction (single accessor, platform-appropriate source), per the contributing guide's rule against single-platform features.
  • The framework-side requests to expose the build version were declined ("Static" versioning in flutter web flutter/flutter#149725, [WEB] Web Cache invalidation based on pubspec.yaml version flutter/flutter#149031), so a plugin-level opt-in seems to be the only avenue available to users today. If Flutter later injects build name/number defines, this accessor can read them with no API change.

flutter analyze is clean and the existing + new tests pass. The web compile-time enforcement is verified by a compilation test (a web build without the define does not compile) rather than a runtime test.

Related Issues

Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I titled the PR using Conventional Commits.
  • I did not modify the CHANGELOG.md nor the plugin version in pubspec.yaml files.
  • All existing and new tests are passing.
  • The analyzer (flutter analyze) does not report any problems on my PR.

Breaking Change

  • Yes, this is a breaking change.
  • No, this is not a breaking change.

Adds an opt-in library, package_info_plus_environment.dart, exposing
PackageInfoEnvironment.packageInfo. It returns the *running* app's
PackageInfo: on web from compile-time PACKAGE_INFO_PLUS_* dart-defines,
on every other platform by delegating to PackageInfo.fromPlatform().

On web there is no reliable runtime source for the running version:
version.json is fetched from the server, so it reflects the deployed
version rather than the executing (possibly cached) bundle, and the fetch
can fail entirely (offline, CORS, hosting rewrites). The compile-time
constants are embedded in the bundle and cannot diverge from it. A web
build that omits PACKAGE_INFO_PLUS_VERSION fails to compile, so a
misleading version can never ship silently. Native builds are unaffected.

PackageInfo.fromPlatform() is unchanged and the new library is not
exported by the package barrel, so this is purely additive and opt-in.

Related: fluttercommunity#2675, fluttercommunity#225, fluttercommunity#456, flutter/flutter#149031.
@Ortes Ortes force-pushed the package-info-compile-time-version branch from e31c344 to 13e02bd Compare June 12, 2026 11:22
@Ortes Ortes changed the title feat(package_info_plus): support compile-time package info via dart-defines feat(package_info_plus): add opt-in compile-time package info accessor Jun 12, 2026
@Ortes

Ortes commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Re: CI status — the red checks here are pre-existing infrastructure failures, not regressions from this PR. Evidence:

The jobs that actually exercise this change all pass:

  • Dart Analyzer ✓ · web_example_build ✓ · android_integration_test (24) ✓ · linux/windows build + integration ✓

The failing jobs fail identically on main and on every recent unrelated PR:

  • all_plugins analyze/pub-check fails on a sensors_plus invalid_annotation_target warning (unrelated package) — red on every PR including ones targeting main.
  • macos_example_build / ios_example_build: Plugin package_info_plus has a Package.swift for macos but is missing a dependency on FlutterFramework + CocoaPods xcfilelist errors — Xcode/SPM toolchain drift on the runners.
  • android_integration_test (shards 29/32/36): Timeout waiting for emulator to boot — emulator flake; shard 24 booted and passed.
  • web_integration_test: AppConnectionException from chromedriver, then a 30-min hang until cancel — driver flake, not a compile error (the web example compiles and launches in the log).

The last ~6 package_info_plus PRs (kotlin-dsl migration, ubuntu-24.04 migration, win32 relax, built-in-kotlin) are all red for the same reasons.

This change is purely additive: a new, non-exported package_info_plus_environment.dart library. It doesn't touch any existing code path, and the barrel (package_info_plus.dart) is unchanged — so existing consumers and the example's web build are unaffected. Happy to rebase once the CI infra is sorted.

…allbacks

PackageInfoEnvironment now resolves the web version from, in order:
PACKAGE_INFO_PLUS_VERSION (explicit), FLUTTER_BUILD_NAME
(flutter/flutter#187935, injected by flutter_tools like
FLUTTER_APP_FLAVOR), and dart.package.version (dart-lang/sdk#38855).
Once either upstream proposal lands, apps need no configuration at all.
@Ortes

Ortes commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Forward-compatibility note. This PR now also recognizes tool-provided version defines as fallbacks, so apps will need no configuration at all once their build front-end injects the version itself:

Resolution order is: explicit PACKAGE_INFO_PLUS_VERSIONFLUTTER_BUILD_NAMEdart.package.version. The two upstream proposals and this PR are deliberately complementary: at least one of them needs to exist for PackageInfo to stop silently reporting the wrong version on web, and whichever lands first makes this plugin correct-by-default there.

bkonyi pushed a commit to theprantadutta/flutter that referenced this pull request Jul 8, 2026
…tter#187935)

The tool already injects `FLUTTER_VERSION`, `FLUTTER_CHANNEL`,
framework/engine revisions (exposed as `FlutterVersion`) and
`FLUTTER_APP_FLAVOR` (exposed as `appFlavor`) as dart-defines. The
application's own `version:` from `pubspec.yaml` is the one missing
field, even though the tool already parses it
(`FlutterManifest.buildName`/`buildNumber`) and embeds it into
`version.json`, `Info.plist` and `build.gradle` on every build.

Without it, the *running* version is unknowable on the web:
`version.json` is fetched from the server at runtime, so a stale cached
client reports the freshly deployed version instead of its own, and
version gates / update prompts built on `package_info_plus` silently
fail for exactly the users they exist for
(fluttercommunity/plus_plugins#2675).

This PR:

- injects `FLUTTER_BUILD_NAME` and `FLUTTER_BUILD_NUMBER` (the app's
pubspec `version`, overridable via `--build-name`/`--build-number`) in
`FlutterCommand.getBuildInfo`, following the `FLUTTER_APP_FLAVOR`
pattern including its reserved-name guards;
- exposes them as `appBuildName` / `appBuildNumber` constants in
`package:flutter/services.dart` (new `src/services/app_version.dart`,
same style as `flavor.dart` / `flutter_version.dart`);
- adds tool tests (injection from pubspec, no injection without a
version, environment/dart-define collision guards) and framework tests.

Like all `String.fromEnvironment` constants, these are tree-shaken when
unreferenced, so nothing ships in the binary unless the app reads them.

`package_info_plus` is ready to consume these defines
(fluttercommunity/plus_plugins#3874), which would make the fix
transparent for the ecosystem. A companion proposal for the pure-Dart
side (`dart compile`, servers/CLIs) is dart-lang/sdk#38855 — neither
subsumes the other, since Flutter does not build through `dartdev`.

Fixes flutter#187934

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
@Ortes

Ortes commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Resolved by flutter/flutter#187934

@Ortes Ortes closed this Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: [package_info_plus] Web: Version info returns next version in server

1 participant