Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Flutterを言い訳にしない!アプリの使い心地改善テクニック5選🔥
Search
Kuno Ayana
November 20, 2024
Programming
3
370
Flutterを言い訳にしない!アプリの使い心地改善テクニック5選🔥
FlutterKaigi 2024 の LT です!
Kuno Ayana
November 20, 2024
Tweet
Share
More Decks by Kuno Ayana
See All by Kuno Ayana
iOS 18 がやってきた!
kno3a87
1
160
おうちハッカソン #2
kno3a87
0
110
ミクアカ成果報告会
kno3a87
0
20
SXSW2021
kno3a87
0
37
ミクアカ中間発表会
kno3a87
0
12
大学院進学ガイダンス
kno3a87
0
72
画像処理論セミナー7-1-3
kno3a87
0
14
内定者自己紹介LT
kno3a87
0
68
画像処理論セミナー7-1,2
kno3a87
0
12
Other Decks in Programming
See All in Programming
As an Engineers, let's build the CRM system via LINE Official Account 2.0
clonn
1
620
[FlutterKaigi2024] Effective Form 〜Flutterによる複雑なフォーム開発の実践〜
chocoyama
1
3.9k
イマのCSSでできる インタラクション最前線 + CSS最新情報
clockmaker
5
3.8k
The Efficiency Paradox and How to Save Yourself and the World
hollycummins
1
120
Java 23の概要とJava Web Frameworkの現状 / Java 23 and Java web framework
kishida
2
370
TypeScript でバックもやるって実際どう? 実運用で困ったこと3選
yuichiro_serita
17
7.5k
Arm移行タイムアタック
qnighy
0
400
Leveling Up Developer Tooling for the Modern Rails & Hotwire Era @ Ruby Türkiye, November 2024
marcoroth
0
150
カンファレンスでLTしました / kashiwarb5
nhayato
0
100
tidymodelsによるtidyな生存時間解析 / Japan.R2024
dropout009
0
210
聞き手から登壇者へ: RubyKaigi2024 LTでの初挑戦が 教えてくれた、可能性の星
mikik0
1
150
DevTools extensions で 独自の DevTool を開発する | FlutterKaigi 2024
kokiyoshida
0
320
Featured
See All Featured
A Philosophy of Restraint
colly
203
16k
Imperfection Machines: The Place of Print at Facebook
scottboms
266
13k
Typedesign – Prime Four
hannesfritz
40
2.4k
Site-Speed That Sticks
csswizardry
1
120
Intergalactic Javascript Robots from Outer Space
tanoku
269
27k
5 minutes of I Can Smell Your CMS
philhawksworth
202
19k
VelocityConf: Rendering Performance Case Studies
addyosmani
326
24k
Embracing the Ebb and Flow
colly
84
4.5k
Designing the Hi-DPI Web
ddemaree
280
34k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
We Have a Design System, Now What?
morganepeng
51
7.2k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.4k
Transcript
ʙΞϓϦͷ͍৺վળςΫχοΫબʙ 'MVUUFSΛݴ͍༁ʹ͠ͳ͍ʂ
None
ͳΜ͔εΫϩʔϧ͢Δͱ ΧΫπΫΜ͚ͩͲʜ
ͳΜ͔εΫϩʔϧ͢Δͱ ΧΫπΫΜ͚ͩͲʜ ͔ͣͬ͠ͱݟͯΔͱ ͕͘ͳͬͯΔؾ͕͢Δʂ
None
ಈը͕දࣔ͞Ε͍ͯΔͱ͖ʹͦ͏
None
˞J1IPOF J04 Λ༻
None
Ƃō
None
5FYUVSF8JEHFU͏ͱ ݹΊͷJ1IPOFͰύϑΥʔϚϯε͕ൃੜ 1MBUGPSN7JFXΛ͓͏ʂ
None
ݱࡏͷϏσΦϑϨʔϜͷ(16্ͷϐΫηϧόοϑΝʔΛ ຖϑϨʔϜ$16ʹίϐʔ͍ͯ͠Δ ͦΕ͕ॏͦ͏ʁ
1MBUGPSN7JFX͔͓ͭ?@?
None
ݟ͚ͭͨʂʂʂ ˞࣮ࡍͷϓϩμΫτͰɼωΠςΟϒଆͰ)-4ϓϨΠϦετͷඇಉظϩʔυ͕ඞཁͩͬͨΓ )%3ରԠ͕ඞཁͩͬͨΓͨ͠ͷͰɼͪ͜ΒͷϥΠϒϥϦΛࢀߟʹϑϧεΫϥονͯ͠·͢
˞J1IPOF J04 Λ༻
None
ͳΜ͔ΞΠίϯઃఆ ө͞ΕΔ·Ͱ͘ͳ͍ʁ
None
UP#ZUF%BUBͰQOHΛ%BSU7.Ͱ Τϯίʔυ͢Δͱ͜ΖͰ͕͔͔࣌ؒͬͯΔ
ϥΠϒϥϦଆͰը૾ʹͤͣ 3(#"όΠτͷ··ฦ͢Α͏ʹϥΠϒϥϦΛվ ωΠςΟϒଆͰ3(#"ΛΫϩοϓͯ͠ը૾Խ͢ΔΑ͏ʹʂ
None
None
J1IPOF࣌ܭͷͱ͜λοϓͰϦετ͕ Ұ൪্·Ͱͬͯཉ͍͠Μ͚ͩͲʜ
None
Ұ൪্·ͰεΫϩʔϧͯ͘͠Εͳ͍ʜ
4DB ff PME -JTU7JFX
4DB ff PME -JTU7JFX 1SJNBSZ4DSPMM$POUSPMMFS
4DB ff PME -JTU7JFX 1SJNBSZ4DSPMM$POUSPMMFS 1SJNBSZ4DSPMM$POUSPMMFS
4DB ff PME -JTU7JFX 1SJNBSZ4DSPMM$POUSPMMFS 4DSPMM$POUSPMMFS ԣʹεΫϩʔϧόʔΛग़ͨ͠Γ Ϧετඌ·ͰεΫϩʔϧ͞ΕͨݕΛ͢ΔͨΊՃ
4DB ff PME -JTU7JFX 1SJNBSZ4DSPMM$POUSPMMFS 4DSPMM$POUSPMMFS εΫϩʔϧόʔΛग़ͨ͠Γ Ϧετඌ·ͰεΫϩʔϧ͞ΕͨݕΛ͢ΔͨΊՃ εςʔλεόʔλοϓͷݕ ͬͪ͜ʹ͘Δʂ
4DB ff PME -JTU7JFX 1SJNBSZ4DSPMM$POUSPMMFS 4DSPMM$POUSPMMFS εΫϩʔϧόʔΛग़ͨ͠Γ Ϧετඌ·ͰεΫϩʔϧ͞ΕͨݕΛ͢ΔͨΊՃ εςʔλεόʔλοϓͷݕ ͬͪ͜ʹ͘Δʂ
ແԠʂ
4DB ff PME -JTU7JFX 1SJNBSZ4DSPMM$POUSPMMFS 4DSPMM$POUSPMMFS εΫϩʔϧόʔΛग़ͨ͠Γ Ϧετඌ·ͰεΫϩʔϧ͞ΕͨݕΛ͢ΔͨΊՃ εςʔλεόʔλοϓͷݕ ͬͪ͜ʹ͘Δʂ
ແԠʂ 1SJNBSZ4DSPMM$PUSPMMFSͷ λοϓݕΛΩϟον͢Εྑ͍ʂ
class _FakeScrollPositionWithSingleContext extends ScrollPositionWithSingleContext { _FakeScrollPositionWithSingleContext({ required BuildContext context, required
Future<void> Function() callback, }) : _callback = callback, super( physics: const NeverScrollableScrollPhysics(), context: _FakeScrollContext(context), ); final Future<void> Function() _callback; @override Future<void> animateTo( double to, { required Duration duration, required Curve curve, }) { return _callback.call(); } }
class _FakeScrollPositionWithSingleContext extends ScrollPositionWithSingleContext { _FakeScrollPositionWithSingleContext({ required BuildContext context, required
Future<void> Function() callback, }) : _callback = callback, super( physics: const NeverScrollableScrollPhysics(), context: _FakeScrollContext(context), ); final Future<void> Function() _callback; @override Future<void> animateTo( double to, { required Duration duration, required Curve curve, }) { return _callback.call(); } } ͕ࣗͨͪͨ͠ ίʔϧόοΫ͕ݺΕΔΑ͏ʹ
class _FakeScrollPositionWithSingleContext extends ScrollPositionWithSingleContext { _FakeScrollPositionWithSingleContext({ required BuildContext context, required
Future<void> Function() callback, }) : _callback = callback, super( physics: const NeverScrollableScrollPhysics(), context: _FakeScrollContext(context), ); final Future<void> Function() _callback; @override Future<void> animateTo( double to, { required Duration duration, required Curve curve, }) { return _callback.call(); } } ͕ࣗͨͪͨ͠ ίʔϧόοΫ͕ݺΕΔΑ͏ʹ 4DSPMM1PTJUJPO8JUI4JOHMF$POUFYUΛ ܧঝِͨ͠Λ࡞Δʂ
final primaryScrollController = PrimaryScrollController.maybeOf(Navigator.of(context).context) ?? PrimaryScrollController.maybeOf(context); if (primaryScrollController == null)
return; final scrollPositionWithSingleContext = _FakeScrollPositionWithSingleContext( context: context, callback: widget.onTap, ); primaryScrollController.attach(scrollPositionWithSingleContext);
final primaryScrollController = PrimaryScrollController.maybeOf(Navigator.of(context).context) ?? PrimaryScrollController.maybeOf(context); if (primaryScrollController == null)
return; final scrollPositionWithSingleContext = _FakeScrollPositionWithSingleContext( context: context, callback: widget.onTap, ); primaryScrollController.attach(scrollPositionWithSingleContext); ͦͷِΛ 1SJNBSZ4DSPMM$POUSPMMFSʹΞλονʂ
None
None
J1IPOFඪ४ͱൺͯ ֆจࣈͷදࣔখ͘͞ͳ͍ʁ
None
None
SwiftUI Flutter
SwiftUI Flutter
SwiftUI Flutter খ͍͞Θʂʂʂ
ྗٕͰ ղܾ
import 'package:flutter/material.dart'; class EmojiUtil { static const r = r'[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-
\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED- \u23EF\u23F1\u23F2\u23F8- \u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600- \u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E \u262F\u2638-\u263A\u2640\u2642\u2648- \u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694 - \u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4 \u26C8\u26CF\u26D1\u26E9\u26F0- \u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D \u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05- \u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?| [\u261D\u270C\u270D](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\u270A\u270B](?: \uD83C[\uDFFB-\uDFFF])?|[\u23E9- \u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD \u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]| \u26D3\uFE0F?(?:\u200D\uD83D\uDCA5)?|\u26F9(?:\uFE0F|\uD83C[\uDFFB-
text.splitMapJoin( EmojiUtil.regex, onMatch: (m) { final emojiCharacters = m.group(0)!.characters; for
(final emojiCharacter in emojiCharacters) { state._add(emojiCharacter, true); } return ''; }, onNonMatch: (s) { if (s == '\u200D') { // ZeroWidthJoinerֆจࣈͱͯ͠ొ͓ͯ͘͠ state._add(s, true); } else { state._add(s, false); } return ''; }, ); return state._build(); } }
text.splitMapJoin( EmojiUtil.regex, onMatch: (m) { final emojiCharacters = m.group(0)!.characters; for
(final emojiCharacter in emojiCharacters) { state._add(emojiCharacter, true); } return ''; }, onNonMatch: (s) { if (s == '\u200D') { // ZeroWidthJoinerֆจࣈͱͯ͠ొ͓ͯ͘͠ state._add(s, true); } else { state._add(s, false); } return ''; }, ); return state._build(); } } ड͚औͬͨςΩετΛ ֆจࣈͱจࣈʹׂ
TextStyle _emojiStyle(TextStyle textStyle, double originalFontSize) { if (Platform.isAndroid) { return
textStyle; } else { return textStyle.copyWith( fontSize: textStyle.fontSize! * 1.4, fontFamilyFallback: [], height: 1.0, ); } } ͱΓ͋͑ͣഒʹͯ͠ΈΔʂ
Before After
࣮ࡍͷϓϩμΫτͰͪ͜ΒͷJTTVFΛࢀߟʹɼϑΥϯτͷେ͖͞ʹΑͬͯֆจࣈͷഒΛม͑ͨΓ จࣈͱ֦େֆจࣈͷηϯλʔϥΠϯΛἧ͑ͨΓ͍ͯ͠·͢ʂ
None
ͳΜ͔J1IPOFͩͱ จࣈ͕ࡉͯ͘ݟʹ͍͘ؾ͕͢Δʜ
SwiftUI Flutter
None
None
ϑΥϯτϨϯμϦϯά࣌ͷ ΞϯνΤΠϦΞεͷೱ͕ωΠςΟϒͱҧ͏ʂ
ྗٕͰ ղܾ
return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text(widget.title), ), body:
const Center( child: Stack( children: [ Text( 'Hello, FlutterKaigi 2024 LT!', style: TextStyle(fontSize: 24), ), Text( 'Hello, FlutterKaigi 2024 LT!', style: TextStyle(fontSize: 24), ), ], ), ), );
ild: Stack( children: [ Text( 'Hello, FlutterKaigi 2024 LT!', style:
TextStyle(fontSize: 24), ), Text( 'Hello, FlutterKaigi 2024 LT!', style: TextStyle(fontSize: 24), ),
Before After
˞࣮ࡍͷϓϩμΫτͰͪ͜ΒͷJTTVFίϝϯτIUUQTHJUIVCDPN fl VUUFS fl VUUFSJTTVFTJTTVFDPNNFOUΛࢀߟʹ ͨͩ4UBDLͰॏͶΔͷͰͳ͘4USPLFΛௐͯͨ͠Γ͠·͢ʂ
Sample GitHub: https://github.com/kno3a87/flutterkaigi-2024-lt ͓ΘΓ
,VOP"ZBOB גࣜձࣾ.*9*ͨΜΆΆࣨ 'MVUUFS,BJHJӡӦελοϑ