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を言い訳にしない!アプリの使い心地改善テクニック5選🔥
Search
Kuno Ayana
November 20, 2024
Programming
3
750
Flutterを言い訳にしない!アプリの使い心地改善テクニック5選🔥
FlutterKaigi 2024 の LT です!
Kuno Ayana
November 20, 2024
Tweet
Share
More Decks by Kuno Ayana
See All by Kuno Ayana
ぬるぬる動かせ! Riveでアニメーション実装🐾
kno3a87
1
1.4k
Dart 参戦!!静的型付き言語界の隠れた実力者
kno3a87
0
220
iOS 18 がやってきた!
kno3a87
1
220
おうちハッカソン #2
kno3a87
0
130
ミクアカ成果報告会
kno3a87
0
35
SXSW2021
kno3a87
0
48
ミクアカ中間発表会
kno3a87
0
24
大学院進学ガイダンス
kno3a87
0
85
画像処理論セミナー7-1-3
kno3a87
0
24
Other Decks in Programming
See All in Programming
CSC305 Lecture 01
javiergs
PRO
1
400
Local Peer-to-Peer APIはどのように使われていくのか?
hal_spidernight
2
450
なぜあの開発者はDevRelに伴走し続けるのか / Why Does That Developer Keep Running Alongside DevRel?
nrslib
3
370
Web技術を最大限活用してRAW画像を現像する / Developing RAW Images on the Web
ssssota
2
1.2k
ポスターセッション: 「まっすぐ行って、右!」って言ってラズパイカーを動かしたい 〜生成AI × Raspberry Pi Pico × Gradioの試作メモ〜
komofr
0
950
CSC305 Lecture 04
javiergs
PRO
0
250
AIで開発生産性を上げる個人とチームの取り組み
taniigo
0
130
デミカツ切り抜きで面倒くさいことはPythonにやらせよう
aokswork3
0
190
iOSアプリの信頼性を向上させる取り組み/ios-app-improve-reliability
shino8rayu9
0
150
AIエージェント時代における TypeScriptスキーマ駆動開発の新たな役割
bicstone
4
1.5k
NetworkXとGNNで学ぶグラフデータ分析入門〜複雑な関係性を解き明かすPythonの力〜
mhrtech
3
1k
Catch Up: Go Style Guide Update
andpad
0
170
Featured
See All Featured
What’s in a name? Adding method to the madness
productmarketing
PRO
23
3.7k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
23
1.5k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
[RailsConf 2023] Rails as a piece of cake
palkan
57
5.9k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
132
19k
How to Think Like a Performance Engineer
csswizardry
27
2k
Fireside Chat
paigeccino
40
3.7k
GitHub's CSS Performance
jonrohan
1032
460k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
Java REST API Framework Comparison - PWX 2021
mraible
33
8.8k
Building a Modern Day E-commerce SEO Strategy
aleyda
43
7.7k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
16k
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ӡӦελοϑ