Skip to main content

The smf create command

smf create <app name> [options]

smf create generates a Flutter app from modules in a new directory. Besides smf help, it is the only command of smf. smf --version prints the version, and smf create --help lists the options.

Options​

OptionWhat it does
-m, --modules=<module,module>The modules to add, separated by commas, such as -m home,bottom_tabs. The modules they need are added too. Without it, a run in a terminal asks for the modules.
--org=<com.example>The organization in reverse domain notation. It starts the identifiers of the app. Without it, a run in a terminal asks; otherwise it is com.example.
-o, --output=<directory>The directory in which the app's own directory is created. The default is the current directory.
--on-conflict=<prompt|replace|copy|cancel>What to do when the app's directory exists and is not empty; see below. The default is prompt.
--strictStop instead of leaving out a module that cannot work; see lenient and strict.
--explainPrint what would be generated and whether the machine is ready, then stop without changing anything; see below.
--skip-external-setupNever install tools, log in or configure external services; print what to run instead.
--[no-]inputWhether the run may ask questions in the terminal. With --no-input, everything comes from the options.
--start=<path>The full path of the screen the app starts on, such as /home. It can name any route without required parameters, and a run without a terminal needs it when several screens can start the app. An option of the router role.
--verboseReport more of what happens. It can come before or after create.
-h, --helpPrint the usage of the command.

The name and the identifiers​

The app name becomes the Dart package name of the app in snake_case, so My App, myApp and my-app all become my_app. The package name is also the name of the app's directory under --output. The package name must start with a Latin letter and contain only letters, digits and underscores. It cannot be a keyword of Dart, Java or Kotlin, or the name of a package every Flutter app depends on, such as flutter or collection.

Every part of the organization starts with a letter and has only letters, digits, hyphens and underscores, and no part is a keyword of Java or Kotlin. The identifiers of the app join the organization and the package name:

smf create my_app --org com.example
Android application id and namespacecom.example.my_app
iOS bundle idcom.example.my-app (bundle ids take hyphens, not underscores)

Questions​

smf create asks questions only when it runs in a terminal, without --no-input and without --explain. It asks for:

  1. the app name, unless it is given;
  2. what to do with an existing directory, unless --on-conflict says;
  3. the organization, unless --org gives it;
  4. the modules, unless -m gives them: first the modules without a role by kind, such as the features, then a provider for each role (see Create your first app);
  5. the provider of a role that the chosen modules require and that several modules provide;
  6. whether to set up what a check of the machine found missing, such as the Firebase CLI, unless the run skips external setup (see the machine and external setup);
  7. the start screen, when several screens can start the app and --start does not say;
  8. whether to run a step of a module after generation now, such as flutterfire configure.

Without a terminal, or with --no-input, the run asks nothing. Where it would have to ask, it stops with a usage error that says what to add: the app name, --on-conflict, a provider in -m, or --start. Without -m, the app gets only what every app needs.

An existing directory​

When the app's directory exists and is not empty, --on-conflict decides:

ValueWhat happens
promptA run in a terminal asks what to do. The default answer keeps the directory and creates the app next to it; the others replace the directory or cancel the run. Without a terminal, the run stops with a usage error.
replaceSMF generates the app first. Then it sets the old directory aside, moves the app into its place and deletes the old directory. If moving the app fails, the old directory goes back.
copyThe app goes into a new directory next to it: my_app copy, or my_app copy 2 if that exists too.
cancelNothing is generated, and smf create exits with code 1.

SMF decides this before it does anything else, such as installing a tool.

Lenient and strict​

By default, smf create is lenient. When a module cannot work in the app, such as a feature whose tab does not fit into the tab bar, it leaves that module out with a warning, plans the app again without it, and generates the rest. At the end it warns again about the modules the app is without.

With --strict, such a problem stops the run with exit code 1 before anything is generated. Use --strict in scripts and CI, where an app without a module you asked for is a failure.

Some problems stop the run in either mode. A problem that no single module causes, such as a usage error, is one of them. So is the problem of a module that no app can be made without, such as flutter_core, the only provider of the app entry. smf create does not leave such a module out, and stops with its problem and how to fix it, such as a Flutter SDK that is too old.

Explain​

--explain runs everything that decides the app, which means the selection of modules, the checks of the module model and the checks of the machine, and prints the result. It never asks, installs or logs in, and it generates no file. Where a real run would ask which module provides a role, it takes the first one and names the others.

