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

6dd0483f1353a4a359e92633cfd65c64?s=128

Daichi Furiya (Wasabeef)

June 22, 2021
Tweet

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