🔩

【git submodule】複数のFlutterプロジェクトの共通コードをサブモジュール化する方法

2022/05/19に公開

Flutter大学にはスマホアプリ、Webアプリ、管理画面、FlutterWorkと4つのプロジェクトがあり、全て同じFirebaseを参照しています。そのため、Domain層やRepository層と言われる部分のコードがほとんど同じです(UserクラスやUserRepositoryなど)。そして全部Flutterで作られています。

スマホアプリ web 管理画面 FlutterWork

つまり、同じようなFlutterのコードが全てのリポジトリにあり、こっちを更新したらあっちを更新しなければいけないという状態でした。

なので共通化しました。

共通部分のFlutterのコードを切り出して1つのpackageとし、git submoduleで連携することで、同じコードを使いまわせるようにした方法をこの記事に記したいと思います。

結論

先にどういう状態になったのか紹介します。

下図のファイル一覧にsalon_app_commonsというリンクが表示されているのがわかると思います。こちらがサブモジュールで、リンクをクリックすると共通コード(salon_app_commons)のリポジトリに飛びます。

アプリのリポジトリ

webのリポジトリ

共通部分(salon_app_commons)

2つのリポジトリはsalon_app_commonsをサブモジュールで持ち、同じコードを使いまわせます。以下みたいに呼び出して使うイメージです。

import 'package:salon_app_commons/salon_app_commons.dart';

final _userRepo = UserRepository();
final User user = await _userRepo.fetch();

例えばUserなんてWebでもアプリでも管理画面でも呼び出すので同じコードでいいですよね。それがライブラリとして呼び出せるようになった感じです。

具体的な手順

以下の4手順で行います。

  • ①共通化するコードの選定
  • ②共通コードをまとめたpackageのFlutterプロジェクトを作成
  • ③使いたいプロジェクトで②のrepoをgit submoduleで接続する
  • ④pubspec.yamlでサブモジュールをpathで呼び出せるようにする

この手順で1つずつ紹介していきます。

①共通化するコードの選定

元々のアプリとwebのリポジトリ構成は以下のようになっていました。

アプリ web

基本的にクリーンアーキテクチャー的なディレクトリ構成にしていて、

  • Presentation層...画面と画面ごとのロジック
  • Domain層...Entity(単一要素)がメイン
  • Repository層...Firebase等とのデータ通信

という役割に応じてフォルダ分けしていました。

アプリとwebで違うのは画面です。アプリはタブバー前提の画面構成ですが、webの方はマイページ中心の画面構成になっています。しかし、Domain層とRepository層は全くコードが同じなのでそこは共通化できるなと感じていました。ってか共通化しないと1度の更新で2度作業しなきゃいけないので手間ですよね。

ってことで、Domain層とRepository層を共通化するため、packageにして、その他便利クラスのUtilsやExtensionなども使いまわせそうなので含むことにしました。

最終的にはsalon_app_commonsのディレクトリ構成は以下のようになっています。

②共通コードをまとめたpackageのFlutterプロジェクトを作成

共通化できるコードは決めたので、アプリのコードをそのままコピーしてきて、packageを作りましょう。

AndroidStudioの場合は以下みたいに最初の選択肢が出ると思いますので、ここで、Flutter Packageを選択してください。

salon_app_commonsという名前のpackageで作った場合は、デフォルトでsalon_app_commons.dartができます。ソイツが全てのファイルをexportしてくれる役割としたいと思います。

以下のようにアプリのリポジトリのdomain、repositoryなどのフォルダもドラッグ&ドロップで突っ込みます。

salon_app_commons.dartのファイルでは、以下のようにそれぞれのファイルをexportして、このライブラリを使う側はsalon_app_commons.dartさえimportすれば全部のファイルを利用できるようにしておきます。

salon_app_commons.dart
library salon_app_commons;

export 'package:salon_app_commons/domain/user.dart';
export 'package:salon_app_commons/extensions/list_extension.dart';
export 'package:salon_app_commons/repository/user_repository.dart';
export 'package:salon_app_commons/utils/format_utils.dart';
export '../utils/url_utils_native.dart' if (dart.library.html) '../utils/url_utils_web.dart';

補足:ネイティブアプリとwebの分岐

お気づきの方もいるかもしれませんが、上記のsalon_app_commons.dartで以下のようなexportをしています。

export '../utils/url_utils_native.dart' if (dart.library.html) '../utils/url_utils_web.dart';

これは、ネイティブアプリとwebで別のファイルをexportするというテクニックです。
import 'dart:html';などFlutter Webでは使うけどアプリでimportするとビルドエラーになる物をうまくそれぞれのプラットフォームで動くように回避させるテクニックです。

以下を参考にしています。
https://github.com/flutter/flutter/issues/53005#issuecomment-621468722

また、Flutter大学の方は以下の松丸さんの勉強会の動画も参考になります。
https://flutteruniv.com/materials/lecture_videos/NHUyZeq3jhyIynv1tSDj

③使いたいプロジェクトで②のrepoをgit submoduleで接続する

上記で作ったpackageをgithubにアップします。(↓Flutter大学の人は見れます)

https://github.com/flutteruniv/salon_app_commons

これをsalon_appやsalon_app_webなどのリポジトリにsubmoduleとして導入します。
以下のコマンドです。

$ git submodule add git@github.com:flutteruniv/salon_app_commons.git

参考
https://qiita.com/sotarok/items/0d525e568a6088f6f6bb

次回以降のclone時

また、違うPCからや別の人がcloneしたい時は以下のコマンドで同期できます。

$ git submodule update --init --recursive

GithubAction対応

GithubActionを使ってる場合はcheckoutするときに以下のようにsubmodulesのoptionをつけてやると、submoduleも一緒にupdateしてくれます。GithubのPersonal Access TokenやSSH Keyは必要ですが。

      - name: Checkout
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
          submodules: recursive

参考
https://qiita.com/u_nation/items/8ff79a9831c05f3f1d1d

④pubspec.yamlでサブモジュールをpathで呼び出せるようにする

最後に、salon_app_commonsを使うプロジェクトのpubspec.yamlでpath指定をして、submoduleで取り込んだフォルダを参照できるようにして、flutter pub get してあげれば完了です。

pubspec.yaml
  go_router: ^3.0.6
  salon_app_commons:
    path: salon_app_commons

まとめ

以上です。

Flutter大学のGitHub Organizationに入っていれば、これらのコードは見れますので、Flutter大学生は直接見てみてください。また、新規の入学もお待ちしております!

https://flutteruniv.com

Flutter大学

Discussion