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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  14. 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 ͕૿͑ɺςΩετ͕Մม͢Δ΢ΟδΣοτˢ
    ↑ ༩͑ΒΕͨ৘ใͷΈͰෆมͳ΢ΟδΣοτ

    View Slide

  15. 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 ͕૿͑ɺςΩετ͕Մม͢Δ΢ΟδΣοτˢ
    ↑ ༩͑ΒΕͨ৘ใͷΈͰෆมͳ΢ΟδΣοτ

    View Slide

  16. 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 ͕૿͑ɺςΩετ͕Մม͢Δ΢ΟδΣοτˢ
    ↑ ༩͑ΒΕͨ৘ใͷΈͰෆมͳ΢ΟδΣοτ

    View Slide

  17. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. StatelessWidget Ͱ΋Կ͔͠Βͷঢ়ଶ
    ؅ཧ͕͍ͨ͠ͱࢥ͕ͬͨ࣌ useState
    Λ࢖͏৔໘Ͱ͢ʢViewModel ૬౰Ͱ
    อ࣋͢Δ΄ͲͰ΋ͳ͍΋ͷͳͲʣ
    useState
    final count = useState(0);

    count.value = 3;

    େମ֮͑Δͷ͸͜Ε͚ͩ
    StatefulWidget Λ࢖Θͳ͍ ؆୯
    25

    View Slide

  26. useState
    final count = useState(0);

    count.value = 3;

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

    View Slide

  27. useState
    final count = useState(0);

    count.value = 3;

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

    View Slide

  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;

    ͜Ε͚ͩ

    View Slide

  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;

    ͜Ε͚ͩ

    View Slide

  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;

    ͜Ε͚ͩ

    View Slide

  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;

    ͜Ε͚ͩ

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  36. LaunchedEffect(id) {

    "// ID ͕มߋ͞ΕΔͨͼ

    }

    SideEffect {

    "// ຖճ

    }

    LaunchedEffect(Unit) {

    "// ࠷ॳͷҰճ

    }

    36
    useEffect
    useEffect(() "=> {

    "// ID ͕มߋ͞ΕΔͨͼ

    }, [id]);

    useEffect(() "=> {

    "// ຖճ

    });

    useEffect(() "=> {

    "// ࠷ॳͷҰճ

    }, []);

    Flutter Hooks Jetpack Compose

    View Slide

  37. LaunchedEffect(id) {

    "// ID ͕มߋ͞ΕΔͨͼ

    }

    SideEffect {

    "// ຖճ

    }

    LaunchedEffect(Unit) {

    "// ࠷ॳͷҰճ

    }

    37
    useEffect
    useEffect(() "=> {

    "// ID ͕มߋ͞ΕΔͨͼ

    }, [id]);

    useEffect(() "=> {

    "// ຖճ

    });

    useEffect(() "=> {

    "// ࠷ॳͷҰճ

    }, []);

    Flutter Hooks Jetpack Compose

    View Slide

  38. LaunchedEffect(id) {

    "// ID ͕มߋ͞ΕΔͨͼ

    }

    SideEffect {

    "// ຖճ

    }

    LaunchedEffect(Unit) {

    "// ࠷ॳͷҰճ

    }

    38
    useEffect
    useEffect(() "=> {

    "// ID ͕มߋ͞ΕΔͨͼ

    }, [id]);

    useEffect(() "=> {

    "// ຖճ

    });

    useEffect(() "=> {

    "// ࠷ॳͷҰճ

    }, const []);

    Flutter Hooks Jetpack Compose

    View Slide

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

    View Slide

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

    View Slide

  41. val now = remember(key) {

    LocalDateTime.now()

    }

    41
    useMemoized
    final now = useMemoized(() {

    return DateTime.now();

    }, [key]);

    Flutter Hooks Jetpack Compose
    remember ʹΩʔΛ౉͢͜ͱͰ͖ɺΩʔ͕ಉ͡
    Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ͞Ε·͢
    useMemoized Λ࢖͏͜ͱͰΩϟογϡ͕Ͱ
    ͖ɺΩʔ͕ಉ͡Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ
    ͞Ε·͢

    View Slide

  42. val now = remember(key) {

    LocalDateTime.now()

    }

    42
    useMemoized
    final now = useMemoized(() {

    return DateTime.now();

    }, [key]);

    Flutter Hooks Jetpack Compose
    remember ʹΩʔΛ౉͢͜ͱͰ͖ɺΩʔ͕ಉ͡
    Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ͞Ε·͢
    useMemoized Λ࢖͏͜ͱͰΩϟογϡ͕Ͱ
    ͖ɺΩʔ͕ಉ͡Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ
    ͞Ε·͢

    View Slide

  43. val now = remember(key) {

    LocalDateTime.now()

    }

    43
    useMemoized
    final now = useMemoized(() {

    return DateTime.now();

    }, [key]);

    Flutter Hooks Jetpack Compose
    remember ʹΩʔΛ౉͢͜ͱͰ͖ɺΩʔ͕ಉ͡
    Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ͞Ε·͢
    useMemoized Λ࢖͏͜ͱͰΩϟογϡ͕Ͱ
    ͖ɺΩʔ͕ಉ͡Ͱ͋Ε͹ͦͷॲཧ͸Ωϟογϡ
    ͞Ε·͢

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide