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
760
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.5k
Dart 参戦!!静的型付き言語界の隠れた実力者
kno3a87
0
220
iOS 18 がやってきた!
kno3a87
1
220
おうちハッカソン #2
kno3a87
0
140
ミクアカ成果報告会
kno3a87
0
35
SXSW2021
kno3a87
0
49
ミクアカ中間発表会
kno3a87
0
24
大学院進学ガイダンス
kno3a87
0
85
画像処理論セミナー7-1-3
kno3a87
0
24
Other Decks in Programming
See All in Programming
Foundation Modelsを実装日本語学習アプリを作ってみた!
hypebeans
1
130
技術的負債の正体を知って向き合う
irof
0
260
SODA - FACT BOOK(JP)
sodainc
1
8.8k
デミカツ切り抜きで面倒くさいことはPythonにやらせよう
aokswork3
0
260
あなたとKaigi on Rails / Kaigi on Rails + You
shimoju
0
190
Server Side Kotlin Meetup vol.16: 内部動作を理解して ハイパフォーマンスなサーバサイド Kotlin アプリケーションを書こう
ternbusty
3
250
EMこそClaude Codeでコード調査しよう
shibayu36
0
430
Claude Agent SDK を使ってみよう
hyshu
0
1.4k
contribution to astral-sh/uv
shunsock
0
540
GC25 Recap: The Code You Reviewed is Not the Code You Built / #newt_gophercon_tour
mazrean
0
110
ドメイン駆動設計のエッセンス
masuda220
PRO
2
140
Go言語の特性を活かした公式MCP SDKの設計
hond0413
2
530
Featured
See All Featured
Rails Girls Zürich Keynote
gr2m
95
14k
It's Worth the Effort
3n
187
28k
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
Mobile First: as difficult as doing things right
swwweet
225
10k
Documentation Writing (for coders)
carmenintech
75
5.1k
Six Lessons from altMBA
skipperchong
29
4k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.7k
The Pragmatic Product Professional
lauravandoore
36
7k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
30
2.9k
Producing Creativity
orderedlist
PRO
347
40k
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ӡӦελοϑ