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.3k
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
5
910
About Flutter Architecture
wasabeef
0
170
2023 Flutter/Dart Summary
wasabeef
0
46
I/O Extended 2023 - Dart と Flutter の新機能
wasabeef
0
140
I/O Extended 2023 - Flutter 活用事例
wasabeef
10
2.8k
What it Takes to be a Flutter Developer
wasabeef
0
160
FlutterKaigi 2022 Keynote
wasabeef
1
560
Flutter 2021 の振り返りと今後のアプリ開発に向けて / Looking back on Flutter 2021 and for future app development.
wasabeef
4
2.1k
Flutter Hooks, sometimes Jetpack Compose
wasabeef
3
1.8k
Other Decks in Programming
See All in Programming
創造的活動から切り拓く新たなキャリア 好きから始めてみる夜勤オペレーターからSREへの転身
yjszk
1
130
Webエンジニア主体のモバイルチームの 生産性を高く保つためにやったこと
igreenwood
0
340
return文におけるstd::moveについて
onihusube
1
1.1k
103 Early Hints
sugi_0000
1
230
nekko cloudにおけるProxmox VE利用事例
irumaru
3
430
HTTP compression in PHP and Symfony apps
dunglas
2
1.7k
命名をリントする
chiroruxx
1
410
create_tableをしただけなのに〜囚われのuuid編〜
daisukeshinoku
0
260
CQRS+ES の力を使って効果を感じる / Feel the effects of using the power of CQRS+ES
seike460
PRO
0
130
テストコード文化を0から作り、変化し続けた組織
kazatohiei
2
1.5k
Kaigi on Railsに初参加したら、その日にLT登壇が決定した件について
tama50505
0
100
「とりあえず動く」コードはよい、「読みやすい」コードはもっとよい / Code that 'just works' is good, but code that is 'readable' is even better.
mkmk884
3
420
Featured
See All Featured
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
32
2.7k
Adopting Sorbet at Scale
ufuk
73
9.1k
Being A Developer After 40
akosma
87
590k
Build The Right Thing And Hit Your Dates
maggiecrowley
33
2.4k
A Philosophy of Restraint
colly
203
16k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.1k
The World Runs on Bad Software
bkeepers
PRO
65
11k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
The Language of Interfaces
destraynor
154
24k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
191
16k
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