Skip to main content

beej 1.4.0 copy "beej: ^1.4.0" to clipboard
beej: ^1.4.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 under lib/ imports package: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.onError and a guarded zone around main, 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 worst material_ui failures never reach flutter analyze.

  • Marionette — an agent can drive the running app: read the widget tree, tap, type, scroll, screenshot, hot reload. MarionetteBinding is installed under kDebugMode only, so a release build carries none of it, and the MCP server is declared in .mcp.json. It needs dart pub global activate marionette_mcp once per machine.

  • Debug builds that install beside the store build. Debug and profile builds carry <applicationId>.dev and a Dev launcher label, so flutter run adds a second icon rather than replacing the copy installed from the store — testing a change never costs you the real app and its data. Release is untouched, and so is everything that names the store build: the Gradle namespace, AppConfig.androidApplicationId, the market:// rate link and fastlane's APP_IDENTIFIER. Off with --no-dev-suffix.

  • An accessibility baseline, tested. Every tappable is labelled and at least 48dp, on desktop too; text clears WCAG AA contrast in both themes for every accent; the device text size is honoured uncapped and every screen lays out at 200%; and on desktop, Tab finishes the navigation before it enters the content. test/accessibility_test.dart runs meetsGuideline on each shipped screen, and a new screen joins it with one line.

  • Seams for analytics and feedback. Analytics and UserFeedback interfaces with no-op defaults in lib/core/hooks/. Nothing leaves the device until you install an implementation with a provider override.

  • Responsive helpers, shared UI (AsyncView, EmptyView, ErrorView, SectionLabel, context.toast, context.confirm), and a Launcher for every hand-off to another app.

  • PROJECT.md as the single guide — scope, workflow, conventions, the responsive contract, a testing strategy and a done-checklist. AGENTS.md is canonical above it: it carries the shared working rails between <!-- popupbits:rails:begin --> / <!-- popupbits:rails:end --> markers, then points at PROJECT.md. CLAUDE.md is exactly @AGENTS.md — the import form Claude Code follows, since it never reads AGENTS.md on its own and a Markdown link is not an import.

  • 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), and mobile-ui-design.

  • CI on day one — .github/workflows/ci.yml runs flutter analyze and flutter test on 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.json declaring 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.devSuffix bool true
features.inAppUpdate bool true
features.notifications bool false
features.nepaliDates bool false
features.onboarding 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.

Onboarding. features.onboarding: true adds a skippable welcome of two to four pages (three placeholders to start), shown once on first launch, before sign-in.

Where app data lives. database: sqflite stores its file through appDataDirectory() in lib/core/storage/app_data_directory.dart, and the onboarding seen-flag, and anything else the app keeps for itself — caches, settings files, a Hive box — belongs there too. On Android and iOS that is the app-private documents directory. On desktop it is the support directory (%APPDATA%\<org>\<name> on Windows, from the runner's company and product names), never getApplicationDocumentsDirectory(), which on desktop is the user's real Documents folder.

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.

A brick can also patch a file flutter create wrote, for the few values that cannot be owned as a whole file — project.pbxproj carries generated UUIDs, and owning AndroidManifest.xml would mean re-shipping the activity config on every Flutter release. Patches are anchored and asserted: if a Flutter release changes one of those files, generation fails naming the file and the anchor rather than quietly skipping the edit. --dry-run lists them alongside the files.

Install #

Requires Flutter 3.50 (Dart 3.14) or newer. Generated apps target the same line: sdk: ^3.14.0, flutter: '>=3.50.0'.

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 11 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.

0
likes
140
points
154
downloads

Documentation

API reference

Publisher

verified publisherdlohani.com.np

Weekly Downloads

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.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

args, io, mustache_template, path, yaml, yaml_edit

More

Packages that depend on beej