flutter_core
| Package | Kind | Provides the role | Needs | Choose with |
|---|---|---|---|---|
smf_flutter_core | scaffold | App entry | nothing | added to every app |
flutter_core creates the Flutter app that every SMF app starts from: the Android and iOS projects, main(), the start-up sequence, the root widget, pubspec.yaml, and, in an app that no route starts, the screen that the app starts on instead. It provides the app entry role, and every app needs exactly one provider of that role, so smf create adds flutter_core to every app.
What it adds to the app
| File | What it holds |
|---|---|
lib/main.dart | main(): WidgetsFlutterBinding.ensureInitialized(), await bootstrap(), then runApp() with the root widget inside the wrappers of other modules. |
lib/bootstrap.dart | bootstrap(), where the other modules put their start-up code, phase by phase. It imports neither material nor cupertino. |
lib/app.dart | App, the root widget: a MaterialApp.router with the router's configuration when the app has a router, or else a MaterialApp that shows the fallback start screen. App creates it in its build, with the arguments that modules and roles give it, such as the theme. Its builder holds the widgets that other modules put around the content of every route. |
lib/core/app/fallback_start_screen.dart | Only in an app that no route starts: FallbackStartScreen, the screen that such an app starts on, and FallbackStartView, which the screen builds with its texts. |
test/core/app/fallback_start_screen_test.dart | Only in an app that no route starts: a widget test of the fallback start screen. |
pubspec.yaml | The dependencies of all modules, with Dart ^3.12.0 and Flutter >=3.44.0, and flutter_lints for the lints. |
analysis_options.yaml | The lints of a new Flutter app, with build/** left out of the analysis. |
README.md | The name of the app, the line "Generated with Say My Frame", and the sections that other modules add. |
.gitignore, .metadata | The files of the same names that flutter create writes. |
android/, ios/ | The projects of flutter create of Flutter 3.44, with the identifiers of the app and the flavors dev, staging and prod. |
The native projects are those that flutter create --platforms=android,ios writes with Flutter 3.44.2, with a few changes:
- The names and identifiers come from the app. The Android namespace, application id and Kotlin package path, and the iOS bundle id, come from
--organd the app name. - The flavors are plain text, with no package. The Gradle file of the app has
flavorDimensionsandproductFlavors, each with its id suffix and its name on the device. On iOS, the Xcode project hasDebug-<flavor>,Profile-<flavor>andRelease-<flavor>in each of its three configuration lists, and a shared scheme for each flavor. The Android label is the placeholder${appName}and the iOS display name the settingAPP_DISPLAY_NAME, so each flavor has a name of its own: the name of the app in title case, withDevorStagingafter it, and without either forprod. The schemeRunnerand the plain configurations stay, with the id and the name ofprod. - The minimum iOS version is 15.0, the lowest that Xcode 27 builds for, unless a module needs a higher one. The Xcode project has no development team.
- The Android manifest,
Info.plist,AppDelegate.swiftand the Gradle files have places where other modules add permissions, keys, components, code and plugins; see the sockets of the app entry. A place that gets nothing leaves no line. So an app whose modules add nothing there has the files offlutter create, apart from two changes below: an emptydependenciesblock in the Gradle file of the app, andRunner.entitlements, which the Runner target signs with. - The Gradle file of the app ends with a
dependenciesblock for the dependencies of the modules, which stays empty when no module adds one. It also has the two places of core library desugaring: the switch incompileOptionsand the library independencies. ios/Runner/Runner.entitlementsholds the entitlements of the iOS app, such as Push Notifications or Sign in with Apple, which modules add. Every build configuration of the Runner target signs with the file, also while it is empty, as Xcode does when it adds a capability. With nothing in it, the file changes nothing of how the app is signed.
The app entry role adds two files to every app too, whichever module provides it: lib/core/app/status_bar_icons.dart with StatusBarIcons, which the root wraps around the content of every route, and its widget test, test/core/app/status_bar_icons_test.dart. The widget asks the system for dark icons of the status bar on a light theme and for light ones on a dark theme. A screen with an AppBar gets the icons that suit its bar. The test is in every app, so flutter test has a test to run in an app whose modules bring none.
The app also gets AGENTS.md and CLAUDE.md, the guide for coding agents. They come from the app entry role, whichever module provides it, so an app with another provider of the role has them too. To the section "App entry" of the guide, the module adds where each flavor is in the native projects, the commands to run after a change, the rules of its root widget with the one MaterialApp of the app, where the test of a file goes, and that the entitlements of the iOS app are in ios/Runner/Runner.entitlements, which the Signing & Capabilities tab of Xcode edits too. See Working with a coding agent.
The fallback start screen
An app has the fallback start screen only when no route can start it: an app without a router, which shows the screen as the home of its root, and an app with a router whose features mark no route to start on, where the screen is at /. It is a screen for you, the developer of the app. An app with a feature whose route can start it, such as home, gets neither the screen nor its test nor its texts.
The screen shows, in the middle:
- The app as a cell of the periodic table, with smaller cells around it.
smf createmakes the symbol and the number of the cell from the name of the app,Maand 5 formy_app: the first letters of its first two words, or the first two letters of a name of one word, and how many letters and digits the name has. - The name of the app.
- A hint: "No start screen yet. Add a feature with a route, or replace this screen."
- The path of the file of the screen,
lib/core/app/fallback_start_screen.dart. A tap copies it, and a snack bar says so.
| Colors and fonts | The screen takes its colors and its text styles from the theme of the app, so it follows the theme mode. The path and the number of the cell are in the monospaced font of the device. |
| Motion | The screen comes in with a short fade, once. On a device that asks for less motion, it is there at once. |
| Large text | The screen scrolls when it is too small for what it shows. The letters of the cell keep their size, since the cell is a picture, and the path grows to at most one and a half times its size. |
| Screen readers | A screen reader passes over the cell, and announces the name of the app as a header and the path as a button. |
Its texts and the languages of the app
The hint and the word that the snack bar says before the path, Copied, are texts of the module, in English and in Ukrainian. In an app with a module that provides the localization, such as gen_l10n, they are the texts flutterCoreFallbackHint and flutterCoreFallbackCopied of the app, and the screen shows them in the language of the app. Without one, they are in English.
The module gives the localization role the two texts only in an app that has the screen. So such an app with a module that provides the localization is in English and in Ukrainian unless --locales says otherwise, also an app without a feature; see Languages.
FallbackStartScreen only reads the two texts and gives them to FallbackStartView, which shows the rest. In an app with gen_l10n:
class FallbackStartScreen extends StatelessWidget {
/// Creates the screen.
const FallbackStartScreen({super.key});
Widget build(BuildContext context) => FallbackStartView(
hint: context.l10n.flutterCoreFallbackHint,
copied: context.l10n.flutterCoreFallbackCopied,
);
}
So the widget test of the app shows the view with texts of its own, and needs neither the root of the app nor its texts:
/// Shows the view of the fallback start screen with texts of the test.
Future<void> _show(WidgetTester tester) => tester.pumpWidget(
const MaterialApp(
home: FallbackStartView(hint: 'A hint', copied: 'Copied'),
),
);
The test checks that the view names the app and the file, that a tap copies the path, and that the view fits a small phone with the largest text size.
Give the app a start screen
In an app that shows the screen, replace it with the first screen of your own. The widget test is a test of FallbackStartView, so change it with the view, or remove it with the view, and with the two texts in an app with gen_l10n.
Start-up phases
bootstrap() runs the start-up code that modules and roles add, in four phases, in this order:
| Phase | What goes there |
|---|---|
| early | Code that must come before anything else |
| platform | Platform services, such as await Firebase.initializeApp(...), installCrashReporting() and await initPreferences() |
| DI | The services of the app: await registerDependencies(); |
| late | Code that needs those services |
main() runs the root widget inside the wrappers that modules add, such as the ProviderScope of Riverpod. App builds below them, so an argument of the root MaterialApp can read an inherited widget among the wrappers from the BuildContext of App, and the root rebuilds when that widget notifies. The theme mode reaches the root this way.
Choosing it
Every app has a provider of the app entry role. While flutter_core is the only module that provides it, you don't choose it: smf create adds it by itself.
Requirements
The app needs Flutter 3.44 or newer and Dart 3.12 or newer, and runs on iOS 15 or newer. For iOS builds with Xcode 27, see troubleshooting.