<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yevhen Beshkarov</title>
    <description>The latest articles on DEV Community by Yevhen Beshkarov (@ybeshkarov).</description>
    <link>https://dev.to/ybeshkarov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4149771%2Fa62933f9-d0ef-42c0-996f-f4cc6d54bb58.jpeg</url>
      <title>DEV Community: Yevhen Beshkarov</title>
      <link>https://dev.to/ybeshkarov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ybeshkarov"/>
    <language>en</language>
    <item>
      <title>SMF 0.3: an open-source Flutter project generator</title>
      <dc:creator>Yevhen Beshkarov</dc:creator>
      <pubDate>Tue, 29 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/ybeshkarov/smf-03-an-open-source-flutter-project-generator-5dja</link>
      <guid>https://dev.to/ybeshkarov/smf-03-an-open-source-flutter-project-generator-5dja</guid>
      <description>&lt;p&gt;Say My Frame (SMF) is a free, open-source Flutter project generator whose modules are checked together: its CI generates apps from many combinations of them and analyzes each one. It also sets up Firebase Crashlytics and Analytics all the way to &lt;code&gt;flutter build ipa&lt;/code&gt;. You choose what the app needs, such as go_router, tabs at the bottom, get_it, and BLoC or Riverpod, and &lt;code&gt;smf create&lt;/code&gt; generates a Flutter project in which these parts already work together. &lt;a href="https://pub.dev/packages/smf_flutter_cli" rel="noopener noreferrer"&gt;Version 0.3 is out on pub.dev&lt;/a&gt;. It does less than the word "generator" might promise, so this post is about what it does today, what it can't do yet, and where I want to take it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dart pub global activate smf_flutter_cli
smf create my_app &lt;span class="nt"&gt;--org&lt;/span&gt; com.example &lt;span class="nt"&gt;-m&lt;/span&gt; home,bottom_tabs,get_it,bloc &lt;span class="nt"&gt;--no-input&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdoc.saymyframe.com%2Fdemo%2Fsmf_create.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdoc.saymyframe.com%2Fdemo%2Fsmf_create.gif" alt="smf create in a terminal asks for the app name, the organization and the modules, adds go_router for the start screen and generates a Flutter app with tabs at the bottom, BLoC and get_it" width="799" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What you get is a regular Flutter project. It has no SMF package in its dependencies, and nothing in it has to be generated again, so the code is yours from the first commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SMF 0.3 is a rewrite
&lt;/h2&gt;

&lt;p&gt;SMF 0.1 came out in August 2025, and 0.2 followed in September. Each module brought its own templates, and an engine patched the shared files. It worked for the combinations I had tried.&lt;/p&gt;

&lt;p&gt;This September I ran the published 0.2 with Flutter 3.44. It reported success and generated apps that did not build. Some modules assumed go_router or get_it, and CI back then never generated an app or ran the analyzer, so nothing caught it.&lt;/p&gt;

&lt;p&gt;0.3 starts from one rule: modules do not know each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Flutter project generator does today
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Modules name roles, not other modules
&lt;/h3&gt;

&lt;p&gt;The start screen needs a router, not go_router. The analytics module logs screen views when the app has a router, whichever router that is. SMF calls these shared parts roles, and there are eight of them: app entry, router, layout, state management, dependency injection, events, crash reporting and analytics. A role can have more than one provider. BLoC and Riverpod both provide state management.&lt;/p&gt;

&lt;p&gt;Modules don't edit files either. Shared files, such as &lt;code&gt;bootstrap.dart&lt;/code&gt;, have sockets: named places where modules put code without knowing who else puts code there. This is how &lt;code&gt;firebase_analytics&lt;/code&gt; hands the router a listener of the screen, and only when the app has a router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SocketContribution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;RouterRole&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;screenListeners&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'logFirebaseScreenView'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;imports:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="n"&gt;ImportRef&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'core/analytics/firebase_analytics_service.dart'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;show:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'logFirebaseScreenView'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;routerRole&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before it writes a single file, &lt;code&gt;smf create&lt;/code&gt; checks the rules. A module may put code only into the sockets of its own roles, of the app entry and of the modules it depends on, and every file of the app has exactly one owner. The tests of every module run the same checks. CI then generates apps from combinations of the modules, 18 of them for the modules of the CLI when I wrote this, runs &lt;code&gt;flutter analyze&lt;/code&gt; on each one and runs tests in some of them. The &lt;a href="https://doc.saymyframe.com/concepts/module-model" rel="noopener noreferrer"&gt;module model&lt;/a&gt; and &lt;a href="https://doc.saymyframe.com/concepts/independence" rel="noopener noreferrer"&gt;module independence&lt;/a&gt; pages of the docs go deeper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Firebase Crashlytics and Analytics, set up all the way to &lt;code&gt;flutter build ipa&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Three modules bring Firebase: &lt;code&gt;firebase_core&lt;/code&gt;, &lt;code&gt;firebase_crashlytics&lt;/code&gt; and &lt;code&gt;firebase_analytics&lt;/code&gt;. Before it generates anything, SMF checks the machine for the Firebase CLI, a Firebase login and the FlutterFire CLI. In a terminal it offers to set up what is missing, and asks first. After generation it offers to run &lt;code&gt;flutterfire configure&lt;/code&gt; with the ids of the app.&lt;/p&gt;

&lt;p&gt;Two details took longer than the rest. With a router, &lt;code&gt;firebase_analytics&lt;/code&gt; logs one screen view for each screen the user sees, tab switches included. And on macOS, SMF fixes the Crashlytics build phase that flutterfire_cli 1.4.1 adds, because &lt;code&gt;flutter build ipa&lt;/code&gt; fails in that phase when the app uses Swift Package Manager. That story gets a post of its own. Until then, the &lt;a href="https://doc.saymyframe.com/guides/firebase" rel="noopener noreferrer"&gt;Firebase guide&lt;/a&gt; has the details.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Flutter app with go_router, tabs, get_it and BLoC or Riverpod
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;-m home,bottom_tabs,get_it,event_bus&lt;/code&gt;, SMF adds two more modules and tells you why:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Adding flutter_core: the only provider of the app entry role, which every app needs.
Adding go_router: the only provider of the router role, which home requires.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Screens navigate with &lt;code&gt;context.nav.home.home().go()&lt;/code&gt;, which doesn't mention go_router. Code that needs a service asks the &lt;code&gt;ServiceLocator&lt;/code&gt; of the DI role, which doesn't mention get_it. With &lt;code&gt;bottom_tabs&lt;/code&gt;, the destinations of the features become tabs of a &lt;code&gt;NavigationBar&lt;/code&gt;, and each tab keeps its own stack. &lt;code&gt;bootstrap()&lt;/code&gt; runs the start-up code of the modules in four phases before the first frame. The Android and iOS projects are the ones &lt;code&gt;flutter create&lt;/code&gt; writes in Flutter 3.44.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://doc.saymyframe.com/getting-started/generated-app" rel="noopener noreferrer"&gt;tour of a generated app&lt;/a&gt; goes file by file and names the module that wrote each one.&lt;/p&gt;

&lt;h3&gt;
  
  
  A plan before anything is written
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;--explain&lt;/code&gt; prints what would be generated and stops. It lists the modules and why each is there, the roles, the dependencies, the commands that would run after generation and the state of the machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;Modules&lt;/span&gt;
  &lt;span class="s"&gt;home&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;requested&lt;/span&gt;
  &lt;span class="s"&gt;bottom_tabs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;requested&lt;/span&gt;
  &lt;span class="s"&gt;get_it&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;requested&lt;/span&gt;
  &lt;span class="s"&gt;event_bus&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;requested&lt;/span&gt;
  &lt;span class="s"&gt;flutter_core&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;the only provider of the app entry role, which every app needs&lt;/span&gt;
  &lt;span class="s"&gt;go_router&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;the only provider of the router role, which home requires&lt;/span&gt;

&lt;span class="s"&gt;Roles&lt;/span&gt;
  &lt;span class="s"&gt;Layout&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bottom_tabs&lt;/span&gt;
  &lt;span class="s"&gt;Dependency injection&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get_it&lt;/span&gt;
  &lt;span class="s"&gt;Events&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;event_bus&lt;/span&gt;
  &lt;span class="s"&gt;App entry&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter_core&lt;/span&gt;
  &lt;span class="s"&gt;Router&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go_router&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For scripts and CI there are &lt;code&gt;--no-input&lt;/code&gt;, &lt;code&gt;--skip-external-setup&lt;/code&gt;, &lt;code&gt;--strict&lt;/code&gt; and exit codes: 0, 1, 64, 70 and 130. SMF renders the app in memory and finishes it in a temporary directory, so a run that fails or that you cancel leaves no half-made app in your project. The &lt;a href="https://doc.saymyframe.com/guides/smf-create" rel="noopener noreferrer"&gt;reference of smf create&lt;/a&gt; lists every option.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modules of your own
&lt;/h3&gt;

&lt;p&gt;Modules are Dart packages built on &lt;code&gt;smf_contracts&lt;/code&gt;, and yours follow the same rules as the built-in ones. The contract harness in &lt;code&gt;smf_pipeline&lt;/code&gt; generates in memory every app that matters for your module and checks it. To generate apps with your module, you write a small command of your own with &lt;code&gt;runCli&lt;/code&gt; and &lt;code&gt;smfModules&lt;/code&gt;. &lt;a href="https://doc.saymyframe.com/extending" rel="noopener noreferrer"&gt;Extending SMF&lt;/a&gt; shows how, and one of its pages builds &lt;a href="https://doc.saymyframe.com/extending/feature-module" rel="noopener noreferrer"&gt;a counter feature with BLoC and Riverpod variants&lt;/a&gt; from start to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  What SMF 0.3 can't do yet
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The app you get is a skeleton. There is one feature module, &lt;code&gt;home&lt;/code&gt;, a start screen with the name of the app. The tab bar stays hidden until a second screen is a destination, so after &lt;code&gt;smf create&lt;/code&gt; you see one screen.&lt;/li&gt;
&lt;li&gt;There are no modules for sign-in, networking, localization or theming. The list has eleven modules, and I wanted the foundation to hold before adding more.&lt;/li&gt;
&lt;li&gt;SMF creates new apps. It can't add a module to an app you already have.&lt;/li&gt;
&lt;li&gt;It generates Android and iOS apps for Flutter 3.44 or newer and Dart 3.12 or newer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flutterfire configure&lt;/code&gt; needs you at the keyboard to log in and pick a Firebase project. In CI, SMF leaves it for later and prints the command.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where it's going
&lt;/h2&gt;

&lt;p&gt;Two things are next on my list: modules with real screens, and a way to add modules to an app that already exists. There are no dates. SMF is a side project, and I'd rather ship things when they work.&lt;/p&gt;

&lt;p&gt;If you have a module in mind, tell me which one on &lt;a href="https://saymyframe.com/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or in &lt;a href="https://github.com/saymyframe/smf_flutter_cli/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrading from 0.2
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The state manager is a module now: &lt;code&gt;-m bloc&lt;/code&gt; or &lt;code&gt;-m riverpod&lt;/code&gt; instead of &lt;code&gt;-s&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--start&lt;/code&gt; replaces &lt;code&gt;-r&lt;/code&gt; and &lt;code&gt;--route&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--strict&lt;/code&gt; and &lt;code&gt;--on-conflict&lt;/code&gt; are options of &lt;code&gt;create&lt;/code&gt; now, so they go after it: &lt;code&gt;smf create my_app --strict&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tabs at the bottom are a module of their own, &lt;code&gt;bottom_tabs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The module API of &lt;code&gt;smf_contracts&lt;/code&gt; is new, so modules written for 0.2 need a rewrite.&lt;/li&gt;
&lt;li&gt;Apps generated by 0.2 stay as they are. SMF generates an app once and never touches it again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same list, with what's new in 0.3, is in the &lt;a href="https://github.com/saymyframe/smf_flutter_cli/releases/tag/smf_flutter_cli-v0.3.0" rel="noopener noreferrer"&gt;release notes of SMF 0.3.0 on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the Flutter project generator
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dart pub global activate smf_flutter_cli
smf create

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SMF is tested on macOS, Linux and Windows. The &lt;a href="https://doc.saymyframe.com/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; starts with installation and your first app, the &lt;a href="https://saymyframe.com/" rel="noopener noreferrer"&gt;Say My Frame site&lt;/a&gt; shows the roles and modules at a glance, and the code is &lt;a href="https://github.com/saymyframe/smf_flutter_cli" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;. Stars there are welcome, but only if you actually like it.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>opensource</category>
      <category>firebase</category>
    </item>
  </channel>
</rss>
