flutter_buildkit library

Build Flutter apps (APK, AAB, IPA) from an interactive console menu or from code, file every build in a predictable folder tree and keep a ledger of what was built, published, uploaded to Google Play and symbolicated.

Most people use the command line app:

dart pub global activate flutter_buildkit
fbk            # inside a Flutter project

The menu (App) and its settings screens are not part of the library API. The commands that run without a menu are in Cli.

The same pieces are available as a library:

Runnable examples for each area are in the package's example/ folder.

Classes

AppConfig
Settings for one Flutter project, read from flutter_buildkit.yaml in the project root, with secrets taken from environment variables.
BuildArtifact
One file produced by a build (an APK per ABI, an AAB, an IPA...).
BuildEvent
Something that happened to a build, kept in BuildRecord.events.
BuildFailure
Why a build did not finish; set on rows with status BuildStatus.failed.
BuildManager
Ledger operations that also touch the build folders on disk.
BuildNaming
Everything a name template can use.
BuildPaths
Decides where a build and its files are stored.
BuildRecord
A single row of the build ledger.
BuildRequest
Everything the user chose for one build.
BuildSigning
Who signed an Android build, read back from the finished file.
Cli
The commands that work without a menu: they take their input from options and arguments, never prompt, and return an ExitCodes value.
ConfigEditor
Edits a flutter_buildkit.yaml text in place, keeping the comments and the order of everything it does not touch.
ConfigTrust
Remembers which project configs the user agreed to run.
Console
Prompts and styled output for the menu.
CrashlyticsConfig
Firebase Crashlytics symbol upload settings, from the crashlytics: section.
DartEntry
A Dart file that can be run as an app: it defines main().
DeleteResult
What BuildManager.delete did, per build.
Doctor
Checks that the tools and settings the other commands rely on are there.
DoctorCheck
One line of the doctor report.
EntryPoint
One Dart file to build. name is null for the default entry point, which keeps the plain names.
ExitCodes
Exit codes of the command line app. They follow sysexits.h where one fits and are stable, so scripts and CI can rely on them.
FlavorConfig
Per-flavor settings. Every field is optional.
FlutterBuilder
Runs flutter build, copies the outputs into the build folder tree and records the build in the ledger.
FlutterProject
What can be learned about a Flutter project from its files.
GradleFlavor
An Android product flavor found in Gradle.
InterruptGuard
Cleans up when the user presses Ctrl-C during a build: stops the running tool and removes folders of builds that have not finished.
JsoncScan
Character classes of a JSONC text: which characters are code (outside strings and comments) and which are inside comments.
KeyPress
One key press: a Key plus, for Key.char, the typed character.
LaunchConfig
One Dart/Flutter configuration read from .vscode/launch.json.
LaunchEntry
One run configuration to add to .vscode/launch.json.
LaunchPlan
What writing launch.json would do.
Ledger
The build ledger, stored as a single JSON document.
LedgerExporter
Turns ledger rows into CSV, TSV or JSON text.
PathTemplate
A name or folder template such as {app}-{flavor}-{mode}-{version}.
PlayConfig
Google Play upload settings, from the play: section.
PlayPublisher
Uploads an AAB to Google Play through the Android Publisher API.
PlayUpload
Where and how a build reached Google Play.
PreBuildCommand
One command of the pre-build plan.
PreBuildConfig
Which pre-build steps are ticked by default in the build menu.
PreBuildRunner
Runs the pre-build plan; stops at the first failing command.
PreviewContext
What previews use as the example build.
ProcessRunner
Runs external tools (flutter, firebase, sentry-cli). Swappable in tests.
PruneResult
What pruneBuilds removed, or would remove with dryRun.
PubspecVersion
version: 1.2.3+45 from pubspec.yaml.
SelectItem
One row of a selection list.
SelectModel
State and key handling of an arrow-key list, single or multi select. Pure and terminal-free, so it can be unit tested.
SentryConfig
Sentry symbol upload settings, from the sentry: section.
SettingChoice
One selectable value of a SettingKind.choice setting.
SettingDef
One option in the settings screen: where it lives in the YAML file, how to show and edit it, and what it will do (the preview).
SigningInspector
Reads the signing certificate of an APK or AAB with apksigner or keytool. Both are optional: without them inspect returns null.
Style
ANSI colors and glyphs. With enabled false every method returns the text unchanged, so output stays clean in pipes, logs and tests.
Suggestion
One value found by reading the project, and why it was chosen.
Symbolicator
De-obfuscates stack traces with the symbols stored for a build.
SymbolTargets
Names of crash reporting tools symbols can be uploaded to.
SymbolUploader
Uploads a build's debug symbols to Firebase Crashlytics and Sentry by shelling out to the firebase and sentry-cli tools.
TraceResult
Outcome of one symbolication run.
VerifyIssue
One thing verify found wrong with the ledger or its folders.
VerifyReport
Result of verifyLedger.

