Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Flutter Hooks, sometimes Jetpack Compose

Flutter Hooks, sometimes Jetpack Compose

Flutter Hooks, sometimes Jetpack Compose

Daichi Furiya (Wasabeef)

June 22, 2021
Tweet

More Decks by Daichi Furiya (Wasabeef)

Other Decks in Programming

Transcript

  1. Flutter 1 Flutter Hooksɺ ɹɹɹ࣌ʑ Jetpack Compose Daichi Furiya @wasabeef_jp

  2. 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 ͸஌ͬͯΔਓ
  3. 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 ͸஌ͬͯΔਓ
  4. 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 ͸஌ͬͯΔਓ
  5. 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. 6 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04

    Use Cases 05 Conclusion
  7. Flutter Hooks Flutter Hooks ͱ͸ʁ ϝϦοτ 7 React Hooks ͷ

    Flutter ൛Ͱจ ๏΋࢖͍ํ΋΄΅Ұॹɻ εςʔτͷॳظԽʢinitStateʣ΍ഁغ (dispose) Λ΢ΟδΣοτͱ෼཭͕Ͱ͖Δɻ ୯७ʹΫϥεͷఆٛΛݮΒ͢γϯλοΫεγϡ ΨʔతͳԸܙ΋͋Δɻ
  8. Flutter Hooks Flutter Hooks ͱ͸ʁ ϝϦοτ 8 React Hooks ͷ

    Flutter ൛Ͱจ ๏΋࢖͍ํ΋΄΅Ұॹɻ εςʔτͷॳظԽʢinitStateʣ΍ഁغ (dispose) Λ΢ΟδΣοτͱ෼཭͕Ͱ͖Δɻ ୯७ʹΫϥεͷఆٛΛݮΒ͢γϯλοΫεγϡ ΨʔతͳԸܙ΋͋Δɻ
  9. Flutter Hooks Flutter Hooks ͱ͸ʁ ϝϦοτ 9 React Hooks ͷ

    Flutter ൛Ͱจ ๏΋࢖͍ํ΋΄΅Ұॹɻ εςʔτͷॳظԽʢinitStateʣ΍ഁغ (dispose) Λ΢ΟδΣοτͱ෼཭͕Ͱ͖Δɻ ୯७ʹΫϥεͷఆٛΛݮΒ͢γϯλοΫεγϡ ΨʔతͳԸܙ΋͋Δɻ
  10. 10 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04

    Use Cases 05 Conclusion
  11. 11 StatelessWidget, StatefulWidget StatelessWidget StatefulWidget ΦϒδΣΫτੜ੒࣌ʹ༩͑ΒΕͨ৘ ใͱ BuildContext Ҏ֎ʹ͸ґଘ͠ͳ ͍΢ΟδΣοτͰ͢ɻIconɺ

    IconButton ΍ Text ͳͲ͕ StatelessWidget ͷαϒΫϥεͰ͢ɻ γεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹ มԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛ ΋ͬͨ΢ΟδΣοτͰ͢ɻImageɺ TextFormField ΍ Hero ͳͲ͕ StatefulWidget ͷαϒΫϥεͰ͢ɻ
  12. 12 StatelessWidget, StatefulWidget StatelessWidget StatefulWidget ΦϒδΣΫτੜ੒࣌ʹ༩͑ΒΕͨ৘ ใͱ BuildContext Ҏ֎ʹ͸ґଘ͠ͳ ͍΢ΟδΣοτͰ͢ɻIconɺ

    IconButton ΍ Text ͳͲ͕ StatelessWidget ͷαϒΫϥεͰ͢ɻ γεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹ มԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛ ΋ͬͨ΢ΟδΣοτͰ͢ɻImageɺ TextFormField ΍ Hero ͳͲ͕ StatefulWidget ͷαϒΫϥεͰ͢ɻ
  13. 13 StatelessWidget, StatefulWidget StatelessWidget StatefulWidget ΦϒδΣΫτੜ੒࣌ʹ༩͑ΒΕͨ৘ ใͱ BuildContext Ҏ֎ʹ͸ґଘ͠ͳ ͍΢ΟδΣοτͰ͢ɻIconɺ

    IconButton ΍ Text ͳͲ͕ StatelessWidget ͷαϒΫϥεͰ͢ɻ γεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹ มԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛ ΋ͬͨ΢ΟδΣοτͰ͢ɻImageɺ TextFormField ΍ Hero ͳͲ͕ StatefulWidget ͷαϒΫϥεͰ͢ɻ
  14. 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. 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. 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. 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. 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. 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. 20 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04

    Use Cases 05 Conclusion
  21. Flutter Hooks ͷϝιου͸ HookWidget Λܧঝͨ͠ build ͷதͰ ݺ͹ͳ͍ͱ͍͚ͳ͍ useContext BuildContext

    ΛऔಘͰ͖Δ 21
  22. @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 Ͱ࢝·Δ͜ͱ ͕ϧʔϧͱͳ͍ͬͯΔɻ
  23. @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 Ͱ࢝·Δ͜ͱ ͕ϧʔϧͱͳ͍ͬͯΔɻ
  24. @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 Ͱ࢝·Δ͜ͱ ͕ϧʔϧͱͳ͍ͬͯΔɻ
  25. StatelessWidget Ͱ΋Կ͔͠Βͷঢ়ଶ ؅ཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useState Λ࢖͏৔໘Ͱ͢ʢViewModel ૬౰Ͱ อ࣋͢Δ΄ͲͰ΋ͳ͍΋ͷͳͲʣ useState final count

    = useState(0); count.value = 3; େମ֮͑Δͷ͸͜Ε͚ͩ StatefulWidget Λ࢖Θͳ͍ ؆୯ 25
  26. useState final count = useState(0); count.value = 3; େମ֮͑Δͷ͸͜Ε͚ͩ StatefulWidget

    Λ࢖Θͳ͍ ؆୯ 26 StatelessWidget Ͱ΋Կ͔͠Βͷঢ়ଶ ؅ཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useState Λ࢖͏৔໘Ͱ͢ʢViewModel ૬౰Ͱ อ࣋͢Δ΄ͲͰ΋ͳ͍΋ͷͳͲʣ
  27. useState final count = useState(0); count.value = 3; େମ֮͑Δͷ͸͜Ε͚ͩ StatefulWidget

    Λ࢖Θͳ͍ ؆୯ 27 StatelessWidget Ͱ΋Կ͔͠Βͷঢ়ଶ ؅ཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useState Λ࢖͏৔໘Ͱ͢ʢViewModel ૬౰Ͱ อ࣋͢Δ΄ͲͰ΋ͳ͍΋ͷͳͲʣ
  28. @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; ͜Ε͚ͩ
  29. @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; ͜Ε͚ͩ
  30. @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; ͜Ε͚ͩ
  31. @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; ͜Ε͚ͩ
  32. ؆୯ͳϥΠϑαΠΫϧॲཧ͕ඞཁ ͩͬͨ৔߹ʹ͸ useEffect Λ࢖͍· ͢ɻ΢ΟδΣοτੜ੒࣌ʹॳظԽ͠ ͍ͨ৔߹ͳͲɻ useEffect ॳظԽ΍ഁغͷॲཧ͕ඞཁͩͬͨΒ 32

  33. 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
  34. 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
  35. 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
  36. LaunchedEffect(id) { "// ID ͕มߋ͞ΕΔͨͼ } SideEffect { "// ຖճ

    } LaunchedEffect(Unit) { "// ࠷ॳͷҰճ } 36 useEffect useEffect(() "=> { "// ID ͕มߋ͞ΕΔͨͼ }, [id]); useEffect(() "=> { "// ຖճ }); useEffect(() "=> { "// ࠷ॳͷҰճ }, []); Flutter Hooks Jetpack Compose
  37. LaunchedEffect(id) { "// ID ͕มߋ͞ΕΔͨͼ } SideEffect { "// ຖճ

    } LaunchedEffect(Unit) { "// ࠷ॳͷҰճ } 37 useEffect useEffect(() "=> { "// ID ͕มߋ͞ΕΔͨͼ }, [id]); useEffect(() "=> { "// ຖճ }); useEffect(() "=> { "// ࠷ॳͷҰճ }, []); Flutter Hooks Jetpack Compose
  38. LaunchedEffect(id) { "// ID ͕มߋ͞ΕΔͨͼ } SideEffect { "// ຖճ

    } LaunchedEffect(Unit) { "// ࠷ॳͷҰճ } 38 useEffect useEffect(() "=> { "// ID ͕มߋ͞ΕΔͨͼ }, [id]); useEffect(() "=> { "// ຖճ }); useEffect(() "=> { "// ࠷ॳͷҰճ }, const []); Flutter Hooks Jetpack Compose
  39. 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 ͷ࢖͍ํΛͨ͠৔߹ʹ͸࣮ߦ͞ΕΔͷ͸ ࠷ॳͷҰճͱͳΓ·͢ɻ
  40. State ͱͯ͠࢖͍͍ͨΘ͚Ͱ͸ͳ͘ຖ ճಉ͡σʔλΛऔಘ͢ΔͷͰ͸ͳ͘ Ωϟογϡ͓͖͍ͯͨ͠৔߹ͳͲʹ useMemoized ஋ΛΩϟογϡ͍ͨ͠৔߹ 40

  41. val now = remember(key) { LocalDateTime.now() } 41 useMemoized final

    now = useMemoized(() { return DateTime.now(); }, [key]); Flutter Hooks Jetpack Compose remember ʹΩʔΛ౉͢͜ͱͰ͖ɺΩʔ͕ಉ͡ Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ͞Ε·͢ useMemoized Λ࢖͏͜ͱͰΩϟογϡ͕Ͱ ͖ɺΩʔ͕ಉ͡Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ ͞Ε·͢
  42. val now = remember(key) { LocalDateTime.now() } 42 useMemoized final

    now = useMemoized(() { return DateTime.now(); }, [key]); Flutter Hooks Jetpack Compose remember ʹΩʔΛ౉͢͜ͱͰ͖ɺΩʔ͕ಉ͡ Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ͞Ε·͢ useMemoized Λ࢖͏͜ͱͰΩϟογϡ͕Ͱ ͖ɺΩʔ͕ಉ͡Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ ͞Ε·͢
  43. val now = remember(key) { LocalDateTime.now() } 43 useMemoized final

    now = useMemoized(() { return DateTime.now(); }, [key]); Flutter Hooks Jetpack Compose remember ʹΩʔΛ౉͢͜ͱͰ͖ɺΩʔ͕ಉ͡ Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ͞Ε·͢ useMemoized Λ࢖͏͜ͱͰΩϟογϡ͕Ͱ ͖ɺΩʔ͕ಉ͡Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ ͞Ε·͢
  44. @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 ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒ Λऔಘͯ͠Ωϟογϡ͍ͯ͠·͢
  45. @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 ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒ Λऔಘͯ͠Ωϟογϡ͍ͯ͠·͢
  46. @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. 47 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04

    Use Cases 05 Conclusion
  48. 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. 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. 50 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04

    Use Cases 05 Conclusion
  51. Thank you! https://wasabeef.jp http://github.com/wasabeef https://twitter.com/wasabeef_jp Resources 51 Daichi Furiya @wasabeef_jp