Flutterでゆるいアプリ新規開発するぞってときにやること備忘録

2020/12/21に公開

なにこれ

  • なんか軽いアプリ作りたいなって思ったときにササッとFlutterで作る流れをまとめました
  • アーキテクチャは最低限のView-Logic-Domainが分かれてれば良いやろという方針です (あくまでゆるいアプリをササッと作ることを目的にしてるので)
  • 割と我流なのでちょくちょくブラッシュアップするかもです

前提

  • AndroidStudio,Xcodeインストール済
  • Flutter開発環境構築済
  • New Flutter AppのデフォルトのカウントAppが動作している状態

やっていき

コーヒーを淹れます ☕️

  • すべてのはじまり
  • まずはテンション上げてこ ↑↑

依存追加

  • 作るアプリに必要なもの入れればいいと思います
  • とりあえずView-Logicで分けるだけしたいのでRiverpodだけ入れます
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  flutter_riverpod: ^2.0.0-dev.9

フォルダ構成

  • ここも開発環境やプレイ人数に合わせて対応すればいいと思います
  • アプリによってはmain.dart単体もありそうですね (規模感や将来性との兼ね合い)
  • とりあえず自分は画面ごと+otherとかで分ければいいじゃんという気持ちです
- ~/lib
	- data
		- data_source
		- (repository)
	- ui
		- conponents
		- scene_1
			- (ui)
			- (logic)
		- scene_2
		...

テンプレ

共通部分

main.dart

  • 命名はmain.dartじゃなくてもいいですがとりあえず・・・
  • ProviderScopeで囲んでます
import 'package:my_app/scenes/home/HomeScene.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/all.dart';

void main() {
  runApp(ProviderScope(child: MainApp()));
}

class MainApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
        title: "appName",
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: FirstScene());
  }
}

各画面

FirstScene.dart

  • StatelessWidgetとして切り分けて画面管理させてます
  • 画面そのものはStatelessWidget、描画担当はConsumerWidgetを使ってます
import 'package:flutter/material.dart';

import 'FirstSceneWidget.dart';

class FirstScene extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("first")), body: FirstSceneWidget());
  }
}

FirstSceneWidget.dart

  • 描画担当です
  • ConsumerWidgetを継承させて、Riverpodwatch()でViewModelを監視するようにしてます
  • 思うがままにレイアウトを構築しましょう
  • ViewModelの処理を呼ぶテンプレを書きたかったのでGestureDetectorでタップ可能にしてます
import 'package:my_app/scenes/home/HomeViewModel.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class FirstSceneWidget extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    final _viewModel = ref.watch(firstProvider);
    return Center(
        child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        GestureDetector(
            onTap: () {
              _viewModel.cntUp();
            },
            child: Text(_viewModel.text,
                style: Theme.of(context).textTheme.headline4))
      ],
    ));
  }
}

FirstViewModel.dart

  • ChangeNotifierを継承させてるのでViewに対して値を通知する前提で実装してます
  • firstProviderとして定義しておくことで各面で呼ぶだけでインスタンス化できるようにします
  • 今回は簡単なカウントアップの処理を書いてます
  • Domainは?
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final firstProvider = ChangeNotifierProvider((ref) => FirstViewModel());

class FirstViewModel extends ChangeNotifier {
  var _cnt = 0;
  var text = "0";

  void cntUp() {
    _cnt++;
    text = _cnt.toString();
    notifyListeners();
  }
}

この後

  • これで一つの画面ができました
  • この後はRepositoryクラスを作ってFirstViewModelのようにproviderを経由させて呼んだり、はたまた同じように新しい画面を作ったりするのも良いと思います

おわり

  • 軽いアプリならさっと作れるやんって感じてくれたら嬉しいです
  • クラスを使い回すのが簡単なのでちゃんと制限をかけて実装していくことが大事ですね
    (基本ですがProviderで処理を切り分けた上で必要な値以外はprivateにしてアクセスできなくするなど)

Discussion