How We Made Google's Archived flutter/gallery Build Again
Marcus ChenLead Engineer at Code2Nativeflutter/galleryis Google's own Flutter showcase app. It has demos for every Material widget, multiple full sample apps inside it, and localization for dozens of languages. Google archived the repo. Its last meaningful commits are from 2023, and nobody is going to update it.
That makes it a perfect torture test for our build service. It is exactly the shape of project people upload to us: a real, substantial app that was healthy when its team stopped touching it, and that its owner now just wants as a signed APK. So I cloned it, uploaded the zip unchanged, and hit build.
It failed. This post is the honest chronology of why, including the diagnosis I got wrong on the first pass.
The first failure, and the wrong diagnosis
Our default toolchain is current stable Flutter, 3.44. On 3.44 the gallery build died, and the loudest thing in the log was this: a large boxed deprecation warning saying the project applies Flutter's app_plugin_loader Gradle plugin imperatively, that this is deprecated, and that we should migrate to the declarative plugins {} block.
So my first diagnosis was a Gradle integration problem. The warning points at android/settings.gradle, the build failed, the two things sit next to each other in the log, case closed. I spent time on the wrong layer because the deprecation warning hijacked the real error. It is a warning. Warnings do not fail builds. The actual failure was further up the log, quieter, and came from the Dart compiler, not from Gradle:
../win32-5.0.9/lib/src/guid.dart: Error: 'UnmodifiableUint8ListView' isn't a type. ../win32-5.0.9/lib/src/guid.dart: Error: Method not found: 'UnmodifiableUint8ListView'. Target kernel_snapshot failed: Exception FAILURE: Build failed with an exception.
The project does not import win32anywhere. It is a transitive dependency, pulled in through the gallery's desktop support. And it is pinned.
The real wall: a 2023 lockfile against a 2024 Dart
The gallery ships a pubspec.lock from 2023, and that lockfile pins win32 5.0.9. Version 5.0.9 of win32 uses UnmodifiableUint8ListView from dart:typed_data. Dart 3.5 deleted that class. Flutter 3.24 ships Dart 3.5. Which means even stepping one era back to Flutter 3.24 could not compile this project. The code was fine when it was written; the platform underneath it moved.
Here is the part that makes this hard to detect: the pubspec gives you nothing. The gallery's SDK constraint is sdk: ^3.1.0, which every Dart release since 2023 satisfies, including the current one. Its intl dependency is intl: any. The pubspec is era-neutral. The era lives in the lockfile and in the Android template files, and if you resolve against the wrong era you do not get a polite version conflict. You get a compile error inside a package the user has never heard of.
Before we built era detection, projects like this hit an even worse wall earlier in the pipeline: version solving failed, page after page of it, because a modern flutter_localizations demands an intl that a 2023 dependency tree cannot accept. Every one of those failures was auto-refunded, which is the correct policy and also a signal we were leaving builds on the table.
Why we did not patch the project
The obvious fixes are all edits. Bump win32 past 5.1.1. Delete the lockfile and re-resolve. Migrate settings.gradle to the declarative plugin block. Any competent Flutter developer would do some combination of these locally, and for their own repo that is the right call.
It is the wrong call for a build service. The lockfile is the project's tested state: those exact versions are what the app last shipped and worked with. Deleting it re-resolves the entire dependency tree years forward and can change behavior in ways nobody has tested. Patching individual pins means we are silently editing code the user gave us, and every project would need different edits. The promise we want to make is narrower and stronger: upload the repo unchanged, get back the binary it would have produced in its own era.
So the fix is not changing the project to fit the toolchain. It is changing the toolchain to fit the project.
Selecting the era: how the version matrix decides
We keep three Flutter eras warm in the build fleet: 3.19, 3.24, and 3.44. Before a build starts, a preflight pass reads the project and picks one. The signals, in the order we check them:
- The pubspec SDK constraint. A real upper bound like
>=2.17.0 <3.0.0dates a project precisely. The gallery's^3.1.0matched everything, so this signal stayed silent. - The intl pin.flutter_localizations pins intl exactly, per Flutter release, so a project's intl version is a fingerprint: 0.19.x means the 3.24 era, 0.18.x means the 3.19 era. The gallery says
intl: any. Silent again. - The Android template generation. If
android/settings.gradleapplies the Flutter Gradle plugin imperatively, the android folder was generated before the Flutter 3.16 template switched to the declarative form. That deprecation warning that misled me on day one turned out to be the single most useful dating signal in this repo. This one fired. - Tie-break to the oldest era. When signals conflict or everything is ambiguous, an old toolchain building an old project fails far less often than a new toolchain building an old one.
For the gallery, signal three decided it, and the build log says exactly why:
[c2n] Toolchain: Flutter 3.19.6 — android/ applies the Flutter Gradle plugin imperatively (pre-3.16 template)
Flutter 3.19.6 ships Dart 3.3, where UnmodifiableUint8ListView still exists. win32 5.0.9 compiles. The 2023 lockfile is respected byte for byte. Nothing in the project was edited.
The result
flutter/gallery, cloned as-is from the archived repo, built in 7m 23s and produced a 117.6MB signed release APK on Flutter 3.19.6. It installs and runs. The repo is still archived, still untouched, and Google still is not going to fix it. It does not need fixing. It needed the 2023 toolchain it was written against, and the only hard part was identifying that from the files alone.
The general lesson I took from the wrong first diagnosis: in a failed build log, the loudest message and the fatal message are usually different messages. Deprecation banners are big, boxed, and designed to be noticed. Compiler errors from a transitive package are one line in a wall of text. Read for the exit code's cause, not for the formatting.
What this means if you have an old Flutter repo
If you have a Flutter project from 2022 or 2023 that no longer builds on your machine, the odds are good that nothing is wrong with your code. Your local Flutter upgraded past it, and the failure modes look exactly like the gallery's: version solving walls, or a type error inside a dependency you never chose.
You do not need to upgrade the project to get a binary out of it. Upload it unchanged, lockfile and all. The preflight reads the same signals described above, picks 3.19, 3.24, or 3.44, and states its reasoning in the build log the way it did for the gallery. If the build still fails, the credit is refunded automatically. That was true for every failure along the way here too.
Test it on your own dead repo
The first build is free. Zip the project exactly as it sits, including pubspec.lock, and upload it. Check the log for the [c2n] Toolchain line to see which era was selected and why.

Marcus Chen
Lead Engineer at Code2Native
Marcus has 8+ years of experience in mobile development, specializing in cross-platform solutions and WebView optimization. He has helped 200+ businesses convert their web apps to native iOS and Android apps.