❤️
【Flutter】state_notifier + flutter_hooks + freezedでお気に入りボタンを作る
freezedの使用手順メモ。
押すと色が変わるIconButton(お気に入りボタンっぽいもの)を作ってみる。
この方法で作れば、いろんなところから状態を呼び出せるので、複数ページでお気に入りボタンを表示したい時とかに便利!
1. こんなんができるよ
2. 導入するPackage
- hooks_riverpod
- flutter_hooks
- freezed
- build_runner
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
hooks_riverpod: ^1.0.0-dev.11
flutter_hooks: ^0.18.0
freezed: ^0.15.0+1
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.1.4
3. UIを作る
個人的に先に見た目を作っておくと進めやすいと思う。
今回は、ダミーデータとして'title'と'isFavo'というkeyを持ったマップのリストを作成。
class FavoWidget extends HookConsumerWidget {
List<Contents> dammyList = dammyData
.map((e) => Contents(title: e['title'], isFavo: e['isFavo']))
.toList();
Widget build(BuildContext context, WidgetRef ref) {
final size = MediaQuery.of(context).size;
return Scaffold(
body: SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
itemCount: 3, //あとで変更
itemBuilder: (BuildContext context, int index) {
return Card(
child: SizedBox(
height: size.height * 0.1,
child: Row(children: [
SizedBox(
width: size.width * 0.8,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text('ダミー'), //あとで変更
),
),
IconButton(
onPressed: () {
//あとで入れる
},
icon: Icon(Icons.favorite,
color: Colors.grey), //あとで変更
),
]),
),
);
}),
),
);
}
}
4. stateのクラスを作る
状態を持たせたい変数(など)の定義をする。
import 'package:favo_statenotifier_freezed/entities/contents.dart';
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'favo_state.freezed.dart';
class FavoState with _$FavoState {
const factory FavoState(
{([]) List<Contents> data}) = _FavoState;
}
6行目”part 'favo_state.freezed.dart';” を入れておく。
この段階ではエラーが出たままでOK。
5. favo_state.freezed.dartを生成するためのコマンドを実行する
コマンドラインで以下を実行
flutter pub run build_runner build --delete-conflicting-outputs
うまくいけば、4.のエラーが消えてfavo_state.freezed.dartが生成される。
6. 状態を変化させるためのメソッドを作る
4で作った状態を変化させるメソッドを作る。
今回は状態の初期化(initState)と変更(favoriteChange)の2つ。
import 'package:favo_statenotifier_freezed/entities/contents.dart';
import 'package:favo_statenotifier_freezed/pages/favo_state.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class FavoController extends StateNotifier<FavoState> {
FavoController() : super(const FavoState());
void initState ({required List<Contents> data}) {
state = state.copyWith(data: data);
}
void favoriteChange ({required List<Contents> data, required int index}) {
data[index].isFavo = !data[index].isFavo;
state = state.copyWith(data: data);
}
}
Listの中身を変化させた上で、stateのコンストラクタに代入する。
7. プロバイダを作る
4、6の状態とメソッドをUI部分で呼び出すためのプロバイダを作る。
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'favo_controller.dart';
import 'favo_state.dart';
final favoProvider =
StateNotifierProvider<FavoController, FavoState>(
(ref) => FavoController());
8. UI部分のコードで状態とメソッドを呼び出すように修正する
- 初期化
class FavoWidget extends HookConsumerWidget {
List<Contents> dammyList = dammyData
.map((e) => Contents(title: e['title'], isFavo: e['isFavo']))
.toList();
Widget build(BuildContext context, WidgetRef ref) {
final size = MediaQuery.of(context).size;
// ここ。4で作った状態を初期化する。
useEffect(() {
ref.read(favoProvider.notifier).initState(data: dammyList);
}, const []);
final _isFavoList = ref.watch(favoProvider).data;
//続く
- ListViewの中身を変更
return Scaffold(
body: SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
itemCount: dammyList.length, //ここ
itemBuilder: (BuildContext context, int index) {
return Card(
child: SizedBox(
height: size.height * 0.1,
child: Row(children: [
SizedBox(
width: size.width * 0.8,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(_isFavoList[index].title.toString()), //ここ
),
),
IconButton(
onPressed: () {
//ここから
ref
.read(favoProvider.notifier)
.favoriteChange(data: _isFavoList, index: index);
//ここまで
},
icon: Icon(Icons.favorite,
//ここから
color: _isFavoList[index].isFavo == true
? Colors.red
: Colors.grey),
//ここまで
),
]),
),
);
}),
),
);
Discussion