Hello. I'm Masaru Hirose.

I introduced the Masamune framework, which enables stable and fast development of applications in Flutter.

Hello. My name is Masaru Hirose.

I would like to write several introductory articles to let you know its charm and how to use it.

By mastering the Masamune framework, you will be able to develop ultra-fast, stable, and high-quality applications.

The first session is "Project Creation".


Naturally, you will need to install Flutter.

Install Flutter and get started. Downloads available for Windows, macOS, Linux, and ChromeOS operating systems.

Install katana_cli.

flutter pub global activate katana_cli

Run katana doctor and check that the necessary commands are installed.

If the required commands are not present, install them as needed.

$ katana doctor

#### Check the installation status of the commands required for the `katana` command.
[o] `flutter` exists at `/Users/xxx/.flutter/bin/flutter`
[o] `dart` exists at `/Users/xxx/.flutter/bin/dart`
[o] `npm` exists at `/usr/local/bin/npm`
[o] `git` exists at `/usr/bin/git`
[o] `keytool` exists at `/opt/homebrew/opt/openjdk/bin/keytool`
[o] `openssl` exists at `/usr/bin/openssl`
[o] `pod` exists at `/usr/local/bin/pod`

#### Check the installation status of the npm commands required for the `katana` command.
[o] `eslint` exists at `/usr/local/bin/eslint`
[o] `firebase` exists at `/usr/local/bin/firebase`
[o] `lefthook` exists at `/opt/homebrew/bin/lefthook`
[o] `gh` exists at `/opt/homebrew/bin/gh`

#### Check the installation status of the flutter commands required for the `katana` command.
[o] `flutterfire` exists at `/Users/xxx/.pub-cache/bin/flutterfire`

Create Project

Now that you have done the preliminary work, let's get started on the project!

Create an empty folder and execute the following command under it.

katana create [Application ID(e.g. com.test.myapplication)]

This time I will create an application with an application ID of net.mathru.test.

katana create net.mathru.test

Wait a few moments and the initial project will be created.

Once completed, do a test build.

flutter run --dart-define=FLAVOR=dev

If you are using VisualStudioCode, launch.json is created and can be debugged by F5.

image block

The familiar counter application is now activated.

image block


The lib folder structure looks like this.

image block

In these files, models/counter.freezed.dart, models/counter.g.dart, models/counter.m.dart, pages/home.page.dart, main.localize.dart, main.theme.dart are automatically created files, so I omit their description.


A file containing the entry points of the application.

Other settings such as application themes, translations, routing, adapters, etc. can be configured. This will be explained shortly.


A file in which the model portion (data portion) of the counter application is described.

An object that can be handled immutably (freezed), a function that can serialize and deserialize to Json (json_serializable), and a function that allows input and output to the database through that object (masamune_builder) are created together.

// counter.dart

/// Value for model.
class CounterModel with _$CounterModel {
  const factory CounterModel({
     @Default(ModelCounter(0)) ModelCounter counter,
  }) = _CounterModel;
  const CounterModel._();

  factory CounterModel.fromJson(Map<String, Object?> json) => _$CounterModelFromJson(json);

  /// Query for document.
  /// ```dart
  /// appRef.model(CounterModel.document());       // Get the document.
  /// ref.model(CounterModel.document())..load();  // Load the document.
  /// ```
  static const document = _$CounterModelDocumentQuery();

  /// Query for form value.
  /// ```dart
  /// ref.page.controller(CounterModel.form(CounterModel()));    // Get the form controller.
  /// ```
  static const form = _$CounterModelFormQuery();

If the following part of the above code is augmented with parameters according to the freezed and json_serializable conventions, it will become the data scheme for storing the data in the database.

const factory CounterModel({
   @Default(ModelCounter(0)) ModelCounter counter,
}) = _CounterModel;

ModelCounter is a special class that performs counting.

The value can be increased or decreased by counter.increment(1).

In this pattern, int is fine, but it is very useful to be able to count values without conflicts when using Firestore later on.


A file containing the views of the counter application.

// home.dart

class HomePage extends PageScopedWidget {
  const HomePage({

  /// Used to transition to the HomePage screen.
  /// ```dart
  /// router.push(HomePage.query(parameters));    // Push page to HomePage.
  /// router.replace(HomePage.query(parameters)); // Replace page to HomePage.
  /// ```
  static const query = _$HomePageQuery();

  Widget build(BuildContext context, PageRef ref) {
    // Describes the process of loading
    // and defining variables required for the page.
    final model = ref.model(CounterModel.document())..load();

    // Describes the structure of the page.
    return UniversalScaffold(
      appBar: UniversalAppBar(title: Text(l().appTitle)),
      body: UniversalColumn(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          const Text(
            "You have pushed the button this many times:",
            "${model.value?.counter.value ?? 0}",
            style: theme.text.displayMedium,
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          final value = model.value ?? const CounterModel();
            value.copyWith(counter: value.counter.increment(1)),
        tooltip: "Increment",
        child: const Icon(Icons.add),

The following line obtains the CounterModel object introduced earlier and reads it from the database. model.value contains the value read from the database, or null if there is no value in the database to begin with.

The view is rebuilt as appropriate when reads from the database are completed or updated.

final model = ref.model(CounterModel.document())..load();

Here, the counter value of CounterModel is directly retrieved and displayed.

  "${model.value?.counter.value ?? 0}",
  style: theme.text.displayMedium,

Counting up here.

If there is no value in the database, model.value will be null, in which case a new CounterModel is created.

The value is then saved as is by passing the CounterModel to be saved to model.save.

final value = model.value ?? const CounterModel();
  value.copyWith(counter: value.counter.increment(1)),

Data Persistence

As it is now, the count goes back to 0 when the app is restarted, just like the usual Flutter counter app.

So, let's create a local DB in the terminal and make the data persistent.

Open main.dart and replace modelAdapter with LocalModelAdapter instead of RuntimeModelAdapter.

// main.dart

final modelAdapter = LocalModelAdapter();
// final modelAdapter = RuntimeModelAdapter();

With just this, the counter value will be retained without going to zero even if the application is restarted.


In the next issue, I will try to use Firestore to maintain data even if the app is reinstalled. Enjoy!

The Masamune framework is available here, and I welcome issues and PullRequests!

If you have any further job requests, please contact me directly through my Twitter or website!

Offers app development and apps using Flutter and Unity. Includes information on music and videos created by the company. Distribution of images and video materials. We also accept orders for work.

GitHub Sponsors

Sponsors are always welcome. Thank you for your support!

Developed the katana/masamune framework, which has dramatically improved the overall efficiency of Flutter-based application development.