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
1.5k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Flutter Hooks を使ったアプリ開発 / App Development with the Flutter Hooks
CyberAgent Developer Conference 2022
Daichi Furiya (Wasabeef)
March 23, 2022
More Decks by Daichi Furiya (Wasabeef)
See All by Daichi Furiya (Wasabeef)
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
6
2.9k
About Flutter Architecture
wasabeef
1
330
2023 Flutter/Dart Summary
wasabeef
0
140
I/O Extended 2023 - Dart と Flutter の新機能
wasabeef
0
240
I/O Extended 2023 - Flutter 活用事例
wasabeef
10
3.1k
What it Takes to be a Flutter Developer
wasabeef
0
260
FlutterKaigi 2022 Keynote
wasabeef
1
750
Flutter 2021 の振り返りと今後のアプリ開発に向けて / Looking back on Flutter 2021 and for future app development.
wasabeef
4
2.3k
Flutter Hooks, sometimes Jetpack Compose
wasabeef
3
1.9k
Other Decks in Programming
See All in Programming
初めての模倣学習とVLA
natsutan
0
310
書籍「プロフェッショナルAI駆動開発」紹介スライド
juntaromatsumoto
0
770
AI Engineeringは、AIプロダクトだけのものか? 〜AIがソフトウェアを作る時代の新しい当たり前〜 / No AI in your product. AI Engineering in your development.
rkaga
5
550
Discordを用いたラボオートメーション関連情報収集の自動化
noguhiro2002
0
460
30年振りにコンパイラの定数整数除算を改善した
herumi
9
4.3k
Go 1.27 における memory allocation の高速化
andpad
0
340
Jindong: Introducing Declarative Haptics in Compose Multiplatform
l2hyunwoo
0
130
いまどきの Codex で開発する visionOS アプリの開発スタイルについて
karad
0
170
AIに既存システムを理解させる技術 ~レガシーを見捨てないハーネスエンジニアリング入門~
ochtum
0
170
高専キャリア LT 発表内容
crysta1221
3
4k
DynamoDBの基礎を振り返りながらベクトル検索機能を理解する
musan
2
240
【QA Test Talk Vol.8】AI-DLC による Whole Team Approach の加速
pkshadeck
PRO
0
250
Featured
See All Featured
Being A Developer After 40
akosma
91
590k
What does AI have to do with Human Rights?
axbom
PRO
1
2.3k
How to train your dragon (web standard)
notwaldorf
97
6.8k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Visualization
eitanlees
152
17k
Evolving SEO for Evolving Search Engines
ryanjones
0
270
How to Think Like a Performance Engineer
csswizardry
28
2.7k
Practical Orchestrator
shlominoach
191
12k
Navigating Weather and Climate Data
rabernat
0
490
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
Google's AI Overviews - The New Search
badams
0
1.6k
A better future with KSS
kneath
240
18k
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