#BLoC
プレゼンテーションをビジネスロジックから分離できる部品。
BLoCを取り入れたアーキテクチャをBLoCアーキテクチャと呼ぶ。
#プレゼンテーションとビジネスロジックって?
アプリを作っているとプレゼンテーション層、ビジネスロジック層、インテグレーション層と恐ろしい呪文が出てくる。
これらは具体的にはアプリの機能や責務に応じて分割された構造(アーキテクチャ)で、それぞれに担う役割があります。
- プレゼンテーション層:クライアントに対する入出力(リクエスト・レスポンス)に関わる処理をする。
- ビジネスロジック層:プレゼンテーション層から受け取った情報を処理する。
- インテグレーション層:外部のデータベース操作、他システムとの連携を実行する。
#プレゼンテーションとビジネスロジックを分けて何が得なのか?
テストがしやすくなる。
再利用性が上がる。
#BLoCを実現するパッケージ
FlutterにはこのBLoCを容易に実現できるパッケージが公開されています。
https://pub.dev/packages/bloc
上記URLのパッケージを利用して今回から二回に分けてBLoCの実装方法を紹介します。
今回はCubitの実装方法を紹介します。
#Cubit
Cubitはメソッドを使用して新しいState(状態)を管理できるものです。
状態管理の対象が1つの場合はBLoCではなくCubitを使用した方が良いと思われます。
このような書き方をするとBLoCとCubitは別物に感じてしまいますが、CubitはBLoCのサブセットです。
つまり、一部でありBLoCアーキテクチャを実現するための一つの手段とも言えます。
#Cubitによる状態管理の手順
- Cubitを作る(Stateの型を指定したCubitを継承したクラスを作る)
- 初期Stateを作る
- Stateを変化させるメソッドを指定(具体的な処理にはemit()メソッドを記述して、このemit()メソッドの引数にstateの変化を記述する)
- 実際に指定したメソッドを呼び出して、stateを変化させる。
以下、公式の実際のコードです。
class CounterCubit extends Cubit<int> {//Stateの型(int)を指定
//初期State(状態)を記述。
CounterCubit() : super(0);
//Stateを変化させるメソッド指定
void increment() => emit(state + 1);
}
ちなみにStateとは状態です。
具体的に何の状態なのかというと、Widgetの状態です。
Stateを利用することで、Widgetの状態を変化させているという事ですね。
#具体的なソースコード
今回はプロジェクトを立ち上げた際に自動的に作成されるカウントアップを改造してCubitを実装します。
import 'package:flutter/material.dart';
import 'package:cubit/cubit.dart';
import 'package:flutter_cubit/flutter_cubit.dart';
void main() {
runApp(MyApp());
}
class CounterCubit extends Cubit<int> {
CounterCubit(): super(0);
void increment() => emit(state+1);
}
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
title:'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: CubitProvider(
create: (context) => CounterCubit(),
child: Scaffold(
appBar: AppBar(
title: Text('increment'),
),
body: CubitBuilder<CounterCubit, int>(
builder: (_ , state) =>(
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('You have pushed the button this many times:'),
Text('$state',style:Theme.of(context).textTheme.headline4)
],
)
)
),
),
floatingActionButton: Builder(
builder: (BuildContext context) => FloatingActionButton(
onPressed: (){
context.cubit<CounterCubit>().increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
)
)
);
}
}
CubitProviderは、createで指定したCubit(今回だとCounterCubit()インスタンス)をCubitProviderよりも下のWidget(クラス)に渡す機能を提供します。SwiftやKotlinをやっているとDI(依存性の注入)という言葉を聞いたことがあると思いますが、あれです。
DIは基本的に事前に使用したいクラスをインスタンス化しといて、別のクラスで使用したいときに使われる手法と思いますがCubitProviderも同じです。最初にWidgetツリーの上位でインスタンス化しといて、下位Widget(クラス)で使用していると思っていただければ大丈夫だと思います。
CubitBuilderは、Cubitとbuilderを必要とするWidgetです。CubitBuilderで、Cubit(今回はCounterCubit)とStateの型を指定してあげます。そして、builderでstateを指定して、下位Widgetをリビルドしています。
Builder()は、contextを下げるために使用しました。仮にこの記載がない場合、onPressed: (){context.cubit<CounterCubit>().increment()}
で使用されるcontextはWidgetツリー最上位のMyAppより上、つまりビルドされる前のものになってしまいます。
ここで使用したいCounterCubit()インスタンスを供給するのはCubitProviderで、CubitProviderはMyAppより下に存在しています。
なので、Builder()でcontextを下げてあげないと、上にCubitProvider(CounterCubit()のincrement())なんてねえぞ!と怒られちゃいます。
#まとめ
簡単にCubitが実装できた!
と言いたいところですが、全然そんなことはなかったです・・・。
特にcontextの部分はどうしてエラーが出てるのか分からなすぎて一日消えました。
この記事の内容も正しいか正直不安です。(間違っている点があればコメントで教えてください)
とりあえず次はBLoCかマテリアルデザインについて書きたいと思います。