$30 off During Our Annual Pro Sale. View Details »

Flutter Hooks を使ったアプリ開発 / App Development with the Flutter Hooks

Flutter Hooks を使ったアプリ開発 / App Development with the Flutter Hooks

CyberAgent Developer Conference 2022

Daichi Furiya (Wasabeef)

March 23, 2022
Tweet

More Decks by Daichi Furiya (Wasabeef)

Other Decks in Programming

Transcript

  1. View Slide

  2. ߱໼େ஍
    גࣜձࣾαΠόʔΤʔδΣϯτ
    ϝσΟΞ౷ׅຊ෦
    %FWFMPQFS1SPEVDUJWJUZࣨ
    XBTBCFFG@KQ
    XBTBCFFG
    (PPHMF%FWFMPQFST&YQFSU

    View Slide

  3. "HFOEB
    wαΠόʔΤʔδΣϯτͷ'MVUUFS࠾༻ࣄྫ
    w'MVUUFS)PPLTΛ࢖ͬͨΞϓϦ։ൃ
    w 'MVUUFS)PPLTͱ͸ʁ
    w 4UBUFGVM8JEHFUͷϥΠϑαΠΫϧΛར༻ͨ͠৔߹
    w 'MVUUFS)PPLTΛجຊΛཧղ͢Δ
    w 'MVUUFS)PPLTͷޮՌతͳར༻๏
    wࠓޙͷల๬

    View Slide

  4. αΠόʔΤʔδΣϯτͷ'MVUUFS࠾༻ࣄྫ

    View Slide

  5. 'MVUUFSͷ࠾༻ࣄྫ
    ग़యɿIUUQTXXXDZCFSBHFOUDPKQUFDIJOGPJOGPEFUBJMJE

    View Slide

  6. 'MVUUFS)PPLTΛ࢖ͬͨΞϓϦ։ൃ

    View Slide

  7. 'MVUUFS)PPLTͱ͸ʁ

    View Slide

  8. 'MVUUFS)PPLTͱ͸ʁ
    ग़యɿIUUQTHJUIVCDPNSSPVTTFM(JU
    fl
    VUUFS@IPPLT
    ग़యɿIUUQTSFBDUKTPSHEPDTIPPLTJOUSPIUNM
    w 3FBDU)PPLTͷ'MVUUFS൛Ͱจ๏΋࢖͍ํ΋΄΅ಉ͡΋ͷ

    View Slide

  9. 4UBUFGVM8JEHFUͷ
    ϥΠϑαΠΫϧΛ
    ར༻ͨ͠৔߹

    View Slide

  10. 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),


    );


    }


    }
    w γεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹ
    มԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛ
    ΋ͬͨ΢ΟδΣοτ
    w *NBHFɺ5FYU'PSN'JFME΍)FSPͳ
    Ͳ͕4UBUFGVM8JEHFUͷαϒΫϥε
    4UBUFGVM8JEHFUͷઆ໌

    View Slide

  11. 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),


    );


    }


    }
    4UBUFGVM8JEHFUΛ࢖͏ʹ͸جຊతʹೋͭͷ
    ΫϥεΛ࡞Δඞཁ͕͋Δ
    w 4UBUFGVM8JEHFUΛܧঝͨ͠$PVOU1BHF
    w 4UBUF5Λܧঝͨ͠@$PVOU4UBUF
    4UBUFGVM8JEHFUͷઆ໌

    View Slide

  12. 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),


    );


    }


    }
    4UBUFGVM8JEHFUͷઆ໌
    4UBUFGVM8JEHFUΛ࢖͏ʹ͸جຊతʹೋͭͷ
    ΫϥεΛ࡞Δඞཁ͕͋Δ
    w 4UBUFGVM8JEHFUΛܧঝͨ͠$PVOU1BHF
    w 4UBUF5Λܧঝͨ͠@$PVOU4UBUF

    View Slide

  13. 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),


    );


    }


    }
    4UBUFGVM8JEHFUͷઆ໌
    JOJU4UBUFɿॳظԽͷॲཧ
    EJTQPTFɿഁغͷॲཧ
    TFU4UBUFɿݺͿ͜ͱͰঢ়ଶ͕มԽͨ͜͠ͱ
    Λ௨஌ͯ͠ϦϏϧυ͕૸Δ

    View Slide

  14. 4UBUFGVM8JEHFUͰ࣮૷ͨ͠৔߹ͷ՝୊
    w ঢ়ଶΛอ࣋͢ΔΫϥεΛ৽ͨʹ࡞Βͳ͍ͱ͍͚ͳ͍
    w ෳࡶͳϩδοΫΛؚΉঢ়ଶΛอ͍࣋ͨ͠৔߹ʹ͸ϩδοΫ
    ͱ΢ΟδΣοτΛ෼཭Ͱ͖͍ͯͳ͍
    w ίʔυͷ࠶ར༻͕ѱ͍
    w ςετ͕ॻ͖ʹ͍͘

    View Slide

  15. 'MVUUFS)PPLTͷ
    جຊΛཧղ͢Δ

    View Slide

  16. 'MVUUFS)PPLTͰԿ͕ղܾͰ͖Δ͔
    ग़యɿIUUQTHJUIVCDPNSSPVTTFM(JU
    fl
    VUUFS@IPPLT
    ग़యɿIUUQTSFBDUKTPSHEPDTIPPLTJOUSPIUNM
    w એݴత6*ͰΫϥΠΞϯτΛ࣮૷্͍ͯ͘͠ͰɺϩδοΫͱ
    ΢ΟδΣοτʢίϯϙʔωϯτʣͷ෼཭ΛϝϯςφϯεੑΛอ
    ͪͭͭҡ࣋͢Δͷ͕೉͘͠ɺͦΕΛղܾ͢ΔػೳΛఏڙ͢Δ
    w ίϯϙʔωϯτΛͪΌΜͱؔ਺ͱͯ͠ѻ͏ͨΊ
    w ΫϥεͷఆٛΛݮΒ͢ʢγϯλοΫεγϡΨʔʣͨΊ
    w 'MVUUFSͷ΢ΟδΣοτ͸සൟʹ࠶ඳը͕࣮ߦ͞ΕΔͨΊॏ͍
    ԋࢉ΍ෳࡶͳॲཧΛ͚͞ΕΔΑ͏ͳػೳΛఏڙ͢Δ
    w ಉ͡ೖྗσʔλͷ৔߹ʹ͸݁ՌΛΩϟογϡ͢Δ

    View Slide

  17. VTF4UBUFΛཧղ͢Δ
    VTF4UBUF͸'MVUUFS)PPLTΛར༻͢ΔͳΒඞͣར༻͢Δ
    Ͱ͋Ζ͏جຊతͳϑοΫͰ͢ʢ಺෦࣮૷͸7BMVF/PUJGJFSʣ
    ར༻͢Δʹ֮͋ͨͬͯ͑Δ͜ͱ؆୯ͰVTF4UBUF 5
    Ͱॳظ
    ஋Λ༩͑ɺDPVOUWBMVFͷ஋Λมߋ͢Δ͜ͱͰ࠶ඳը
    //
    ॳظԽ


    final count = useState(0);


    //
    ஋ͷߋ৽Λ࠶ඳը


    count.value = 3;

    View Slide

  18. VTF4UBUFΛཧղ͢Δ
    ঢ়ଶͷ؅ཧΛ͢ΔͨΊʹϞόΠϧΞϓϦ։ൃͷΞʔΩςΫ
    νϟͰΑ͘ฉ͘Α͏ͳ7JFX.PEFM΍4UPSFΛ༻ҙ͢Δ΄
    ͲͰ΋ͳ͍৔߹ʹར༻͢Δ͜ͱͰ͖Δ
    ྫ͑͹ɺϘλϯͷ&OBCMFE%JTBCMFEͷঢ়ଶͷอ࣋͸Θ͟
    Θ͟ΫϥεΛ࡞Δ·Ͱ΋ͳ͘ɺͦͷ΢ΟδΣοτͰอ࣋͢
    ΔͳͲ

    View Slide

  19. VTF4UBUFΛཧղ͢Δ
    class CountPage extends HookWidget {


    @override


    Widget build(BuildContext context) {


    final count = useState(0);


    return TextButton(


    onPressed: () { count.value
    ++
    ; },


    child: Text("Count is ${count.value}"),


    );


    }


    }
    class CountPage 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("Count is $count"),


    onPressed: ()
    =>
    setCount(count + 1),


    );


    }


    }
    4UBUFGVM8JEHFU VTF4UBUF
    େ͖ͳҧ͍͸4UBUFΛܧঝͨ͠Ϋϥε͕ඞ
    ཁͩͬͨ΋ͷ͕ͳ͘ͳΓVTF4UBUF 5
    ʹॳ
    ظ஋Λ༩͑Δ͚ͩʹͳ͍ͬͯΔ

    View Slide

  20. VTF4UBUFΛཧղ͢Δ
    class CountPage extends HookWidget {


    @override


    Widget build(BuildContext context) {


    final count = useState(0);


    return TextButton(


    onPressed: () { count.value
    +
    +
    ; },


    child: Text("Count is ${count.value}"),


    );


    }


    }


    View Slide

  21. VTF&
    ff
    FDUΛཧղ͢Δ
    ؆୯ͳ΢ΟδΣοτͷϥΠϑαΠΫϧͰॲཧ͕ඞཁͩͬͨ
    ৔߹ʹ͸VTF&GGFDUΛ࢖͏

    ʢ΢ΟδΣοτੜ੒࣌ʹॳظԽ΍ഁغΛ͍ͨ͠৔߹ͳͲʣ
    class _CountState extends State {


    ɹ
    // . ..

    @override


    void initState() {
    /
    **
    ॳظԽ **/ }


    @override


    void dispose() {
    /**
    ഁغ **/ }


    }


    ɹ
    // . ..

    }
    4UBUFGVM8JEHFUͷJOJU4UBUFEJQPTF૬౰

    View Slide

  22. VTF&
    ff
    FDUΛཧղ͢Δ
    class CountPage extends HookWidget {


    @override


    Widget build(BuildContext context) {


    useEffect(()
    =>
    {


    / /
    ॳظԽ


    final subscription = source.subscribe(id);


    return ()
    =>
    {


    //
    ഁغ


    subscription.unsubscribe(id);


    };


    }, [id]);


    return
    //
    Widget
    .
    ..


    }


    }
    8JEHFUͷCVJME಺ͰVTF&GGFDUͷୈҰҾ
    ਺ʹΫϩʔδϟΛ౉͠ɺͦͷΫϩʔδϟͷ
    SFUVSO࣌ʹഁغ࣌ͷॲཧΛॻ͘
    VTF&GGFDUͷୈೋҾ਺ʹ͸ॳظԽΛϏϧυ࣌
    ʹຖճ࣮ߦ͞Εͳ͍Α͏ʹΩʔͱͳΔΦϒ
    δΣΫτΛ౉͢ඞཁ͕͋Δ

    View Slide

  23. VTF&
    ff
    FDUͷୈೋҾ਺͸ॏཁ
    class CountPage extends HookWidget {


    @override


    Widget build(BuildContext context) {


    useEffect(()
    =>
    {


    / /
    ᶃ ID ͕มߋ͞ΕΔͨͼʹݺͼग़͠


    }, [id]);


    useEffect(()
    =>
    {


    / /
    ᶄ ຖճݺͼग़͠


    });


    useEffect(()
    =>
    {


    / /
    ᶅ ࠷ॳͷҰճ͚ͩݺͼग़͠


    }, const []);


    return
    //
    Widget
    .
    ..


    }


    }

    View Slide

  24. VTF"OJNBUJPO$POUSPMMFSΛར༻ͯ͠ΈΔ
    class Example extends StatefulWidget {


    final Duration duration;


    const Example({Key? key, required this.duration}) : super(key: key);


    @override


    _ExampleState createState()
    =>
    _ExampleState();


    }


    class _ExampleState extends State with SingleTickerProviderStateMixin {


    AnimationController? _controller;


    @override


    void initState() {


    super.initState();


    _controller = AnimationController(vsync: this, duration: widget.duration);


    }


    @override


    void didUpdateWidget(Example oldWidget) {


    super.didUpdateWidget(oldWidget);


    if (widget.duration
    !=
    oldWidget.duration) {


    _controller!.duration = widget.duration;


    }


    }


    @override


    void dispose() {


    _controller!.dispose();


    super.dispose();


    }


    @override


    Widget build(BuildContext context) {


    return Container();


    }


    }
    4UBUFGVM8JEHFU
    'MVUUFSͰΞχϝʔγϣϯΛ4UBUFGVM8JEHFU
    Ͱ࣮૷͠Α͏ͱ͢Δͱঢ়ଶ؅ཧͷίʔυΛΫ
    ϥεʹ͚ͩͰ͜Ε͚ͩඞཁʹͳΓͦͷଟ͕͘
    ࢖͍ճ͠Ͱ͖ͳ͍ίʔυʹͳΔ
    ˞৑௕ͳίʔυΛݮΒ͢ͱ͍͏໨త͚ͩͰ͋
    Ε͹%BSUʹ΋.JYJO͕͋ΔͷͰͦΕͰڞ௨
    Խ͢Δ͜ͱ΋Ͱ͖Δ

    View Slide

  25. VTF"OJNBUJPO$POUSPMMFSΛར༻ͯ͠ΈΔ
    class Example extends HookWidget {


    const Example({required this.duration});


    final Duration duration;


    @override


    Widget build(BuildContext context) {


    final controller =


    useAnimationController(duration: duration);


    return Container();


    }


    }
    class Example extends StatefulWidget {


    final Duration duration;


    const Example({Key? key, required this.duration}) : super(key: key);


    @override


    _ExampleState createState()
    =>
    _ExampleState();


    }


    class _ExampleState extends State with SingleTickerProviderStateMixin {


    AnimationController? _controller;


    @override


    void initState() {


    super.initState();


    _controller = AnimationController(vsync: this, duration: widget.duration);


    }


    @override


    void didUpdateWidget(Example oldWidget) {


    super.didUpdateWidget(oldWidget);


    if (widget.duration
    !=
    oldWidget.duration) {


    _controller!.duration = widget.duration;


    }


    }


    @override


    void dispose() {


    _controller!.dispose();


    super.dispose();


    }


    @override


    Widget build(BuildContext context) {


    return Container();


    }


    }
    4UBUFGVM8JEHFU VTF"OJNBUJPO$POUSPMMFS
    VTF"OJNBUJPO$POUSPMMFSΛ࢖͏͜ͱͰ৑௕
    ͳίʔυྔ͸ݮΒ͢͜ͱ͕Ͱ͖ɺར༻ଆͰͷ
    ίετΛԼ͛Δ͜ͱ͕Ͱ͖Δ

    View Slide

  26. 'MVUUFS)PPLTͷ
    ޮՌతͳར༻๏

    View Slide

  27. ΧελϜϑοΫΛ࡞੒͢Δ
    w ؔ਺໊ʹ͸VTF999ͱ͍͏໊લΛ͚ͭΔ
    w ࠷ॳ͸VTF4UBUF΍VTF&
    ff
    FDUΛ૊Έ߹Θ
    ࣮ͤͯ૷ͯ͠ΈΔ
    VoidCallback useUpdate() {


    final attempt = useState(0);


    return ()
    =>
    attempt.value
    ++
    ;


    }
    class Sample extends HookWidget {


    @override


    Widget build(BuildContext context) {


    final update = useUpdate();


    return Column(


    children: [


    ElevatedButton(


    onPressed: ()
    =>
    update(),


    child: const Text("Update"),


    ),


    ]


    );


    }


    }
    VTF6QEBUF
    4BNQMF

    View Slide

  28. fl
    VUUFS@VTFϥΠϒϥϦΛར༻ͯ͠ΈΔ
    w SFBDU@VTFͷ'MVUUFS൛
    w Λ௒͑Δ৭ʑͳϢʔεέʔεʹରԠͨ͠ΧελϜϑοΫू
    w ࣗ࡞ΧελϜϑοΫΛ࡞Δͱ͖ʹࢀߟʹͰ͖Δʢͱࢥ͏ʣ

    View Slide

  29. fl
    VUUFS@VTFϥΠϒϥϦΛར༻ͯ͠ΈΔ

    View Slide

  30. VTF&
    ff
    FDU0ODFΛར༻ͯ͠ΈΔ
    w
    fl
    VUUFS@VTFʹ͓͍ͯ࠷΋୯७ͳ࣮૷
    w VTF&
    ff
    FDUͷୈೋҾ਺Λলུ͢ΔͨΊͷΧελϜϑοΫ
    useEffect(()
    =>
    {


    //
    ࠷ॳͷҰճ͚ͩݺͼग़͠


    }, const []);
    void useEffectOnce(Dispose? Function() effect) {


    return useEffect(effect, const []);


    }
    VTF&GGFDU
    VTF&GGFDU0ODFͷ࣮૷

    View Slide

  31. VTF&
    ff
    FDU0ODFΛར༻ͯ͠ΈΔ
    w
    fl
    VUUFS@VTFʹ͓͍ͯ࠷΋୯७ͳ࣮૷
    w VTF&
    ff
    FDUͷୈೋҾ਺Λলུ͢ΔͨΊͷΧελϜϑοΫ
    useEffect(()
    =>
    {


    //
    ࠷ॳͷҰճ͚ͩݺͼग़͠


    }, const []);
    void useEffectOnce(Dispose? Function() effect) {


    return useEffect(effect, const []);


    }
    VTF&GGFDU
    VTF&GGFDU0ODFͷ࣮૷

    View Slide

  32. w ΞϓϦཁ݅ͰԿඵޙʹॲཧΛ࣮ߦ͍ͤͨ͞৔߹
    w VTF5JNFPVUVTF5JNFPVU'OΛ࢖͏͜ͱͰࢦఆͷ࣌ؒܦա
    ޙʹϦϏϧυ΍ΫϩʔδϟΛ࣮ߦ͢Δ͜ͱ͕Ͱ͖Δ
    class Sample extends HookWidget {


    @override


    Widget build(BuildContext context) {


    final timeout = useTimeout(const Duration(seconds: 3));


    return Column(


    children: [


    Text("isReady
    ?:
    ${timeout.isReady}"),


    ElevatedButton(


    onPressed: ()
    =>
    timeout.reset(),


    child: const Text("Reset"),


    ),


    ],


    );


    }


    }
    VTF5JNFPVUΛར༻ͯ͠ΈΔ

    View Slide

  33. w ಺෦Ͱ͸DPOOFDUJWJUZ@QMVTͱ͍͏ϥΠϒϥϦΛ࢖ͬͯ୺
    ຤ͷωοτϫʔΫঢ়گΛऔಘ͍ͯ͠Δ
    w ࣗ෼Ͱ࣮૷͢Δͱඇಉظॲཧͷ੍ޚͳͲ͕ඞཁ͕ͩɺ͜
    ͷΧελϜϑοΫͰ͸ར༻ଆ͸γϯϓϧʹ͍ͯ͠Δ
    class Sample extends HookWidget {


    @override


    Widget build(BuildContext context) {


    final networkState = useNetworkState();


    return Column(


    children: [


    Text(


    "Network: ${networkState.connectivityResult}"),


    ],


    );


    }


    }
    VTF/FUXPSL4UBUFΛར༻ͯ͠ΈΔ

    View Slide

  34. fl
    VUUFS@VTF
    IUUQTHJUIVCDPNXBTBCFFG
    fl
    VUUFS@VTF

    View Slide

  35. ΧελϜϑοΫͷςετ
    w 'MVUUFS)PPLT5FTUJOH-JCSBSZΛར༻ͯ͠ΈΔ
    w SFBDUIPPLTUFTUJOHMJCSBSZͷ'MVUUFS൛

    View Slide

  36. ϥΠϒϥϦΛར༻͠ͳ͍৔߹
    testWidgets('usePrevious', (tester) async {


    await tester.pumpWidget(


    HookBuilder(builder: (context) {


    usePrevious(42);


    return const SizedBox();


    }),


    );


    await tester.pumpWidget(


    HookBuilder(builder: (context) {


    usePrevious(21);


    return const SizedBox();


    }),


    );


    final element = tester.element(find.byType(HookBuilder));


    expect(


    element


    .toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage)


    .toStringDeep(),


    equalsIgnoringHashCodes(


    'HookBuilder\n'


    ' │ usePrevious: 42\n'


    ' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',


    ),


    );


    });
    VTF1SFWJPVT͸'MVUUFS)PPLTʹඪ
    ४Ͱؚ·Ε͍ͯΔ༩͑ͨ஋ͷҰͭલͷ
    ঢ়ଶΛอ࣋͢ΔΧελϜϑοΫ
    ͜ͷϑοΫΛςετॻ͘৔߹ʹ͸࠷௿
    Ͱ΋͜Ε͚ͩͷίʔυྔͰ͢

    View Slide

  37. testWidgets('usePrevious', (tester) async {


    /
    / ...

    await tester.pumpWidget(


    HookBuilder(builder: (context) {


    usePrevious(42);


    return const SizedBox();


    }),


    );


    /
    / ...

    });


    ϥΠϒϥϦΛར༻͠ͳ͍৔߹
    গ͠ࡉ͔͘આ໌͍ͯ͘͠ͱɺ
    QVNQ8JEHFUͱ͍͏ςετ༻΢Ο
    δΣοτͷঢ়ଶΛ؅ཧ͍ͯ͠Δؔ਺ʹ
    )PPL#VJMEFSΛ౉ͯ͠ɺͦͷதͰΧε
    λϜϑοΫΛ࢖͏ඞཁ͕͋Δ

    View Slide

  38. testWidgets('usePrevious', (tester) async {


    /
    / ...

    await tester.pumpWidget(


    HookBuilder(builder: (context) {


    usePrevious(21);


    return const SizedBox();


    }),


    );


    /
    / ...

    });


    ϥΠϒϥϦΛར༻͠ͳ͍৔߹
    ΋͏Ұ౓ߋ৽͠ͳ͍ͱ͍͚ͳ͍ͷͰɺ
    ઌ΄Ͳ͸͕ͩͬͨࠓճ͸Λ౉
    ͍ͯ͠Δ

    View Slide

  39. testWidgets('usePrevious', (tester) async {


    // .. .

    final element = tester.element(find.byType(HookBuilder));


    expect(


    element


    .toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage)


    .toStringDeep(),


    equalsIgnoringHashCodes(


    'HookBuilder\n'


    ' │ usePrevious: 42\n'


    ' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',


    ),


    );


    // .. .

    });
    VTF1SFWJPVTͱ͍͏Ұͭલͷ஋Λอ࣋
    ͢ΔͷͰ࠷ॳʹ౉͕ͨ͠ݱࡏอ࣋
    ͍ͯ͠Δ஋ͱͳΔ
    ͜ΕΛνΣοΫ͢ΔͨΊʹςετ΢Ο
    δΣοτͷϊʔυΛจࣈྻ͔Βϋο
    γϡίʔυͰൺֱ͍ͯ͠Δ
    ϥΠϒϥϦΛར༻͠ͳ͍৔߹

    View Slide

  40. testWidgets('usePrevious', (tester) async {


    await tester.pumpWidget(


    HookBuilder(builder: (context) {


    usePrevious(42);


    return const SizedBox();


    }),


    );


    await tester.pumpWidget(


    HookBuilder(builder: (context) {


    usePrevious(21);


    return const SizedBox();


    }),


    );


    final element = tester.element(find.byType(HookBuilder));


    expect(


    element


    .toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage)


    .toStringDeep(),


    equalsIgnoringHashCodes(


    'HookBuilder\n'


    ' │ usePrevious: 42\n'


    ' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',


    ),


    );


    });
    ϥΠϒϥϦΛར༻͠ͳ͍৔߹

    View Slide

  41. testWidgets('usePrevious', (tester) async {


    final result = await buildHook(


    (value)
    = >
    usePrevious(value),


    initialProps: 42,


    );


    await result.rebuild(21);


    expect(result.current, 42);


    });
    testWidgets('usePrevious', (tester) async {


    await tester.pumpWidget(


    HookBuilder(builder: (context) {


    usePrevious(42);


    return const SizedBox();


    }),


    );


    await tester.pumpWidget(


    HookBuilder(builder: (context) {


    usePrevious(21);


    return const SizedBox();


    }),


    );


    final element = tester.element(find.byType(HookBuilder));


    expect(


    element


    .toDiagnosticsNode(style: DiagnosticsTreeStyle.offstage)


    .toStringDeep(),


    equalsIgnoringHashCodes(


    'HookBuilder\n'


    ' │ usePrevious: 42\n'


    ' └SizedBox(renderObject: RenderConstrainedBox#00000)\n',


    ),


    );


    });
    ར༻͠ͳ͍ ར༻͢Δ
    ΧελϜϑοΫͷঢ়ଶΛߋ৽͢ΔͨΊʹ
    UFTUFSQVNQ8JEHFUʹ)PPL#VJMEFSΛຖ
    ճ౉͍ͯͨ͠΋ͷΛলུͰ͖ɺFYQFDU࣌ͷ
    ঢ়ଶऔಘ΋γϯϓϧʹॻ͚Δ
    ϥΠϒϥϦΛར༻͢Δ৔߹

    View Slide

  42. testWidgets('usePrevious', (tester) async {


    final result = await buildHook(


    (value)
    = >
    usePrevious(value),


    initialProps: 42,


    );


    await result.rebuild(21);


    expect(result.current, 42);


    });
    'MVUUFS)PPLT5FTUJOH-JCSBSZΛར༻ͨ͠৔
    ߹͸ଟ͘ͷ৑௕ίʔυΛ࡟ݮ͢Δ͜ͱ͕Ͱ͖Δ
    CVJME)PPLɿͷΫϩʔδϟʹΧελϜϑοΫͷ
    ݺͼग़͠Λॻ͖ɺୈೋҾ਺ʹॳظ஋Λ༩͑Δ͜
    ͱ͕Ͱ͖Δ
    SFCVJMEɿCVJME)PPLͰੜ੒͞ΕͨΫϥεͰ஋
    Λߋ৽͠ϦϏϧυͰ͖Δ
    DVSSFOUɿอ͍࣋ͯ͠Δ஋ΛऔಘͰ͖Δ
    ϥΠϒϥϦΛར༻͢Δ৔߹

    View Slide

  43. 'MVUUFS)PPLT5FTUJOH-JCSBSZ
    IUUQTHJUIVCDPNXBTBCFFG
    fl
    VUUFS@IPPLT@UFTU

    View Slide

  44. 'MVUUFS)PPLTͷ-JOU
    w
    fl
    VUUFS@IPPLT@MJOU@QMVHJOΛར༻ͯ͠ΈΔ
    w FTMJOUQMVHJOSFBDUIPPLTͷ'MVUUFS൛
    IUUQTHJUIVCDPNNKIE
    fl
    VUUFS@IPPLT@MJOU@QMVHJO

    View Slide

  45. final variable1 = callSomething();


    final variable2 = callSomething();


    //
    ෆඞཁ·ͨ͸ෆ଍͍ͯ͠ΔΩʔ


    useEffect(() {


    print(variable1);


    }, [variable2]);


    //
    ϑοΫΛ৚݅෼ذͷதʹೖΕͳ͍


    if (flag) {


    final variable = useState('hello');


    }


    ϑοΫʹ͸͍͔ͭ͘ͷϧʔϧ͕͋Γ·͢

    w ΧελϜϑοΫͷؔ਺໊ʹ͸VTF999
    ͱ͍͏໊લΛ͚ͭΔ
    ɾϑοΫΛ৚݅෼ذʹೖΕͳ͍
    ͜ͷ1MVHJOΛ࢖͏͜ͱͰόάΛ๷͙ͱڞ
    ʹϨϏϡʔͷίετͳͲͷԼ͛Δ
    fl
    VUUFS@IPPLT@MJOU@QMVHJOΛར༻ͯ͠ΈΔ

    View Slide

  46. ࠓޙͷల๬

    View Slide

  47. ࠓޙͷల๬
    w )PPLTʹΑΔੈք؍Λ'MVUUFSք۾ʹਁಁ͍͖͍ͤͯͨ͞
    w ੵۃతʹ'MVUUFSΑΓ΋ྺ࢙ͷ͋Δ3FBDU͔ΒऔΓೖΕ͍ͯ͘
    w ྺ࢙͸3FBDUʼ'MVUUFSʼ+FUQBDL$PNQPTF4XJGU6*ͳͷͰ
    'MVUUFSͷ஌ݟΛ+FUQBDL$PNQPTFͳͲʹ΋స༻͍ͯ͘͠
    w 3FBDU2VFSZʹΑΔγϯϓϧͳઃܭΛ'MVUUFSʹ΋औΓೖΕ͍ͨ
    w ΞϓϦΞʔΩςΫνϟʹ࣌ؒΛ͔͚ͳ͍ੈք؍ͷ044࡞Δ
    w 3FDPJMɺ.77.ͳͲͷֶशʹ࣌ؒΛ࢖Θͣ؆୯ͳΞϓϦΛ؆୯ʹ࡞Δ
    ग़యɿIUUQTXXXJSBTVUPZBDPN

    View Slide