Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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 ͸஌ͬͯΔਓ

Slide 3

Slide 3 text

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 ͸஌ͬͯΔਓ

Slide 4

Slide 4 text

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 ͸஌ͬͯΔਓ

Slide 5

Slide 5 text

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 ͸஌ͬͯΔਓ

Slide 6

Slide 6 text

6 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04 Use Cases 05 Conclusion

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

10 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04 Use Cases 05 Conclusion

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

14 StatelessWidget, StatefulWidget class Counter extends StatefulWidget { @override State createState() "=> _CountState(); } class _CountState extends State { 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 ͕૿͑ɺςΩετ͕Մม͢Δ΢ΟδΣοτˢ ↑ ༩͑ΒΕͨ৘ใͷΈͰෆมͳ΢ΟδΣοτ

Slide 15

Slide 15 text

15 StatelessWidget, StatefulWidget class Counter extends StatefulWidget { @override State createState() "=> _CountState(); } class _CountState extends State { 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 ͕૿͑ɺςΩετ͕Մม͢Δ΢ΟδΣοτˢ ↑ ༩͑ΒΕͨ৘ใͷΈͰෆมͳ΢ΟδΣοτ

Slide 16

Slide 16 text

16 StatelessWidget, StatefulWidget class Counter extends StatefulWidget { @override State createState() "=> _CountState(); } class _CountState extends State { 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 ͕૿͑ɺςΩετ͕Մม͢Δ΢ΟδΣοτˢ ↑ ༩͑ΒΕͨ৘ใͷΈͰෆมͳ΢ΟδΣοτ

Slide 17

Slide 17 text

17 class CountPage extends StatefulWidget { @override State createState() "=> _CountState(); } class _CountState extends State { 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 Λܧঝ ͨ͠ _CountState Ͱ͢ɻ initState ͰॳظԽɺdispose Ͱഁغͷॲ ཧΛॻ͘͜ͱ͕Ͱ͖·͢ɻ ·ͨ setState ΛݺͿ͜ͱͰঢ়ଶ͕มԽ͠ ͨ͜ͱΛ௨஌ͯ͠ϦϏϧυ͕૸Γ·͢ɻ StatelessWidget, StatefulWidget

Slide 18

Slide 18 text

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 ͷ ໾ׂͳͲ͸ޙड़͍͖ͯ͠·͢ɻ

Slide 19

Slide 19 text

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 ͷॳΊͯͷαϯϓϧΛ୳͠ ͯΈΔͱઌ΄ͲΑ͏ʹΧ΢ϯλʔͷίʔ υΛΑ͘ݟ͔͚·͕͢ɺ࣮ࡍʹ͸ॳݟͰ ͦΕ͚ͩΈͯ΋ϐϯͱ͜ͳ͔ͬͨͷͰɺ ͜͜Ͱ͸ϑΥʔϜʹ஋͕͋Δ͔Ͳ͏͔Ͱ Ϙλϯͷ༗ޮແޮΛ੾Γସ͍͑ͯ·͢ɻ

Slide 20

Slide 20 text

20 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04 Use Cases 05 Conclusion

Slide 21

Slide 21 text

Flutter Hooks ͷϝιου͸ HookWidget Λܧঝͨ͠ build ͷதͰ ݺ͹ͳ͍ͱ͍͚ͳ͍ useContext BuildContext ΛऔಘͰ͖Δ 21

Slide 22

Slide 22 text

@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 Ͱ࢝·Δ͜ͱ ͕ϧʔϧͱͳ͍ͬͯΔɻ

Slide 23

Slide 23 text

@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 Ͱ࢝·Δ͜ͱ ͕ϧʔϧͱͳ͍ͬͯΔɻ

Slide 24

Slide 24 text

@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 Ͱ࢝·Δ͜ͱ ͕ϧʔϧͱͳ͍ͬͯΔɻ

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

@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; ͜Ε͚ͩ

Slide 29

Slide 29 text

@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; ͜Ε͚ͩ

Slide 30

Slide 30 text

@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; ͜Ε͚ͩ

Slide 31

Slide 31 text

@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; ͜Ε͚ͩ

Slide 32

Slide 32 text

؆୯ͳϥΠϑαΠΫϧॲཧ͕ඞཁ ͩͬͨ৔߹ʹ͸ useEffect Λ࢖͍· ͢ɻ΢ΟδΣοτੜ੒࣌ʹॳظԽ͠ ͍ͨ৔߹ͳͲɻ useEffect ॳظԽ΍ഁغͷॲཧ͕ඞཁͩͬͨΒ 32

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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 ͷ࢖͍ํΛͨ͠৔߹ʹ͸࣮ߦ͞ΕΔͷ͸ ࠷ॳͷҰճͱͳΓ·͢ɻ

Slide 40

Slide 40 text

State ͱͯ͠࢖͍͍ͨΘ͚Ͱ͸ͳ͘ຖ ճಉ͡σʔλΛऔಘ͢ΔͷͰ͸ͳ͘ Ωϟογϡ͓͖͍ͯͨ͠৔߹ͳͲʹ useMemoized ஋ΛΩϟογϡ͍ͨ͠৔߹ 40

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

@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 ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒ Λऔಘͯ͠Ωϟογϡ͍ͯ͠·͢

Slide 45

Slide 45 text

@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 ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒ Λऔಘͯ͠Ωϟογϡ͍ͯ͠·͢

Slide 46

Slide 46 text

@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 ͕૿͑ͨʢΫϦοΫͨ͠ʣλΠϛϯάͰݱࡏ࣌ؒ Λऔಘͯ͠Ωϟογϡ͍ͯ͠·͢

Slide 47

Slide 47 text

47 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04 Use Cases 05 Conclusion

Slide 48

Slide 48 text

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 ͷॳظԽɾഁغΛ΢ΟδΣοτͱ෼཭͓ͯ͘͜͠ͱͰɺผͷ΢ΟδΣοτͰ΋࢖͍ճ͕͠Ͱ͖Δ

Slide 49

Slide 49 text

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 Խͨ͠ྫɻ͜Ε΋ଞͷը໘Ͱ࢖͍ճ͕͠Ͱ͖ΔΑ͏ʹͳΔ

Slide 50

Slide 50 text

50 02 StatelessWidget, StatefulWidget 01 Flutter Hooks 03 useXXX 04 Use Cases 05 Conclusion

Slide 51

Slide 51 text

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