2023-07-01

[Flutter]

Hello. I'm Masaru Hirose.

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

https://medium.comhttps://medium.com

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.

In the previous issue, I discussed "Easy Connection to Firestore".

https://medium.comhttps://medium.com

The ability to seamlessly migrate from local databases to Firestore is appealing.

I would like to start actual application development in the third session.

I will develop the simplest "Notepad application".

Basic Concept

The basic concept in creating an application is described below in advance.

CRUD(Create Read Update Delete)

This is the basic data handling in the application.

Create data, Retrieve data, Update data, Delete data. It is said that most applications can be created with just this process between database = application.

The Masamune framework provides features that make CRUD very easy to handle.

Documents and Collections

The Masamune framework employs a data structure of documents that collect multiple Firestore key/value pairs and collections of those documents.

For more information, please see below.

https://medium.comhttps://medium.com
https://firebase.google.comhttps://firebase.google.com
title

How to develop apps in Masamune

With the Masamune framework, you can create a CRUD application by simply building a data model that basically determines how data will be stored and a page that determines how the screen will be displayed.

You are free to think about the data model first based on how the application is used or the pages first based on the image of the application.

In this issue, I would like to consider this from the data model.

Creating a data model for Notepad

If I were to create a simple notepad, I would need the following data.

  • Title of memo
  • Text of memo
  • Date and time the memo was posted

Now let's actually make this work.

Open the previous Flutter project and enter the following command in a separate terminal

katana code watch

This ensures that file changes are monitored and auto-generated files are created immediately.

In addition, enter the Create Data Model command.

katana code collection memo

The following MemoModel will be created in models/memo.dart.

// memo.dart

/// Value for model.
@freezed
@formValue
@immutable
// TODO: Set the path for the collection.
@CollectionModelPath("memo")
class MemoModel with _$MemoModel {
  const factory MemoModel({
     // TODO: Set the data schema.
     
  }) = _MemoModel;
  const MemoModel._();

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

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

  /// Query for collection.
  ///
  /// ```dart
  /// appRef.model(MemoModel.collection());       // Get the collection.
  /// ref.model(MemoModel.collection())..load();  // Load the collection.
  /// ref.model(
  ///   MemoModel.collection().data.equal(
  ///     "data",
  ///   )
  /// )..load(); // Load the collection with filter.
  /// ```
  static const collection = _$MemoModelCollectionQuery();

  /// Query for form value.
  ///
  /// ```dart
  /// ref.form(MemoModel.form(MemoModel()));    // Get the form controller.
  /// ```
  static const form = _$MemoModelFormQuery();
}

Add the following parameters to this constructor part.

const factory MemoModel({
   // TODO: Set the data schema.
   required String title,
   required String text,
   @Default(ModelTimestamp()) ModelTimestamp createdAt,
}) = _MemoModel;

It corresponds to the memo title (title), memo body (text), and memo posting date and time (createdAt).

ModelTimestamp is a special class for handling dates and times within the Masamune framework.

When you save the file, katana code watch will automatically create memo.freed.dart, memo.g.dart, and memo.m.dart.

image block

This completes the data model definition.

Create memo list page

Next, a memo list page is created based on the memo data created above.

The following command creates a page.

katana code page memo_list

The following MemoListPage will be created in pages/memo_list.dart.

// memo_list.dart

@immutable
// TODO: Set the path for the page.
@PagePath("memo_list")
class MemoListPage extends PageScopedWidget {
  const MemoListPage({
    super.key,
    // TODO: Set parameters for the page.
    
  });

  // TODO: Set parameters for the page in the form [final String xxx].

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

  @override
  Widget build(BuildContext context, PageRef ref) {
    // Describes the process of loading
    // and defining variables required for the page.
    // TODO: Implement the variable loading process.
    

    // Describes the structure of the page.
    // TODO: Implement the view.
    return UniversalScaffold();
  }
}

First, the process for loading memo list data is described in the build method.

// memo_list.dart

@override
Widget build(BuildContext context, PageRef ref) {
  // Describes the process of loading
  // and defining variables required for the page.
  // TODO: Implement the variable loading process.
  final memoList = ref.model(MemoModel.collection())..load();

  // Describes the structure of the page.
  // TODO: Implement the view.
  return UniversalScaffold();
}

Arrange the contents of memoList using UniversalListView and ListTile.

(UniversalListView is a ListView available within the Masamune framework)

// memo_list.dart

// Describes the structure of the page.
// TODO: Implement the view.
return UniversalScaffold(
  appBar: UniversalAppBar(
    title: Text("Memo List"),
  ),
  body: UniversalListView(
    children: memoList.mapListenable((e) {
      return ListTile(
        title: Text(e.value?.title ?? ""),
        subtitle: Text(e.value?.createdAt.value.yyyyMMddHHmm() ?? ""),
      );
    }),
  ),
);

