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.4k
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.1k
About Flutter Architecture
wasabeef
1
280
2023 Flutter/Dart Summary
wasabeef
0
89
I/O Extended 2023 - Dart と Flutter の新機能
wasabeef
0
190
I/O Extended 2023 - Flutter 活用事例
wasabeef
10
3k
What it Takes to be a Flutter Developer
wasabeef
0
210
FlutterKaigi 2022 Keynote
wasabeef
1
630
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
AI 駆動開発におけるコミュニティと AWS CDK の価値
konokenj
5
320
AkarengaLT vol.38
hashimoto_kei
1
130
TFLintカスタムプラグインで始める Terraformコード品質管理
bells17
2
520
ドメイン駆動設計のエッセンス
masuda220
PRO
15
7.4k
SwiftDataを使って10万件のデータを読み書きする
akidon0000
0
250
モテるデスク環境
mozumasu
3
1.4k
data-viz-talk-cz-2025
lcolladotor
0
110
オンデバイスAIとXcode
ryodeveloper
0
380
3年ぶりにコードを書いた元CTOが Claude Codeと30分でMVPを作った話
maikokojima
0
750
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
500
実践Claude Code:20の失敗から学ぶAIペアプログラミング
takedatakashi
18
9.4k
Go言語はstack overflowの夢を見るか?
logica0419
1
680
Featured
See All Featured
How STYLIGHT went responsive
nonsquared
100
5.9k
KATA
mclloyd
PRO
32
15k
Build The Right Thing And Hit Your Dates
maggiecrowley
38
2.9k
The Language of Interfaces
destraynor
162
25k
The Cult of Friendly URLs
andyhume
79
6.7k
Keith and Marios Guide to Fast Websites
keithpitt
412
23k
Designing Experiences People Love
moore
142
24k
How to Think Like a Performance Engineer
csswizardry
27
2.2k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.3k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
3.2k
Typedesign – Prime Four
hannesfritz
42
2.9k
Visualization
eitanlees
150
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