Enums

ArtifactType
The kind of artifact flutter build produces.
BuildMode
Flutter build mode.
BuildStatus
Where a build is in its life, most advanced state wins.
CheckLevel
How a DoctorCheck came out.
ExportFormat
Output format for exporting ledger rows.
Key
A key the terminal can report to a prompt.
LayoutPreset
Ready-made folder layouts for the build output. Pick one with output_layout: in the config, or write your own template.
PreBuildStep
Steps that can run once before a batch of builds.
SelectOutcome
What a key press did to a SelectModel.
SettingKind
How a setting is edited in the settings screen.
TraceKind
Kind of stack trace to de-obfuscate.
UiMode
How prompts read input.

Constants

buildInfoName → const String
Name of the file in each build folder that holds the ledger row.
buildLogName → const String
Name of the file in each build folder that holds the flutter build output.
launchModes → const List<String>
Run modes written to launch.json, in order.

Functions

abiFromTrace(String text) → String?
The Android ABI folder name for a crash's ABI: 'arm64' line, or null.
addCommandOptions(ArgParser parser) → void
Adds the options of the non-interactive commands to parser.
addToGitignore(FlutterProject project, List<String> entries) → void
Appends entries to the project's .gitignore (created if missing).
availablePreBuildSteps(FlutterProject project) → List<PreBuildStep>
Steps that make sense for project (build_runner and gen-l10n only when the project uses them).
buildMismatchWarning(BuildRecord r, String text) → String?
A warning when the trace's build id is not one of r's, or null when it matches or the trace names none.
copyDirectory(Directory from, Directory to) → Future<void>
Recursively copies the contents of from into to, creating to if needed.
dartBuildIds(Directory dir) → Map<String, String>
Build ids of the Dart symbols files in dir (app.android-arm64.symbols and so on), by file name.
decodeJsonc(String text) → Object?
Decodes JSONC, throwing FormatException when it is not valid.
defaultPreBuildSteps(AppConfig config, List<PreBuildStep> available) → List<PreBuildStep>
Which of available are ticked by default, from the config.
describeCommand(List<String> command) → String
Shell-style rendering of a command, for showing the user what runs.
detectEntryPoints(FlutterProject project, Iterable<String> flavors) → Map<String, String>
Dart files with a main() anywhere in the project that are not the default (lib/main.dart) or a flavor's own main: main_admin.dart becomes the entry point admin.
detectTraceKind(String text) → TraceKind
Guesses what kind of stack trace text is.
elfBuildId(Uint8List bytes) → String?
readElfBuildId for bytes already in memory.
endsInIncompleteSequence(List<int> bytes) → bool
True when bytes ends in the middle of an escape sequence (a lone ESC, or ESC [ without its final byte). Terminals normally deliver a whole sequence at once, but over a slow link the rest can arrive in a later read, so the caller should wait briefly for more bytes before deciding.
entryPointPreview(AppConfig c, PreviewContext x, String name, String path, {String? flavor}) → List<String>
What building the entry point name (a Dart file at path) does.
entryPointsFor(AppConfig config, FlutterProject project, String? flavor) → List<EntryPoint>
The entry points to build for flavor, in menu order.
findArray(String text, String key) → (int, int)?
Where the array under the top level key starts and ends: (open, close) are the indexes of [ and ]. Null when there is none.
fitLine(String line, int width) → String
Cuts line to at most width visible characters, leaving ANSI escape sequences intact. A line that wraps would take two terminal rows and break the cursor arithmetic used to redraw a list in place.
flavorSettings(String flavor) → List<SettingDef>
Settings of one flavor (flavors.<name>.*).
flutterBuildArgs(BuildRequest r, {String? symbolsDir}) → List<String>
Arguments after the flutter executable, e.g. build appbundle --release --flavor dev -t lib/main_dev.dart ....
formatBytes(int bytes) → String
Formats bytes as a human-readable size such as 1.5 MB.
globalSettings() → List<SettingDef>
All project wide settings, in the order shown in the menu.
jsonText(Object? value) → String
Pretty JSON for reports.
launchEntriesFor(AppConfig config, FlutterProject project) → List<LaunchEntry>
The run configurations for the project: every flavor × entry point × mode, with the flavor's dart-define file. program is only set for an entry point other than lib/main.dart.
launchJsonFile(FlutterProject project) → File
The .vscode/launch.json file of project; it may not exist.
launchLabel(String s) → String
clientDb and pre_prod become CLIENT DB and PRE PROD.
matchBuild(Iterable<BuildRecord> records, String text) → BuildRecord?
The build that produced the crash in text, by Dart build id first, then by a versionName+versionCode that appears in the report. Null when nothing matches or several builds share the version.
mergeLaunchJson(String? existing, List<LaunchEntry> entries) → String
existing with entries added to its configurations, keeping everything else (comments, formatting, other configurations). With no existing text a new file is made.
missingGitignoreEntries(FlutterProject project, {required String outputDir, required String configFileName}) → List<String>
Entries that should be in .gitignore but are not: the build output folder and the personal config overlay (flutter_buildkit.local.yaml). The main config is meant to be committed.
newBuildId(DateTime time) → String
20261001-071230-a1b2: sortable and unique enough for a local ledger.
normalizeName(String s) → String
Lower case letters and digits only, so clientDb, client_db and Client-DB compare equal.
parseDefaultApplicationId(String gradle) → String?
applicationId inside defaultConfig { }.
parseFirebaseAppId(String googleServicesJson, String? packageName) → String?
mobilesdk_app_id of the client matching packageName (or the first).
parseGradleFlavors(String gradle) → List<GradleFlavor>
Flavor names (and application ids) declared in productFlavors { }, for both Groovy (dev { }) and Kotlin DSL (create("dev") { }).
parseKeys(List<int> bytes) → List<KeyPress>
Parses the bytes of one terminal read into key presses. A terminal delivers a whole escape sequence in one read, so a lone ESC byte is the Escape key and ESC [ A is the up arrow.
parseLaunchConfigs(String text) → List<LaunchConfig>
Reads the dart configurations of a launch.json text (JSONC: comments and trailing commas are fine). Throws FormatException for invalid text.
parseNumberList(String text) → List<int>?
Parses 1,3, 1-3 or 1 3 into the numbers they name (1-based), or null when the text is not a number list.
pickDartSymbols(String dartSymbolsDir, String traceText) → String
Picks the split-debug-info file for the trace's architecture, or the folder itself when it can't tell (flutter symbolize accepts both).
planLaunchJson(AppConfig config, FlutterProject project) → LaunchPlan
Works out which entries to add to the project's launch.json.
planPreBuild(AppConfig config, Iterable<PreBuildStep> steps) → List<PreBuildCommand>
Commands for steps, in the order that works: flutter clean wipes .dart_tool, so it is always followed by pub get before code generation.
pruneBuilds(Ledger ledger, BuildManager manager, {int? logDays, int? symbolsKeepLast, bool dryRun = false, DateTime? now}) → Future<PruneResult>
Applies the retention settings.
pruneEmptyParents(String root, Directory dir) → Future<void>
Deletes dir and its parents while they are empty, stopping before root itself.
readElfBuildId(File file) → String?
The GNU build id of an ELF file as lower case hex, or null when file is not an ELF file with one.
renderSelect(String title, SelectModel m, {int maxRows = 12, Style style = Style.plain, int? width}) → List<String>
Renders model as lines of text. maxRows is how many rows the list may take, indicator lines included; the list scrolls to keep the cursor in view. With a width, every line is cut to fit so none wraps onto a second terminal row.
renderTable(List<String> header, List<List<String>> rows, {Style style = Style.plain, String decorate(int column, String paddedCell)?}) → String
Renders rows as an aligned table. Cells are padded on their plain text first, then decorate may color them, so ANSI codes never break the alignment.
scanDartEntries(String dir, {Iterable<String> extra = const [], Iterable<String> skip = const []}) → List<DartEntry>
Finds the entry points of the project at dir, wherever they are: every Dart file under lib/ that defines main(), and files named like main.dart, main_x.dart or x_main.dart elsewhere. Platform folders, tests, generated code and hidden folders are skipped. extra adds files (relative paths, e.g. a launch.json program) that exist even if they do not look like entry points.
selectListRows(int terminalRows, SelectModel m) → int
The rows a list may use for its items and its "more above/below" lines, given a terminal of terminalRows. Everything else renderSelect draws (title, help, and the filter, number and error lines when shown) is subtracted, plus one row for the cursor line under the menu, so the menu never grows taller than the screen. Never less than 3.
selectWindow(int total, int cursor, int budget) → (int, int)
Picks which items of a list of total to show in budget rows, keeping cursor in view. Returns (start, end). The "more above/below" lines take part of the budget, but only when items are really hidden: a list that fits shows every item and no indicator.
splitCommandLine(String text) → List<String>
Splits a command line into arguments on whitespace; text inside single or double quotes stays together and loses the quotes, so "C:\Program Files\flutter\bin\flutter.bat" --verbose is two arguments. Backslashes are ordinary characters (they are Windows path separators). An unclosed quote runs to the end.
stripJsonc(String text) → String
Removes comments and trailing commas so the text is plain JSON.
suggestConfig(FlutterProject project) → List<Suggestion>
Reads the Flutter project and proposes config values: flavors with their entry point, dart-define file, package name and Firebase app id, extra entry points, whether build_runner / gen-l10n / Crashlytics / Sentry are used, FVM, a layout that fits, and a Play key named in fastlane.
traceBuildId(String text) → String?
The Dart build_id a crash report names (build_id: 'abc...'), or null.
verifyLedger(Ledger ledger, {bool hash = true}) → Future<VerifyReport>
Compares the ledger with the build folders on disk.
writeLaunchJson(FlutterProject project, String text) → void
Writes text to .vscode/launch.json, keeping the old file as launch.json.bak once.

Exceptions / Errors

BuildException
Thrown when a build cannot be run or flutter build fails.
ConfigException
Thrown when the config file or one of its values is invalid.
ConsoleAbort
Thrown when the user presses Ctrl-C inside a prompt.
LaunchJsonException
Thrown when launch.json cannot be read or changed safely.
LedgerException
Thrown when the ledger file cannot be read or a change is invalid.
PathTemplateException
Thrown when a name or folder template is invalid.
PlayException
Thrown when an upload to Google Play is rejected or cannot be attempted.
PlayTrackInUseException
Thrown when the track already carries a staged or halted rollout that an upload would drop. Upload again with replaceExisting to go ahead.
PreBuildException
Thrown when a pre-build command cannot start or exits non-zero.
SymbolUploadException
Thrown when symbols cannot be uploaded, e.g. none exist or settings are missing.
TraceException
Thrown when a trace cannot be symbolicated, e.g. symbols are missing.