Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Flutter Hooks, sometimes Jetpack Compose
Search
Daichi Furiya (Wasabeef)
June 22, 2021
Programming
1.9k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Flutter Hooks, sometimes Jetpack Compose
Flutter Hooks, sometimes Jetpack Compose
Daichi Furiya (Wasabeef)
June 22, 2021
More Decks by Daichi Furiya (Wasabeef)
See All by Daichi Furiya (Wasabeef)
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
6
2.9k
About Flutter Architecture
wasabeef
1
340
2023 Flutter/Dart Summary
wasabeef
0
140
I/O Extended 2023 - Dart と Flutter の新機能
wasabeef
0
250
I/O Extended 2023 - Flutter 活用事例
wasabeef
10
3.1k
What it Takes to be a Flutter Developer
wasabeef
0
270
FlutterKaigi 2022 Keynote
wasabeef
1
760
Flutter Hooks を使ったアプリ開発 / App Development with the Flutter Hooks
wasabeef
2
1.6k
Flutter 2021 の振り返りと今後のアプリ開発に向けて / Looking back on Flutter 2021 and for future app development.
wasabeef
4
2.3k
Other Decks in Programming
See All in Programming
Turning Architecture into Unit Tests in the AI Era (NSSpain XIV)
steliosf
PRO
1
120
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
430
速く作れる。その次は、速く確かめられる開発へ 〜AIネイティブ開発を支える、Shift Down〜 / Can build fast. Next, moving to development where we can verify fast.
rkaga
8
5.2k
JRuby: Past, Present, and Future
headius
0
210
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
280
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
350
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
560
SONY CISC-NEWS NWS-1750 + NWB-225 フレームバッファの NetBSD/news68k ドライバ実装 / OSC2026Hiroshima
tsutsui
0
160
標準パッケージに uuid が追加された 背景から見る Go らしい意思決定 / go_127_uuid_decision
convto
5
9.2k
世界の中心で、AI(App Intents)をさけぶ ー App Intents中心設計の実践ガイド
touyou
0
730
Go × SIMDで高速化するベクトル検索 ~ルーフラインモデルでSIMDが効く境界を探れ! ~
po3rin
1
5.1k
AI が書く Go コードの品質を劇的に向上させる Linter: “declscope”
mpyw
0
450
Featured
See All Featured
Raft: Consensus for Rubyists
vanstee
142
7.7k
Building Adaptive Systems
keathley
44
3.2k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
330
Bash Introduction
62gerente
615
220k
Balancing Empowerment & Direction
lara
6
1.3k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
750
Un-Boring Meetings
codingconduct
0
430
The agentic SEO stack - context over prompts
schlessera
0
950
Facilitating Awesome Meetings
lara
57
7.2k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
850
Transcript
Flutter 1 Flutter Hooksɺ ɹɹɹ࣌ʑ Jetpack Compose Daichi Furiya @wasabeef_jp
Target audience Hooks ΛΒͳ͍ਓ React Hook ͬͯΔਓ 2 ͜ΕΛظʹ Hooks
ʹ৮Εͯɺ Flutter Hooks/React Hook ͱ Jetpack Compose ͷεςʔ τཧΛཧղͷҰาΛਐΉ Flutter Hooks ͱ React Hook ɺ΄΅ಉ͡ͳͷͰม ͭͭ͠ Jetpack Compose ͷ εςʔτཧʹ৮ΕΔ Jetpack Compose ͱ Flutter Hooks/React Hooks Λม Ͱ͖ΔΑ͏ʹ͢Δ Jetpack Compose ͬͯΔਓ
Target audience Hooks ΛΒͳ͍ਓ React Hook ͬͯΔਓ 3 ͜ΕΛظʹ Hooks
ʹ৮Εͯɺ Flutter Hooks/React Hook ͱ Jetpack Compose ͷεςʔ τཧΛཧղͷҰาΛਐΉ Flutter Hooks ͱ React Hook ɺ΄΅ಉ͡ͳͷͰม ͭͭ͠ Jetpack Compose ͷ εςʔτཧʹ৮ΕΔ Jetpack Compose ͱ Flutter Hooks/React Hooks Λม Ͱ͖ΔΑ͏ʹ͢Δ Jetpack Compose ͬͯΔਓ
Target audience Hooks ΛΒͳ͍ਓ React Hook ͬͯΔਓ 4 ͜ΕΛظʹ Hooks
ʹ৮Εͯɺ Flutter Hooks/React Hook ͱ Jetpack Compose ͷεςʔ τཧΛཧղͷҰาΛਐΉ Flutter Hooks ͱ React Hook ɺ΄΅ಉ͡ͳͷͰม ͭͭ͠ Jetpack Compose ͷ εςʔτཧʹ৮ΕΔ Jetpack Compose ͱ Flutter Hooks/React Hooks Λม Ͱ͖ΔΑ͏ʹ͢Δ Jetpack Compose ͬͯΔਓ
Target audience Hooks ΛΒͳ͍ਓ React Hook ͬͯΔਓ 5 ͜ΕΛظʹ Hooks
ʹ৮Εͯɺ Flutter Hooks/React Hook ͱ Jetpack Compose ͷεςʔ τཧΛཧղͷҰาΛਐΉ Flutter Hooks ͱ React Hook ɺ΄΅ಉ͡ͳͷͰม ͭͭ͠ Jetpack Compose ͷ εςʔτཧʹ৮ΕΔ Jetpack Compose ͱ Flutter Hooks/React Hooks Λม Ͱ͖ΔΑ͏ʹ͢Δ Jetpack Compose ͬͯΔਓ
6 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04
Use Cases 05 Conclusion
Flutter Hooks Flutter Hooks ͱʁ ϝϦοτ 7 React Hooks ͷ
Flutter ൛Ͱจ ๏͍ํ΄΅Ұॹɻ εςʔτͷॳظԽʢinitStateʣഁغ (dispose) ΛΟδΣοτͱ͕Ͱ͖Δɻ ୯७ʹΫϥεͷఆٛΛݮΒ͢γϯλοΫεγϡ ΨʔతͳԸܙ͋Δɻ
Flutter Hooks Flutter Hooks ͱʁ ϝϦοτ 8 React Hooks ͷ
Flutter ൛Ͱจ ๏͍ํ΄΅Ұॹɻ εςʔτͷॳظԽʢinitStateʣഁغ (dispose) ΛΟδΣοτͱ͕Ͱ͖Δɻ ୯७ʹΫϥεͷఆٛΛݮΒ͢γϯλοΫεγϡ ΨʔతͳԸܙ͋Δɻ
Flutter Hooks Flutter Hooks ͱʁ ϝϦοτ 9 React Hooks ͷ
Flutter ൛Ͱจ ๏͍ํ΄΅Ұॹɻ εςʔτͷॳظԽʢinitStateʣഁغ (dispose) ΛΟδΣοτͱ͕Ͱ͖Δɻ ୯७ʹΫϥεͷఆٛΛݮΒ͢γϯλοΫεγϡ ΨʔతͳԸܙ͋Δɻ
10 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04
Use Cases 05 Conclusion
11 StatelessWidget, StatefulWidget StatelessWidget StatefulWidget ΦϒδΣΫτੜ࣌ʹ༩͑ΒΕͨ ใͱ BuildContext Ҏ֎ʹґଘ͠ͳ ͍ΟδΣοτͰ͢ɻIconɺ
IconButton Text ͳͲ͕ StatelessWidget ͷαϒΫϥεͰ͢ɻ γεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹ มԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛ ͬͨΟδΣοτͰ͢ɻImageɺ TextFormField Hero ͳͲ͕ StatefulWidget ͷαϒΫϥεͰ͢ɻ
12 StatelessWidget, StatefulWidget StatelessWidget StatefulWidget ΦϒδΣΫτੜ࣌ʹ༩͑ΒΕͨ ใͱ BuildContext Ҏ֎ʹґଘ͠ͳ ͍ΟδΣοτͰ͢ɻIconɺ
IconButton Text ͳͲ͕ StatelessWidget ͷαϒΫϥεͰ͢ɻ γεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹ มԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛ ͬͨΟδΣοτͰ͢ɻImageɺ TextFormField Hero ͳͲ͕ StatefulWidget ͷαϒΫϥεͰ͢ɻ
13 StatelessWidget, StatefulWidget StatelessWidget StatefulWidget ΦϒδΣΫτੜ࣌ʹ༩͑ΒΕͨ ใͱ BuildContext Ҏ֎ʹґଘ͠ͳ ͍ΟδΣοτͰ͢ɻIconɺ
IconButton Text ͳͲ͕ StatelessWidget ͷαϒΫϥεͰ͢ɻ γεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹ มԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛ ͬͨΟδΣοτͰ͢ɻImageɺ TextFormField Hero ͳͲ͕ StatefulWidget ͷαϒΫϥεͰ͢ɻ
14 StatelessWidget, StatefulWidget class Counter extends StatefulWidget { @override State
createState() "=> _CountState(); } class _CountState extends State<CountPage> { int count = 0; void setCount(int value) { setState(() "=> count = value); } @override Widget build(BuildContext context) { return TextButton( child: Text("a is $count"), onPressed: () "=> setCount(count + 1), ); } } StatelessWidget class SimpleLabel extends StatelessWidget { const SimpleLabel({required this.label}); final String label; @override Widget build(BuildContext context) { return Text( label, style: const TextStyle( decoration: TextDecoration.underline, ), ); } } StatefulWidget ϘλϯΛԡ͢ຖʹ count ͕૿͑ɺςΩετ͕Մม͢ΔΟδΣοτˢ ↑ ༩͑ΒΕͨใͷΈͰෆมͳΟδΣοτ
15 StatelessWidget, StatefulWidget class Counter extends StatefulWidget { @override State
createState() "=> _CountState(); } class _CountState extends State<CountPage> { int count = 0; void setCount(int value) { setState(() "=> count = value); } @override Widget build(BuildContext context) { return TextButton( child: Text("a is $count"), onPressed: () "=> setCount(count + 1), ); } } StatelessWidget class SimpleLabel extends StatelessWidget { const SimpleLabel({required this.label}); final String label; @override Widget build(BuildContext context) { return Text( label, style: const TextStyle( decoration: TextDecoration.underline, ), ); } } StatefulWidget ϘλϯΛԡ͢ຖʹ count ͕૿͑ɺςΩετ͕Մม͢ΔΟδΣοτˢ ↑ ༩͑ΒΕͨใͷΈͰෆมͳΟδΣοτ
16 StatelessWidget, StatefulWidget class Counter extends StatefulWidget { @override State
createState() "=> _CountState(); } class _CountState extends State<CountPage> { int count = 0; void setCount(int value) { setState(() "=> count = value); } @override Widget build(BuildContext context) { return TextButton( child: Text("a is $count"), onPressed: () "=> setCount(count + 1), ); } } StatelessWidget class SimpleLabel extends StatelessWidget { const SimpleLabel({required this.label}); final String label; @override Widget build(BuildContext context) { return Text( label, style: const TextStyle( decoration: TextDecoration.underline, ), ); } } StatefulWidget ϘλϯΛԡ͢ຖʹ count ͕૿͑ɺςΩετ͕Մม͢ΔΟδΣοτˢ ↑ ༩͑ΒΕͨใͷΈͰෆมͳΟδΣοτ
17 class CountPage extends StatefulWidget { @override State createState() "=>
_CountState(); } class _CountState extends State<CountPage> { int count = 0; @override void initState() { ""/** ॳظԽ **/ } @override void dispose() { ""/** ഁغ **/ } void setCount(int value) "=> setState(() "=> count = value); @override Widget build(BuildContext context) { return TextButton( child: Text("Count is $count"), onPressed: () "=> setCount(count + 1), ); } } StatefulWidget Λ͏ʹجຊతʹೋͭ ͷΫϥεΛ࡞Δඞཁ͕͋Γ·͢ɻ StatefulWidget Λܧঝͨ͠ CountPage ͱ State<T extends StatefulWidget> Λܧঝ ͨ͠ _CountState Ͱ͢ɻ initState ͰॳظԽɺdispose Ͱഁغͷॲ ཧΛॻ͘͜ͱ͕Ͱ͖·͢ɻ ·ͨ setState ΛݺͿ͜ͱͰঢ়ଶ͕มԽ͠ ͨ͜ͱΛ௨ͯ͠ϦϏϧυ͕Γ·͢ɻ StatelessWidget, StatefulWidget
18 class CountPage extends HookWidget { @override Widget build(BuildContext context)
{ final count = useState(0); return TextButton( child: Text("Count: ${count.value}"), onPressed: () "=> count.value"++, ); } } StatelessWidget, StatefulWidget γϯϓϧʹ useState Λ͏ͱॻ͘ͱ͜Ε ͘Β͍Ͱલͷϖʔδͱಉ͡ίʔυͷಈ࡞ Λ࣮ݱ͢Δ͜ͱ͕Ͱ͖·͢ɻuseState ͷ ׂͳͲޙड़͍͖ͯ͠·͢ɻ
19 class HomePage extends HookWidget { const HomePage({Key? key}) :
super(key: key); @override Widget build(BuildContext context) { final enabled = useState(false); return Column( children: [ TextFormField( onChanged: (value) "=> enabled.value = value.isNotEmpty, ), ElevatedButton( onPressed: enabled.value ? () { ""/** Submit **/ } : null, child: Text('Submit'), ), ], ); } } StatelessWidget, StatefulWidget Flutter Hooks ͷॳΊͯͷαϯϓϧΛ୳͠ ͯΈΔͱઌ΄ͲΑ͏ʹΧϯλʔͷίʔ υΛΑ͘ݟ͔͚·͕͢ɺ࣮ࡍʹॳݟͰ ͦΕ͚ͩΈͯϐϯͱ͜ͳ͔ͬͨͷͰɺ ͜͜ͰϑΥʔϜʹ͕͋Δ͔Ͳ͏͔Ͱ Ϙλϯͷ༗ޮແޮΛΓସ͍͑ͯ·͢ɻ
20 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04
Use Cases 05 Conclusion
Flutter Hooks ͷϝιου HookWidget Λܧঝͨ͠ build ͷதͰ ݺͳ͍ͱ͍͚ͳ͍ useContext BuildContext
ΛऔಘͰ͖Δ 21
@Composable fun SampleName() { val context = LocalContext.current Text( text
= "${context.getString(R.string.name)}" ) } 22 useContext class SampleName extends HookWidget { @override Widget build(BuildContext context) { final l10n = useL10n(); return Text(l10n.name); } } L10n useL10n() { final context = useContext(); return L10n.of(context)!; } Flutter Hooks Jetpack Compose HookWidget Λܧঝ͢Δඞཁ͕͋Δɻ Hooks Λͬͨϝιουʹৗʹ use Ͱ࢝·Δ͜ͱ ͕ϧʔϧͱͳ͍ͬͯΔɻ
@Composable fun SampleName() { val context = LocalContext.current Text( text
= "${context.getString(R.string.name)}" ) } 23 useContext class SampleName extends HookWidget { @override Widget build(BuildContext context) { final l10n = useL10n(); return Text(l10n.name); } } L10n useL10n() { final context = useContext(); return L10n.of(context)!; } Flutter Hooks Jetpack Compose HookWidget Λܧঝ͢Δඞཁ͕͋Δɻ Hooks Λͬͨϝιουʹৗʹ use Ͱ࢝·Δ͜ͱ ͕ϧʔϧͱͳ͍ͬͯΔɻ
@Composable fun SampleName() { val context = LocalContext.current Text( text
= "${context.getString(R.string.name)}" ) } 24 useContext class SampleName extends HookWidget { @override Widget build(BuildContext context) { final l10n = useL10n(); return Text(l10n.name); } } L10n useL10n() { final context = useContext(); return L10n.of(context)!; } Flutter Hooks Jetpack Compose HookWidget Λܧঝ͢Δඞཁ͕͋Δɻ Hooks Λͬͨϝιουʹৗʹ use Ͱ࢝·Δ͜ͱ ͕ϧʔϧͱͳ͍ͬͯΔɻ
StatelessWidget ͰԿ͔͠Βͷঢ়ଶ ཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useState Λ͏໘Ͱ͢ʢViewModel ૬Ͱ อ࣋͢Δ΄ͲͰͳ͍ͷͳͲʣ useState final count
= useState(0); count.value = 3; େମ֮͑Δͷ͜Ε͚ͩ StatefulWidget ΛΘͳ͍ ؆୯ 25
useState final count = useState(0); count.value = 3; େମ֮͑Δͷ͜Ε͚ͩ StatefulWidget
ΛΘͳ͍ ؆୯ 26 StatelessWidget ͰԿ͔͠Βͷঢ়ଶ ཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useState Λ͏໘Ͱ͢ʢViewModel ૬Ͱ อ࣋͢Δ΄ͲͰͳ͍ͷͳͲʣ
useState final count = useState(0); count.value = 3; େମ֮͑Δͷ͜Ε͚ͩ StatefulWidget
ΛΘͳ͍ ؆୯ 27 StatelessWidget ͰԿ͔͠Βͷঢ়ଶ ཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useState Λ͏໘Ͱ͢ʢViewModel ૬Ͱ อ࣋͢Δ΄ͲͰͳ͍ͷͳͲʣ
@Composable fun Count() { val count = remember { mutableStateOf(0)
} Button(onClick = { count.value"++ }) { Text("Count: ${count.value}") } } 28 useState class CountPage extends HookWidget { @override Widget build(BuildContext context) { final count = useState(0); return TextButton( onPressed: () { count.value"++; }, child: Text("Count: ${count.value}"), ); } } Flutter Hooks Jetpack Compose remember composable Λར༻ͯ͠ঢ়ଶͷอ ࣋Λߦ͏͜ͱ͕Ͱ͖Δ final state = useState(initialValue); state.value = newValue; ͜Ε͚ͩ
@Composable fun Count() { val count = remember { mutableStateOf(0)
} Button(onClick = { count.value"++ }) { Text("Count: ${count.value}") } } 29 useState class CountPage extends HookWidget { @override Widget build(BuildContext context) { final count = useState(0); return TextButton( onPressed: () { count.value"++; }, child: Text("Count: ${count.value}"), ); } } Flutter Hooks Jetpack Compose remember composable Λར༻ͯ͠ঢ়ଶͷอ ࣋Λߦ͏͜ͱ͕Ͱ͖Δ final state = useState(initialValue); state.value = newValue; ͜Ε͚ͩ
@Composable fun Count() { val (count, setCount) = remember {
mutableStateOf(0) } Button(onClick = { setCount(count + 1) }) { Text("Count: ${count}") } } 30 useState class CountPage extends HookWidget { @override Widget build(BuildContext context) { final count = useState(0); return TextButton( onPressed: () { count.value"++; }, child: Text("Count: ${count.value}"), ); } } Flutter Hooks Jetpack Compose Jetpack Compose value ͱ setter Λ࣋ͭ tuple Λੜ͢Δ͜ͱͰ͖·͢ʢReact Ά͍ʣ final state = useState(initialValue); state.value = newValue; ͜Ε͚ͩ
@Composable fun Count() { val (count, setCount) = remember {
mutableStateOf(0) } Button(onClick = { setCount(count + 1) }) { Text("Count: ${count}") } } 31 useState class CountPage extends HookWidget { @override Widget build(BuildContext context) { final count = useState(0); return TextButton( onPressed: () { count.value"++; }, child: Text("Count: ${count.value}"), ); } } Flutter Hooks Jetpack Compose remember composable Λར༻ͯ͠ঢ়ଶͷอ ࣋Λߦ͏͜ͱ͕Ͱ͖Δ final state = useState(initialValue); state.value = newValue; ͜Ε͚ͩ
؆୯ͳϥΠϑαΠΫϧॲཧ͕ඞཁ ͩͬͨ߹ʹ useEffect Λ͍· ͢ɻΟδΣοτੜ࣌ʹॳظԽ͠ ͍ͨ߹ͳͲɻ useEffect ॳظԽഁغͷॲཧ͕ඞཁͩͬͨΒ 32
DisposableEffect(id) { val subscription = source.subscribe(id) onDispose { "// ഁغ͞ΕΔ࣌ʹ࣮ߦ(ActivityഁغͳͲ)
subscription.unsubscribe(id) } } 33 useEffect useEffect(() "=> { final subscription = source.subscribe(id); return () "=> { "// ഁغ͞ΕΔ࣌ʹ࣮ߦ subscription.unsubscribe(id); }; }, [id]); Flutter Hooks Jetpack Compose
DisposableEffect(id) { val subscription = source.subscribe(id) onDispose { "// ഁغ͞ΕΔ࣌ʹ࣮ߦ(ActivityഁغͳͲ)
subscription.unsubscribe(id) } } 34 useEffect useEffect(() "=> { final subscription = source.subscribe(id); return () "=> { "// ഁغ͞ΕΔ࣌ʹ࣮ߦ subscription.unsubscribe(id); }; }, [id]); Flutter Hooks Jetpack Compose
DisposableEffect(id) { val subscription = source.subscribe(id) onDispose { "// ഁغ͞ΕΔ࣌ʹ࣮ߦ(ActivityഁغͳͲ)
subscription.unsubscribe(id) } } 35 useEffect useEffect(() "=> { final subscription = source.subscribe(id); return () "=> { "// ഁغ͞ΕΔ࣌ʹ࣮ߦ subscription.unsubscribe(id); }; }, [id]); Flutter Hooks Jetpack Compose
LaunchedEffect(id) { "// ID ͕มߋ͞ΕΔͨͼ } SideEffect { "// ຖճ
} LaunchedEffect(Unit) { "// ࠷ॳͷҰճ } 36 useEffect useEffect(() "=> { "// ID ͕มߋ͞ΕΔͨͼ }, [id]); useEffect(() "=> { "// ຖճ }); useEffect(() "=> { "// ࠷ॳͷҰճ }, []); Flutter Hooks Jetpack Compose
LaunchedEffect(id) { "// ID ͕มߋ͞ΕΔͨͼ } SideEffect { "// ຖճ
} LaunchedEffect(Unit) { "// ࠷ॳͷҰճ } 37 useEffect useEffect(() "=> { "// ID ͕มߋ͞ΕΔͨͼ }, [id]); useEffect(() "=> { "// ຖճ }); useEffect(() "=> { "// ࠷ॳͷҰճ }, []); Flutter Hooks Jetpack Compose
LaunchedEffect(id) { "// ID ͕มߋ͞ΕΔͨͼ } SideEffect { "// ຖճ
} LaunchedEffect(Unit) { "// ࠷ॳͷҰճ } 38 useEffect useEffect(() "=> { "// ID ͕มߋ͞ΕΔͨͼ }, [id]); useEffect(() "=> { "// ຖճ }); useEffect(() "=> { "// ࠷ॳͷҰճ }, const []); Flutter Hooks Jetpack Compose
39 useEffect Flutter Hooks @override Widget build(BuildContext context) { final
enabled = useState(false); useEffect(() { print("ॳճͷΈ"); }, const []); return Column( children: [ TextFormField( onChanged: (value) "=> enabled.value = value.isNotEmpty, ), ElevatedButton( onPressed: enabled.value ? () { ""/** Submit **/ } : null, child: Text('Submit'), ), ], ); } ςΩετ͕ೖྗ͞ΕͨΒϘλϯ͕༗ޮͱ ͳΔαϯϓϧͰ͕ͨ͠ɺ͜ͷ useEffect ͷ͍ํΛͨ͠߹ʹ࣮ߦ͞ΕΔͷ ࠷ॳͷҰճͱͳΓ·͢ɻ
State ͱ͍͍ͯͨ͠Θ͚Ͱͳ͘ຖ ճಉ͡σʔλΛऔಘ͢ΔͷͰͳ͘ Ωϟογϡ͓͖͍ͯͨ͠߹ͳͲʹ useMemoized ΛΩϟογϡ͍ͨ͠߹ 40
val now = remember(key) { LocalDateTime.now() } 41 useMemoized final
now = useMemoized(() { return DateTime.now(); }, [key]); Flutter Hooks Jetpack Compose remember ʹΩʔΛ͢͜ͱͰ͖ɺΩʔ͕ಉ͡ Ͱ͋ΕͦͷॲཧΩϟογϡ͞Ε·͢ useMemoized Λ͏͜ͱͰΩϟογϡ͕Ͱ ͖ɺΩʔ͕ಉ͡Ͱ͋ΕͦͷॲཧΩϟογϡ ͞Ε·͢
val now = remember(key) { LocalDateTime.now() } 42 useMemoized final
now = useMemoized(() { return DateTime.now(); }, [key]); Flutter Hooks Jetpack Compose remember ʹΩʔΛ͢͜ͱͰ͖ɺΩʔ͕ಉ͡ Ͱ͋ΕͦͷॲཧΩϟογϡ͞Ε·͢ useMemoized Λ͏͜ͱͰΩϟογϡ͕Ͱ ͖ɺΩʔ͕ಉ͡Ͱ͋ΕͦͷॲཧΩϟογϡ ͞Ε·͢
val now = remember(key) { LocalDateTime.now() } 43 useMemoized final
now = useMemoized(() { return DateTime.now(); }, [key]); Flutter Hooks Jetpack Compose remember ʹΩʔΛ͢͜ͱͰ͖ɺΩʔ͕ಉ͡ Ͱ͋ΕͦͷॲཧΩϟογϡ͞Ε·͢ useMemoized Λ͏͜ͱͰΩϟογϡ͕Ͱ ͖ɺΩʔ͕ಉ͡Ͱ͋ΕͦͷॲཧΩϟογϡ ͞Ε·͢
@Composable fun Greeting() { val (count, setCount) = remember {
mutableStateOf(0) } val now = remember(count) { LocalDateTime.now() } Column { Text(text = "Click Time: $now") Button(onClick = { setCount(count + 1) }) { Text("Count: ${count}") } } } 44 useMemoized @override Widget build(BuildContext context) { final count = useState(0); final now = useMemoized(() "=> DateTime.now(), [count.value]); return Column( children: [ Text("Click Time: $now"), TextButton( onPressed: () "=> count.value"++, child: Text("Count: ${count.value}"), ), ], ); } Flutter Hooks Jetpack Compose ͜ͷྫͰɺcount ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒ Λऔಘͯ͠Ωϟογϡ͍ͯ͠·͢
@Composable fun Greeting() { val (count, setCount) = remember {
mutableStateOf(0) } val now = remember(count) { LocalDateTime.now() } Column { Text(text = "Click Time: $now") Button(onClick = { setCount(count + 1) }) { Text("Count: ${count}") } } } 45 useMemoized @override Widget build(BuildContext context) { final count = useState(0); final now = useMemoized(() "=> DateTime.now(), [count.value]); return Column( children: [ Text("Click Time: $now"), TextButton( onPressed: () "=> count.value"++, child: Text("Count: ${count.value}"), ), ], ); } Flutter Hooks Jetpack Compose ͜ͷྫͰɺcount ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒ Λऔಘͯ͠Ωϟογϡ͍ͯ͠·͢
@Composable fun Greeting() { val (count, setCount) = remember {
mutableStateOf(0) } val now = remember(count) { LocalDateTime.now() } Column { Text(text = "Click Time: $now") Button(onClick = { setCount(count + 1) }) { Text("Count: ${count}") } } } 46 useMemoized @override Widget build(BuildContext context) { final count = useState(0); final now = useMemoized(() "=> DateTime.now(), [count.value]); return Column( children: [ Text("Click Time: $now"), TextButton( onPressed: () "=> count.value"++, child: Text("Count: ${count.value}"), ), ], ); } Flutter Hooks Jetpack Compose ͜ͷྫͰɺcount ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒ Λऔಘͯ͠Ωϟογϡ͍ͯ͠·͢
47 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04
Use Cases 05 Conclusion
48 Use Cases VideoPlayerController useAssetVideoController({ required String asset, bool autoPlay
= false, bool looping = false, }) { final controller = useMemoized( () "=> VideoPlayerController.asset(asset), [asset], ); useEffect(() { controller "..initialize() "..setLooping(looping); if (autoPlay) { controller.play(); } return () "=> controller.dispose(); }, [asset, autoPlay, looping]); return controller; } Flutter Hooks class VideoPage extends HookWidget { @override Widget build(BuildContext context) { final videoController = useAssetVideoController( asset: Assets.videos.bigbuckbunny, autoPlay: true, looping: true, ); return Scaffold( appBar: AppBar(), body: Center( child: AspectRatio( aspectRatio: 16 / 9, child: VideoPlayer(videoController), ), ), ); } } video_player ΛͬͨྫɻVideoPlayerController ͷॳظԽɾഁغΛΟδΣοτͱ͓ͯ͘͜͠ͱͰɺผͷΟδΣοτͰ͍ճ͕͠Ͱ͖Δ
49 Use Cases @Composable public fun BackHandler(enabled: Boolean = true,
onBack: () "-> Unit) { val currentOnBack by rememberUpdatedState(onBack) val backCallback = remember { object : OnBackPressedCallback(enabled) { override fun handleOnBackPressed() { currentOnBack() } } } SideEffect { backCallback.isEnabled = enabled } val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) { "No OnBackPressedDispatcherOwner" }.onBackPressedDispatcher val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner, backDispatcher) { backDispatcher.addCallback(lifecycleOwner, backCallback) onDispose { backCallback.remove() } } } Jetpack Compose class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme { Surface() { BackHandler(onBack = { "// Back pressed }) } } } } } Back key ͷϋϯυϦϯάΛ Composable Խͨ͠ྫɻ͜Εଞͷը໘Ͱ͍ճ͕͠Ͱ͖ΔΑ͏ʹͳΔ
50 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04
Use Cases 05 Conclusion
Thank you! https://wasabeef.jp http://github.com/wasabeef https://twitter.com/wasabeef_jp Resources 51 Daichi Furiya @wasabeef_jp