2
0
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

Flutterプロジェクト 自分用のテンプレートを公開した ~概要編~

Last updated at Posted at 2024-01-28

概要

約半年間、独学でFlutterを勉強しました。
自分なりに開発の型が見えてきたのでGitのリポジトリと記事を残したいと思います。
本記事はその概要編です。



flutterプロジェクトの開始時に作成されるサンプルアプリを置き換えたもので、基本的な仕様やUIは一緒ですが、カウントを1つ進めるたびにsharedPreferencesに現在のカウントが保存されるようになっています。


デザインパターン

こちらの大変わかりやすい記事を参考にさせていただきました!

私が現在作成している別のアプリはLayer-firstを採用しているのですが、コード量が増えて見通しが悪くなってきたため、今回公開したリポジトリでは勉強も兼ねてFeature-firstを採用しています。
規模に応じて使い分けることが使い分ける必要があるなと感じます。

おおまかなディレクトリ構成

lib
├ features/
│ ├ common_utils/
│ └ #feature-1
│ ├ application/
│ ├ domain/
│ └ infrastructure/
├ presentation/
└ main.dart

レイヤードアーキテクチャに関しては、前述の記事内で詳しく(かつ非常にわかりやすく!)説明がされているので、そちらの記事をご参照ください。
(私もまだ勉強中)

Application
Domain
Infreastructure
Presentation
の各層に対して、本記事とは別の個別記事で実装内容を書き残していこうと思いますが、main.dartとfeatures直下にあるcommon_utilsに関してはどの層にも属していないので本記事で触れようと思います。
(設定で変更できるようなので、presentation/router内に入れてもいいかもしれません)


main.dart

lib/main.dart
void main() {
  Widget app = ScreenUtilInit(
    designSize: const Size(baseWidth, baseHeight),
    minTextAdapt: true,
    splitScreenMode: true,
    builder: (context, child) {
      return const MyApp();
    },
  );

  runApp(ProviderScope(overrides: prdProviderOverrides, child: app));
}



ScreenUtils

ScreenUtilInit()でScreenUtilを使用する準備をしています。

このパッケージを利用するとデバイスのサイズに応じて高さ、幅、文字サイズなどの値を柔軟にリサイズしてくれるようになります。
(詳しくはpresentation層編の記事で後述します)

runApp

runAppにrepositoryをoverrideするためにProviderScope()を渡してあげています。
usecaseでrepotiroryを呼び出すとこは全て下記のような未実装エラーを投げるようになっているので、ここでoverrideしてあげないとrepository層は機能しません。

lib\features\counter\infrastructure\prd_counter_repository.dart
final counterRepositoryProvider = Provider<CounterRepository>(
  (_) => throw UnimplementedError(),
);

abstract interface class CounterRepository {
  //保存したcountを取得する
  Future<Count?> fetch();

  //countを保存する
  Future<void> saveCount(Count count);
}




下記のように環境に応じたrepositoryのOverriesを用意しておき、flavorなどを利用して条件分岐すれば簡単にrepositoryを切り替えることができます!

\lib\presentation\provider_overrides.dart
final prdProviderOverrides = [
  counterRepositoryProvider.overrideWithValue(PrdCounterRepository())
];

final mockProviderOverrides = [
  counterRepositoryProvider.overrideWithValue(MockCountRepository())
];

このmain.dartではprdProviderOverridesがProviderScopeのoverridesに渡されているので、counterRepositoryが呼ばれている個所では代わりにPrdCounterRepositoryが呼ばれることになります。


common_utils

各featuresで共通で使用するMixinなどの機能を配置しています。
MixinについてはApplication層編で触れているので、そちらをご覧ください。

page_state

Presentation層で使用するstateを提供するproviderを配置しています。
このテンプレートではusecaseの実行中かどうかをboolで保持しているoverlayLoadingProviderと現在アプリに適用されているthemeを保持しているthemeProviderがあります。

\lib\features\common_utils\page_state\theme_provider.dart
//アプリに適用されるthemeを保持するプロバイダー
final themeProvider =
    StateProvider<ThemeData>((ref) => AppTheme.lightThemeData());

themeはapp.dartで下記のように適用されるので、この値が別のthemeで更新されるとアプリのデザインが切り替わります。

\lib\presentation\router\app.dart
@override
  Widget build(BuildContext context, WidgetRef ref) {
    //router
    final router = ref.watch(goRouterProvider);
    //theme
    final ThemeData themeData = ref.read(themeProvider);

    return MaterialApp(
        //ページをタップするとfocusを外す
        home: GestureDetector(
            onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
            //loading画面をメインの画面に重ねる
            child: Stack(children: [
              MaterialApp.router(
                //以下の階層でthemeが適用される
                theme: themeData,
                routerConfig: router,
              ),
              const LoadingScreen(),
            ])));
  }

おわりに

概要編は以上になります。
随時投稿予定の以下の記事もよろしくお願いします!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0