Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Flutter Hooks を使ったアプリ開発 / App Development with ...
Search
Daichi Furiya (Wasabeef)
March 23, 2022
Programming
2
1.5k
Flutter Hooks を使ったアプリ開発 / App Development with the Flutter Hooks
CyberAgent Developer Conference 2022
Daichi Furiya (Wasabeef)
March 23, 2022
Tweet
Share
More Decks by Daichi Furiya (Wasabeef)
See All by Daichi Furiya (Wasabeef)
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
6
2.6k
About Flutter Architecture
wasabeef
1
300
2023 Flutter/Dart Summary
wasabeef
0
120
I/O Extended 2023 - Dart と Flutter の新機能
wasabeef
0
220
I/O Extended 2023 - Flutter 活用事例
wasabeef
10
3.1k
What it Takes to be a Flutter Developer
wasabeef
0
230
FlutterKaigi 2022 Keynote
wasabeef
1
670
Flutter 2021 の振り返りと今後のアプリ開発に向けて / Looking back on Flutter 2021 and for future app development.
wasabeef
4
2.2k
Flutter Hooks, sometimes Jetpack Compose
wasabeef
3
1.9k
Other Decks in Programming
See All in Programming
Premier Disciplin for Micro Frontends Multi Version/ Framework Scenarios @OOP 2026, Munic
manfredsteyer
PRO
0
200
CSC307 Lecture 15
javiergs
PRO
0
210
New in Go 1.26 Implementing go fix in product development
sunecosuri
0
310
Go Conference mini in Sendai 2026 : Goに新機能を提案し実装されるまでのフロー徹底解説
yamatoya
0
510
2026年は Rust 置き換えが流行る! / 20260220-niigata-5min-tech
girigiribauer
0
220
AIに仕事を丸投げしたら、本当に楽になれるのか
dip_tech
PRO
0
180
AIプロダクト時代のQAエンジニアに求められること
imtnd
2
640
ご飯食べながらエージェントが開発できる。そう、Agentic Engineeringならね。
yokomachi
1
280
izumin5210のプロポーザルのネタ探し #tskaigi_msup
izumin5210
1
510
encoding/json/v2のUnmarshalはこう変わった:内部実装で見る設計改善
kurakura0916
0
290
grapheme_strrev関数が採択されました(あと雑感)
youkidearitai
PRO
1
200
15年目のiOSアプリを1から作り直す技術
teakun
1
590
Featured
See All Featured
Site-Speed That Sticks
csswizardry
13
1.1k
The Language of Interfaces
destraynor
162
26k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
230
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
110
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
79
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
250
Building the Perfect Custom Keyboard
takai
2
710
Designing for humans not robots
tammielis
254
26k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
8.8k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
24k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
65
Transcript
None
߱େ גࣜձࣾαΠόʔΤʔδΣϯτ ϝσΟΞ౷ׅຊ෦ %FWFMPQFS1SPEVDUJWJUZࣨ XBTBCFFG@KQ XBTBCFFG (PPHMF%FWFMPQFST&YQFSU
"HFOEB wαΠόʔΤʔδΣϯτͷ'MVUUFS࠾༻ࣄྫ w'MVUUFS)PPLTΛͬͨΞϓϦ։ൃ w 'MVUUFS)PPLTͱʁ w 4UBUFGVM8JEHFUͷϥΠϑαΠΫϧΛར༻ͨ͠߹ w 'MVUUFS)PPLTΛجຊΛཧղ͢Δ w
'MVUUFS)PPLTͷޮՌతͳར༻๏ wࠓޙͷల
αΠόʔΤʔδΣϯτͷ'MVUUFS࠾༻ࣄྫ
'MVUUFSͷ࠾༻ࣄྫ ग़యɿIUUQTXXXDZCFSBHFOUDPKQUFDIJOGPJOGPEFUBJMJE
'MVUUFS)PPLTΛͬͨΞϓϦ։ൃ
'MVUUFS)PPLTͱʁ
'MVUUFS)PPLTͱʁ ग़యɿIUUQTHJUIVCDPNSSPVTTFM(JU fl VUUFS@IPPLT ग़యɿIUUQTSFBDUKTPSHEPDTIPPLTJOUSPIUNM w 3FBDU)PPLTͷ'MVUUFS൛Ͱจ๏͍ํ΄΅ಉ͡ͷ
4UBUFGVM8JEHFUͷ ϥΠϑαΠΫϧΛ ར༻ͨ͠߹
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), ); } } w γεςϜͷঢ়ଶͳͲʹΑͬͯಈతʹ มԽΛ͢Δখ͞ͳϥΠϑαΠΫϧΛ ͬͨΟδΣοτ w *NBHFɺ5FYU'PSN'JFME)FSPͳ Ͳ͕4UBUFGVM8JEHFUͷαϒΫϥε 4UBUFGVM8JEHFUͷઆ໌
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), ); } } 4UBUFGVM8JEHFUΛ͏ʹجຊతʹೋͭͷ ΫϥεΛ࡞Δඞཁ͕͋Δ w 4UBUFGVM8JEHFUΛܧঝͨ͠$PVOU1BHF w 4UBUF5Λܧঝͨ͠@$PVOU4UBUF 4UBUFGVM8JEHFUͷઆ໌
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), ); } } 4UBUFGVM8JEHFUͷઆ໌ 4UBUFGVM8JEHFUΛ͏ʹجຊతʹೋͭͷ ΫϥεΛ࡞Δඞཁ͕͋Δ w 4UBUFGVM8JEHFUΛܧঝͨ͠$PVOU1BHF w 4UBUF5Λܧঝͨ͠@$PVOU4UBUF
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), ); } } 4UBUFGVM8JEHFUͷઆ໌ JOJU4UBUFɿॳظԽͷॲཧ EJTQPTFɿഁغͷॲཧ TFU4UBUFɿݺͿ͜ͱͰঢ়ଶ͕มԽͨ͜͠ͱ Λ௨ͯ͠ϦϏϧυ͕Δ
4UBUFGVM8JEHFUͰ࣮ͨ͠߹ͷ՝ w ঢ়ଶΛอ࣋͢ΔΫϥεΛ৽ͨʹ࡞Βͳ͍ͱ͍͚ͳ͍ w ෳࡶͳϩδοΫΛؚΉঢ়ଶΛอ͍࣋ͨ͠߹ʹϩδοΫ ͱΟδΣοτΛͰ͖͍ͯͳ͍ w ίʔυͷ࠶ར༻͕ѱ͍ w ςετ͕ॻ͖ʹ͍͘
'MVUUFS)PPLTͷ جຊΛཧղ͢Δ
'MVUUFS)PPLTͰԿ͕ղܾͰ͖Δ͔ ग़యɿIUUQTHJUIVCDPNSSPVTTFM(JU fl VUUFS@IPPLT ग़యɿIUUQTSFBDUKTPSHEPDTIPPLTJOUSPIUNM w એݴత6*ͰΫϥΠΞϯτΛ্࣮͍ͯ͘͠ͰɺϩδοΫͱ ΟδΣοτʢίϯϙʔωϯτʣͷΛϝϯςφϯεੑΛอ ͪͭͭҡ࣋͢Δͷ͕͘͠ɺͦΕΛղܾ͢ΔػೳΛఏڙ͢Δ w
ίϯϙʔωϯτΛͪΌΜͱؔͱͯ͠ѻ͏ͨΊ w ΫϥεͷఆٛΛݮΒ͢ʢγϯλοΫεγϡΨʔʣͨΊ w 'MVUUFSͷΟδΣοτසൟʹ࠶ඳը͕࣮ߦ͞ΕΔͨΊॏ͍ ԋࢉෳࡶͳॲཧΛ͚͞ΕΔΑ͏ͳػೳΛఏڙ͢Δ w ಉ͡ೖྗσʔλͷ߹ʹ݁ՌΛΩϟογϡ͢Δ
VTF4UBUFΛཧղ͢Δ VTF4UBUF'MVUUFS)PPLTΛར༻͢ΔͳΒඞͣར༻͢Δ Ͱ͋Ζ͏جຊతͳϑοΫͰ͢ʢ෦࣮7BMVF/PUJGJFSʣ ར༻͢Δʹ֮͋ͨͬͯ͑Δ͜ͱ؆୯ͰVTF4UBUF 5 Ͱॳظ Λ༩͑ɺDPVOUWBMVFͷΛมߋ͢Δ͜ͱͰ࠶ඳը // ॳظԽ final
count = useState(0); // ͷߋ৽Λ࠶ඳը count.value = 3;
VTF4UBUFΛཧղ͢Δ ঢ়ଶͷཧΛ͢ΔͨΊʹϞόΠϧΞϓϦ։ൃͷΞʔΩςΫ νϟͰΑ͘ฉ͘Α͏ͳ7JFX.PEFM4UPSFΛ༻ҙ͢Δ΄ ͲͰͳ͍߹ʹར༻͢Δ͜ͱͰ͖Δ ྫ͑ɺϘλϯͷ&OBCMFE%JTBCMFEͷঢ়ଶͷอ࣋Θ͟ Θ͟ΫϥεΛ࡞Δ·Ͱͳ͘ɺͦͷΟδΣοτͰอ࣋͢ ΔͳͲ
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<CountPage> { 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 ʹॳ ظΛ༩͑Δ͚ͩʹͳ͍ͬͯΔ
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}"), ); } }
VTF& ff FDUΛཧղ͢Δ ؆୯ͳΟδΣοτͷϥΠϑαΠΫϧͰॲཧ͕ඞཁͩͬͨ ߹ʹVTF&GGFDUΛ͏ ʢΟδΣοτੜ࣌ʹॳظԽഁغΛ͍ͨ͠߹ͳͲʣ class _CountState extends
State<CountPage> { ɹ // . .. @override void initState() { / ** ॳظԽ **/ } @override void dispose() { /** ഁغ **/ } } ɹ // . .. } 4UBUFGVM8JEHFUͷJOJU4UBUFEJQPTF૬
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ͷୈೋҾʹॳظԽΛϏϧυ࣌ ʹຖճ࣮ߦ͞Εͳ͍Α͏ʹΩʔͱͳΔΦϒ δΣΫτΛ͢ඞཁ͕͋Δ
VTF& ff FDUͷୈೋҾॏཁ class CountPage extends HookWidget { @override Widget
build(BuildContext context) { useEffect(() => { / / ᶃ ID ͕มߋ͞ΕΔͨͼʹݺͼग़͠ }, [id]); useEffect(() => { / / ᶄ ຖճݺͼग़͠ }); useEffect(() => { / / ᶅ ࠷ॳͷҰճ͚ͩݺͼग़͠ }, const []); return // Widget . .. } }
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<Example> 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͕͋ΔͷͰͦΕͰڞ௨ Խ͢Δ͜ͱͰ͖Δ
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<Example> 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Λ͏͜ͱͰ ͳίʔυྔݮΒ͢͜ͱ͕Ͱ͖ɺར༻ଆͰͷ ίετΛԼ͛Δ͜ͱ͕Ͱ͖Δ
'MVUUFS)PPLTͷ ޮՌతͳར༻๏
ΧελϜϑοΫΛ࡞͢Δ w ໊ؔʹVTF999ͱ͍͏໊લΛ͚ͭΔ w ࠷ॳVTF4UBUFVTF& 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
fl VUUFS@VTFϥΠϒϥϦΛར༻ͯ͠ΈΔ w SFBDU@VTFͷ'MVUUFS൛ w Λ͑Δ৭ʑͳϢʔεέʔεʹରԠͨ͠ΧελϜϑοΫू w ࣗ࡞ΧελϜϑοΫΛ࡞Δͱ͖ʹࢀߟʹͰ͖Δʢͱࢥ͏ʣ
fl VUUFS@VTFϥΠϒϥϦΛར༻ͯ͠ΈΔ
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ͷ࣮
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ͷ࣮
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Λར༻ͯ͠ΈΔ
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Λར༻ͯ͠ΈΔ
fl VUUFS@VTF IUUQTHJUIVCDPNXBTBCFFG fl VUUFS@VTF
ΧελϜϑοΫͷςετ w 'MVUUFS)PPLT5FTUJOH-JCSBSZΛར༻ͯ͠ΈΔ w SFBDUIPPLTUFTUJOHMJCSBSZͷ'MVUUFS൛
ϥΠϒϥϦΛར༻͠ͳ͍߹ 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ʹඪ ४Ͱؚ·Ε͍ͯΔ༩͑ͨͷҰͭલͷ ঢ়ଶΛอ࣋͢ΔΧελϜϑοΫ ͜ͷϑοΫΛςετॻ͘߹ʹ࠷ Ͱ͜Ε͚ͩͷίʔυྔͰ͢
testWidgets('usePrevious', (tester) async { / / ... await tester.pumpWidget( HookBuilder(builder:
(context) { usePrevious(42); return const SizedBox(); }), ); / / ... }); ϥΠϒϥϦΛར༻͠ͳ͍߹ গ͠ࡉ͔͘આ໌͍ͯ͘͠ͱɺ QVNQ8JEHFUͱ͍͏ςετ༻Ο δΣοτͷঢ়ଶΛཧ͍ͯ͠Δؔʹ )PPL#VJMEFSΛͯ͠ɺͦͷதͰΧε λϜϑοΫΛ͏ඞཁ͕͋Δ
testWidgets('usePrevious', (tester) async { / / ... await tester.pumpWidget( HookBuilder(builder:
(context) { usePrevious(21); return const SizedBox(); }), ); / / ... }); ϥΠϒϥϦΛར༻͠ͳ͍߹ ͏Ұߋ৽͠ͳ͍ͱ͍͚ͳ͍ͷͰɺ ઌ΄Ͳ͕ͩͬͨࠓճΛ ͍ͯ͠Δ
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ͱ͍͏ҰͭલͷΛอ࣋ ͢ΔͷͰ࠷ॳʹ͕ͨ͠ݱࡏอ࣋ ͍ͯ͠ΔͱͳΔ ͜ΕΛνΣοΫ͢ΔͨΊʹςετΟ δΣοτͷϊʔυΛจࣈྻ͔Βϋο γϡίʔυͰൺֱ͍ͯ͠Δ ϥΠϒϥϦΛར༻͠ͳ͍߹
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', ), ); }); ϥΠϒϥϦΛར༻͠ͳ͍߹
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࣌ͷ ঢ়ଶऔಘγϯϓϧʹॻ͚Δ ϥΠϒϥϦΛར༻͢Δ߹
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ɿอ͍࣋ͯ͠ΔΛऔಘͰ͖Δ ϥΠϒϥϦΛར༻͢Δ߹
'MVUUFS)PPLT5FTUJOH-JCSBSZ IUUQTHJUIVCDPNXBTBCFFG fl VUUFS@IPPLT@UFTU
'MVUUFS)PPLTͷ-JOU w fl VUUFS@IPPLT@MJOU@QMVHJOΛར༻ͯ͠ΈΔ w FTMJOUQMVHJOSFBDUIPPLTͷ'MVUUFS൛ IUUQTHJUIVCDPNNKIE fl VUUFS@IPPLT@MJOU@QMVHJO
final variable1 = callSomething(); final variable2 = callSomething(); // ෆඞཁ·ͨෆ͍ͯ͠ΔΩʔ
useEffect(() { print(variable1); }, [variable2]); // ϑοΫΛ݅ذͷதʹೖΕͳ͍ if (flag) { final variable = useState('hello'); } ϑοΫʹ͍͔ͭ͘ͷϧʔϧ͕͋Γ·͢ ྫ w ΧελϜϑοΫͷ໊ؔʹVTF999 ͱ͍͏໊લΛ͚ͭΔ ɾϑοΫΛ݅ذʹೖΕͳ͍ ͜ͷ1MVHJOΛ͏͜ͱͰόάΛ͙ͱڞ ʹϨϏϡʔͷίετͳͲͷԼ͛Δ fl VUUFS@IPPLT@MJOU@QMVHJOΛར༻ͯ͠ΈΔ
ࠓޙͷల
ࠓޙͷల w )PPLTʹΑΔੈք؍Λ'MVUUFSք۾ʹਁಁ͍͖͍ͤͯͨ͞ w ੵۃతʹ'MVUUFSΑΓྺ࢙ͷ͋Δ3FBDU͔ΒऔΓೖΕ͍ͯ͘ w ྺ࢙3FBDUʼ'MVUUFSʼ+FUQBDL$PNQPTF4XJGU6*ͳͷͰ 'MVUUFSͷݟΛ+FUQBDL$PNQPTFͳͲʹస༻͍ͯ͘͠ w 3FBDU2VFSZʹΑΔγϯϓϧͳઃܭΛ'MVUUFSʹऔΓೖΕ͍ͨ
w ΞϓϦΞʔΩςΫνϟʹ࣌ؒΛ͔͚ͳ͍ੈք؍ͷ044࡞Δ w 3FDPJMɺ.77.ͳͲͷֶशʹ࣌ؒΛΘͣ؆୯ͳΞϓϦΛ؆୯ʹ࡞Δ ग़యɿIUUQTXXXJSBTVUPZBDPN