A memoList is converted to a ListTile with mapListenable.

By using mapListenable, the contents of the ListTile will be automatically updated when each element of the memoList is updated.

This completes the creation of the memo list screen.

Creating and debugging mock data

You may be currently connected to Firestore, but in order to check the status of the application data, disconnect it and display mock data instead.

Mock data can be handled by RuntimeModelAdapter.

Open main.dart and rewrite it as follows

// main.dart
import 'models/memo.dart';

~~~~~~

final modelAdapter = RuntimeModelAdapter(
  initialValue: [
    MemoModelInitialCollection(
      {
        for (var i = 0; i < 10; i++)
          generateCode(32, seed: i): MemoModel(
            title: "メモタイトル$i",
            text: "メモテキスト$i",
            createdAt: ModelTimestamp(DateTime.now().subtract(i.h)),
          )
      },
    )
  ],
);
// final modelAdapter = FirestoreModelAdapter(
//   options: DefaultFirebaseOptions.currentPlatform,
// );

Mock data can be specified for initialValue in the RuntimeModelAdapter.

Each mock data is defined by passing the automatically created ModelInitialCollection data, including pairs of IDs and Model objects.

The ID can be created in any way, but since a 32-digit string is used within the Masamune framework, a 32-digit random string is created using generateCode.

Also in main.dart, set the first page to MemoListPage.

// main.dart
import 'pages/memo_list.dart';

~~~~~~~~

final initialQuery = MemoListPage.query();
// final initialQuery = HomePage.query();

Now let's do a debug build in this state.

image block

You see a list of memos.

Creation of memo content page

Let's display the contents of the memo.

The following command creates a page.

katana code page memo_detail

A MemoDetailPage will be created in pages/memo_detail.dart.

// memo_detail.dart

@immutable
// TODO: Set the path for the page.
@PagePath("memo_detail")
class MemoDetailPage extends PageScopedWidget {
  const MemoDetailPage({
    super.key,
    // TODO: Set parameters for the page.
    
  });

  // TODO: Set parameters for the page in the form [final String xxx].

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

  @override
  Widget build(BuildContext context, PageRef ref) {
    // Describes the process of loading
    // and defining variables required for the page.
    // TODO: Implement the variable loading process.
    

    // Describes the structure of the page.
    // TODO: Implement the view.
    return UniversalScaffold();
  }
}

First, the memo ID to be displayed is required and is specified as an argument.

// memo_detail.dart

const MemoDetailPage({
  super.key,
  // TODO: Set parameters for the page.
  required this.memoId,
});

// TODO: Set parameters for the page in the form [final String xxx].
final String memoId;

Describe the process for loading the corresponding memo data based on the memo ID in the build method.

// memo_detail.dart

@override
Widget build(BuildContext context, PageRef ref) {
  // Describes the process of loading
  // and defining variables required for the page.
  // TODO: Implement the variable loading process.
  final memoData = ref.model(MemoModel.document(memoId))..load();

  // Describes the structure of the page.
  // TODO: Implement the view.
  return UniversalScaffold();
}

Create UI based on memoData.

// memo_detail.dart

return UniversalScaffold(
  appBar: UniversalAppBar(
    title: Text(memoData.value?.title ?? ""),
  ),
  body: UniversalListView(
    padding: 16.p,
    children: [
      Text("投稿日時:${memoData.value?.createdAt.value.yyyyMMddHHmm() ?? ""}"),
      16.sy,
      Text(memoData.value?.text ?? ""),
    ],
  ),
);

Allows the user to navigate to MemoDetailPage from each element of MemoListPage.

// memo_list.dart

// Describes the structure of the page.
// TODO: Implement the view.
return UniversalScaffold(
  appBar: UniversalAppBar(
    title: Text("Memo List"),
  ),
  body: UniversalListView(
    children: memoList.mapListenable((e) {
      return ListTile(
        title: Text(e.value?.title ?? ""),
        subtitle: Text(e.value?.createdAt.value.yyyyMMddHHmm() ?? ""),
        onTap: () {
          router.push(MemoDetailPage.query(memoId: e.uid));
        },
      );
    }),
  ),
);

The uid of each document is the ID set in the document itself, so it can be passed as memoId as it is.

Let's debug with this.

image block

A memo detail page has been created.

Conclusion

As you may have noticed during the creation process, I have written almost exclusively for the UI.

In this way, the Masamune framework can minimize implementation around data by combining automatic generation by command and automatic generation by katana code watch.

If the UI side also uses GithubCopilot, etc., it may be possible to further shorten the implementation by using suggestions. It's a good time!

In the next issue, I would like to proceed with the implementation of the memo data addition/update section. Please look forward to it!

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.
https://mathru.nethttps://mathru.net
title

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.
https://github.comhttps://github.com
title