smf create my_app --org com.example -m home,bottom_tabs,get_it,event_bus --explain
App my_app of com.example
Android application id: com.example.my_app
iOS bundle id: com.example.my-app
Directory: /Users/you/projects/my_app

Modules
home: requested
bottom_tabs: requested
get_it: requested
event_bus: requested
flutter_core: the only provider of the app entry role, which every app needs
go_router: the only provider of the router role, which home requires

Roles
Layout: bottom_tabs
Dependency injection: get_it
Events: event_bus
App entry: flutter_core
Router: go_router

Dependencies
get_it ^9.3.0 (get_it)
event_bus ^2.0.1 (event_bus)
flutter from the flutter SDK (flutter_core)
go_router ^17.5.0 (go_router)

Dev dependencies
flutter_test from the flutter SDK (flutter_core)
flutter_lints ^6.0.0 (flutter_core)

Machine
✓ Flutter SDK

Depending on the app, the report has more sections. "Left out (lenient mode)" lists the modules that the app would be without, and why. "Order of contributions" shows, for code that several modules put into the same place, the order they come in and the reason for it. "After generation" lists the commands that would run in the new app, such as flutterfire configure of the Firebase modules. "Machine" gets a line for each check a module needs, with instructions for what is missing:

Machine
✓ Flutter SDK
✗ Firebase CLI (for firebase_core): missing
Install it with "npm install -g firebase-tools", or see https://firebase.google.com/docs/cli.
An interactive run offers to set it up.
✗ Firebase login (for firebase_core): missing
Install the Firebase CLI, then log in with "firebase login", or on a remote machine, such as over SSH, with "firebase login --no-localhost".
✓ FlutterFire CLI 1.4.1 or a later 1.x (for firebase_core)
✓ Xcode project tools of flutterfire (for firebase_core)
✓ Setup of the Xcode project on a Mac (for firebase_core)

The machine and external setup​

Before it generates anything, smf create checks the machine: the Flutter SDK for every app, and whatever the modules need, such as the Firebase CLI for the Firebase modules. The Flutter SDK must satisfy the SDK constraints of the app, which means Flutter 3.44 or newer, or the run stops, as troubleshooting shows.

When something is missing, the checks of the built-in modules only warn and print instructions. In a terminal, SMF offers to set up what it can. Each question says what the check found, such as a tool that is missing or an older version of it, and how to set it up by hand, then asks whether to set it up now. With --skip-external-setup, it never installs anything, logs in or sets up an external service, and prints the instructions instead.

Some modules run a command in the new app, such as flutterfire configure. A module marks such a step as skippable when the app is complete without it, as firebase_core does. A skippable step may not run, because the run has no terminal, skips external setup or lacks a tool the step needs. It may also fail, or you may leave it for later. In each case smf create still creates the app and prints the command to run in it later, with the reason. When the command needs a tool that the machine lacks, the reason names that tool too, as on a machine without the Firebase CLI:

[WARN] Configuring Firebase with flutterfire is not done, because the run skips external setup, and Firebase CLI and Firebase login are missing. Run it in the app: dart pub global run flutterfire_cli:flutterfire configure --platforms=android,ios --overwrite-firebase-options --ios-bundle-id=com.example.my-app --android-package-name=com.example.my_app

A step that the app cannot do without either runs or stops the run.

Exit codes​

CodeMeaning
0The app was created, or smf printed help, the version or the report of --explain.
1Generation failed, and the messages say why: an error of a module with --strict, or of a module that every app needs, a machine without what the app needs, such as a Flutter SDK that is missing or too old, a failed command, or --on-conflict cancel. If the app was generated already, it stays in a temporary directory that the message names.
64The command line was wrong or incomplete: an unknown option or module, a missing app name, or a question that a run without a terminal cannot ask.
70An unexpected error, which is a bug of smf or of a module. Run again with --verbose for the full log, and please report it.
130The run was cancelled with Ctrl-C, by the end of the input, with Cancel in the question about an existing directory, or, on macOS and Linux, with SIGTERM. Nothing of the app is left behind.

In scripts and CI​

A run for a script names everything and asks nothing:

smf create my_app \
--org com.example \
-m home,bottom_tabs,get_it,bloc \
--no-input \
--skip-external-setup \
--strict \
--on-conflict replace

SMF's own CI generates its apps the same way, without a terminal and with --strict. It checks each app with flutter analyze, and runs tests in the apps of some modules, such as the start-up of Firebase.