🧊
[Flutter] cubitのレシピ
Cubitのレシピ
を読んで、どういう手順で作ればいいのかわからない人向けに手順を紹介
UIとBlocとDataを切り分けることで、さまざまなメリットが得られる
今回は、
UI:ログイン画面
Bloc:Cubit
Data:Repository(httpでサーバーからデータを受け取る)
1.stateを作る
// 抽象クラスで、SiginInStateを作成する
abstract class SignInState {}
// 必要なstate(状態)を考えて、SignInStateを継承させる
// サインイン前の初期状態
class SignInInitialState extends SignInState {}
// サインイン成功した状態
class SignInSuccesState extends SignInState {}
// サインイン失敗状態、エラーメッセージ持たせれたら、いいかも
class SignInErrorState extends SignInState {
final String message;
SignInErrorState({required this.message});
}
// サインイン中の状態、データの読み込みに時間かかる場合のグルグルを表示するのに便利
class SignInLoadingState extends SignInState {}
2.cubitを作る
// CubitにSignInStateを持たせたClassを継承するSignInCubitを作成
class SignInCubit extends Cubit<SignInState> {
// サインイン前の初期状態を設定
SignInCubit() : super(SignInInitialState());
// 状態を変えるFutureのFunctionを設定
// 状態の変化を考えて順に設定していく
Future<void> signIn(String email, String password) async {
// 最初は、ローディング状態をUI側に伝える
emit(SignInLoadingState());
try {
// 3.で作成するデータのRepositoryをawaitを使って呼び出す。
// email,passwordをhttpでサーバーにPostして承認Tokenの文字列を返すようデータ側に伝える
await SignInRepository().signIn(email, password);
// 成功した時に成功した状態をUI側に伝える
emit(SignInSuccesState());
} catch (error) {
// SignInRepository().signIn(email, password)の中にある失敗の場合ここに入る
// 失敗した時の状態をUI側に伝える
emit(SignInErrorState(message: error.toString()));
rethrow;
}
}
}
3.repositoryを作る
// 何かとClassで定義しているの便利なので、RepositoryのClassを設定
class SignInRepository {
// httpでデータを取得するFutureを設定
Future<String> signIn(String email, String password) async {
// Postの応答を得るもオブジェクトの作成
final response = await http.post(
Uri.parse('http://localhost:8080/auth/sign-in'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'email': email,
'password': password,
}),
);
// SharedPrefsProviderでログイン状態などをアプリに一時的に保存できる。
final provider = SharedPrefsProvider();
// http通信のPostが成功した時の条件
if (response.statusCode == 200) {
provider.saveAuthToken(response.toString());
// 3. httpの応答の型はそれぞれ違うので、Stringになるように操作する
return response.toString();
} else {
// 成功以外の場合
throw Exception('Failed to Login.');
}
}
}
4.UIページの変更
長くなるので、重要な部分だけ抜き出して記載
三種の神器よりも有用な、三種のBloc~~~
1.BlocProvider:設定した以下のWidget TreeでCubitが利用可能になるので必須
2.BlocListener:BlocProviderで設定したCubitでemitされたstateを受け取り、状態によってファンクションや値の代入などを行う時に使う
3.BlocBuilder:BlocProviderで設定したCubitでemitされたstateを受け取り、状態によってWidgetを返す時などに使う
...
Widget build(BuildContext context) {
// BlocProviderを設定して、以下のWidget Treeで使いたいCubitを設定することができる
return BlocProvider(
// 使いたいcubitを設定
create: (context) => SignInCubit(),
// BlocListenerを設定、BlocListenerには使うcubitとStateを設定する
// functionや値を代入する時などにListenerを使う、
// Widgetを使うときはWidgetBuilderを使う
// stateにはabstract classのStateを設定する
child: BlocListener<SignInCubit, SignInState>(
listener: (context, state) {
// state毎の条件を設定
// 成功した時のStateの時
if (state is SignInSuccesState) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const HomePage(),
),
);
}
// 失敗した時のStateの時
if (state is SignInErrorState) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Email or Password is wrong"),
),
);
}
},
...
// BlocBuilderを設定してWidgetを返す
child: BlocBuilder<SignInCubit, SignInState>(
builder: ((context, state) {
// 読み込み中の時
if (state is SignInLoadingState) {
return const SizedBox(
height: 30,
width: 30,
child: CircularProgressIndicator(),
);
}
return const Text('Sign In');
}),
),
...
Discussion