beej 1.2.0
beej: ^1.2.0 copied to clipboard
Plants a new Flutter app — material_ui, Riverpod, go_router, en/ne localization, theming, fastlane and the store-screenshot harness, with Appwrite, sqflite and the rest as explicit choices.
beej #
बीज — plant a new Flutter app.
A Dart CLI that generates a complete, buildable Flutter project: material_ui,
Riverpod, routing, localization, theming with live personalization, an About
module, release tooling and agent config — with the parts that genuinely differ
left as explicit choices.
It is opinionated rather than general-purpose. The opinions came from thirteen
production Flutter apps that had converged on one stack but drifted into five
lib/ layouts, four analyzer configs and four localization setups. Defaults are
neutral (com.example, English), and beej config is where you make them
yours.
dart pub global activate beej
beej config set org com.acme # once, not per project
beej create tipot # interactive
beej create tipot --yes # take every default
beej create --spec app.yaml # from a file
beej create tipot --yes --dry-run # show the plan, write nothing
What you always get #
-
material_ui— Material decoupled from the Flutter SDK. Nothing underlib/importspackage:flutter/material.dart, and a generated test fails the build if that ever changes. -
Riverpod 3, no code generation.
-
Localization in
lib/l10n/— English and Nepali, actually translated. -
Theming from design tokens, with working personalization: accent colour, theme mode, language and text size, persisted and applied before the first frame.
-
The About module — privacy policy, open-source licences, share, rate, more apps, contact support — wired into routes and the settings list.
-
Error capture —
FlutterError.onError,PlatformDispatcher.onErrorand a guarded zone aroundmain, feeding a deduplicated, size-capped on-device log and a Diagnostics screen the user can share. It stays on the device: a shipped app that cannot see its own runtime failures is guessing, and the worstmaterial_uifailures never reachflutter analyze. -
Responsive helpers, shared UI (
AsyncView,EmptyView,ErrorView,SectionLabel,context.toast,context.confirm), and aLauncherfor every hand-off to another app. -
PROJECT.mdas the single guide, withCLAUDE.mdandAGENTS.mdpointing at it — scope, workflow, conventions, the responsive contract, a testing strategy and a done-checklist. -
Agent skills in
.claude/skills/:material-ui(the frozen-vs-modern Material traps),store-readiness(will the submission be accepted),app-store-optimization(will the listing be found and installed), andmobile-ui-design. -
CI on day one —
.github/workflows/ci.ymlrunsflutter analyzeandflutter teston every push and pull request, so the generated guards actually hold. Releasing stays separate and manual. -
Store screenshots as a workflow — manual workflows capture on a matching emulator or simulator, render with moksha, and open a PR. One renderer covers Play phone and tablet plus iPhone and iPad, from one committed copy file, so the listings look like the same app. Every image is checked against the store's upload rules first — an alpha channel, Play's 2:1 aspect limit and Apple's accepted dimensions each reject a perfectly valid PNG, and all three now fail in CI instead of at upload.
-
A skill for beej itself, shipped the way Dart packages now distribute them, so an agent in any repo can learn to scaffold with it:
dart run skills@ add https://github.com/lohanidamodar/beej.git -
Agent tooling:
.mcp.jsondeclaring the Dart MCP server (it ships inside the Dart SDK, so nothing to install) plus Appwrite's hosted server when that backend is on, and project-scoped skills under.claude/skills/. Turn it all off with--no-agent-config, or narrow it with--skills=material-ui.
What you choose #
| Option | Values | Default |
|---|---|---|
org |
reverse-DNS prefix | com.example |
locales |
en plus any of ne |
en |
platforms |
android, ios, web, windows, macos, linux | all |
backend |
appwrite, none |
none |
router |
go_router, navigator |
go_router |
nav |
tabs, drawer, tabs+drawer |
tabs+drawer |
database |
sqflite, none |
none |
icons |
picons, material |
picons |
features.inAppUpdate |
bool | true |
features.notifications |
bool | false |
features.nepaliDates |
bool | false |
features.review |
bool | true |
tooling.fastlane |
bool | true |
tooling.githubWorkflow |
bool | true |
tooling.screenshots |
bool | true |
agents.mcp |
bool | true |
agents.skills |
all / none / list |
all |
beej bricks lists what each contributes.
Making it yours #
beej ships neutral defaults — com.example, English only, no About URLs — so
it is useful to anyone. beej config is where you set yours once instead of
passing the same flags to every create:
beej config # show current settings and their file
beej config keys # everything settable
beej config set org com.acme
beej config set locales en,ne
beej config set about.privacyPolicyUrl https://acme.com/{name-kebab}-privacy
beej config unset locales # back to the built-in default
The file is $XDG_CONFIG_HOME/beej/config.yaml (%APPDATA%\beej on Windows),
overridable with BEEJ_CONFIG. It is a spec file without a name, so it
accepts exactly the same keys as --spec and is hand-editable.
About URLs accept {name} and {name-kebab}, expanded per project — which is
what makes a per-app privacy-policy URL storable as a one-time preference.
Anything you do not set is simply absent: an About row is generated only when its value exists, because a row linking nowhere is worse than no row. beej warns when no privacy-policy URL is configured, since both stores require one before release.
Resolution order: built-in defaults < config < spec file < flags.
For agents #
beej spec --schema # JSON Schema for the spec file
beej spec --example # annotated example
beej create --spec app.yaml --yes
Precedence is flag > spec file > prompt > default. --yes never prompts,
so it is safe in a script. --dry-run prints the resolved plan and writes
nothing.
How it works #
flutter create produces the platform folders, then bricks render template
files over that skeleton. Bricks are additive — a feature that is off simply
never contributes. Three registries in the generated app are the seams a
feature extends: core/bootstrap.dart, core/router/routes.dart, and
features/settings/tiles.dart.
Install #
dart pub global activate beej
One command, no clone. pub compiles a snapshot at activation time, so runs are
clean and quick (~70ms) and nothing is re-resolved per invocation. Make sure
~/.pub-cache/bin is on your PATH.
For an unreleased change, activate from git instead:
dart pub global activate --source git https://github.com/lohanidamodar/beej.git
If you are working on beej itself #
./tool/install.sh # compiles a standalone binary to ~/.local/bin/beej
~3ms rather than ~70ms, and it re-embeds templates first so a stale one cannot ship. The binary does not track source edits, so re-run it after changing beej.
What not to use #
dart pub global activate --source path . re-resolves dependencies on every
invocation and prints "Resolving dependencies..." to stdout, which corrupts
the output any agent or script is reading. It also starts in ~3s. Use one of
the two above instead.
Development #
dart test # beej's own tests — seconds
dart run tool/verify_matrix.dart # generate 10 configs, analyze + test each
dart run tool/verify_matrix.dart minimal --keep
dart run tool/embed_templates.dart # after editing anything under templates/
Editing a template takes effect immediately when running from source —
TemplateSource prefers the directory and falls back to the embedded copy. A
test asserts the two agree, so a forgotten re-embed fails CI rather than
shipping stale templates in a binary.
The matrix is the real safety net. Mutually exclusive bricks can never coexist in one project, so "the templates compile" is not achievable — proving the output compiles is what keeps fifteen toggles from rotting.
Package versions live in one place: lib/src/bricks/versions.dart. Bump there,
then re-run the matrix.
What a generated project depends on #
One external repository: popupbits/fastlane-plugin-play_publisher, which is
public. The release workflow is written into the project rather than calling a
shared reusable workflow, so nothing in a generated repo depends on private
infrastructure.
Known limitations #
The screenshot workflows have never run on a runner. Everything upstream of
them is verified — capture, the job builder, and moksha rendering correct sizes
for every device family — but emulator boot, the moksha checkout and
gh pr create are unproven. Expect to fix something the first time.
No App Store preview video. moksha renders stills. Apple's 15-30 second recording is a separate job.
material_ui is an unusual choice. It is Material decoupled from the
Flutter SDK, and everything generated here imports it instead of
package:flutter/material.dart. If you want plain Flutter Material, beej is
the wrong starting point — that assumption runs through the templates, the
tests and the docs, and is not a flag you can flip.