cyberagent.connpass.com
本日はサイバーエージェント さんのFlutterエンジニアによる勉強会、 CA.flutter #1 に参加しました!会場はサイバーエージェント さんのアベマタワーズ でした。
オンラインでの配信もされていて、YouTube Live で配信内容が見れるようになっているのもとても良かったです!(巻き戻しもできたのでブログまとめたい勢としては聞き逃した部分を確認できるのでめちゃくちゃありがたい……!)
VIDEO www.youtube.com
質問は sli.do でリアルタイムに受け付けられていたのもとても親切な設計だと感じました!質問もすべて読み上げられないほどめちゃくちゃ盛り上がっていました…!
app.sli.do
以下発表内容になります!
複雑な画面遷移を賢く管理する: AutoRouteGuardの活用法
発表者の ひろ(Hirokazu Tanaka) さんは、2023年新卒入社で、株式会社WinTicketでFlutterアプリ開発 を行ってらっしゃるそうです。
auto_route
は Flutter のナビゲーションパッケージのひとつで、2023年現在、 go_router
に次いで人気のあるパッケージです。
Winticketでは、2021年3月にFlutter製アプリにリプレースした当初から auto_route
を使用されてらっしゃるそうです。
auto_route
の特徴的な機能として、 AutoRouteGuard
があります。 AutoRouteGuard
では、AutoRouteGuard
クラスを継承したクラスを定義して、ルーティングのよくある複雑さを解消しています。
たとえば、公式の以下の例では、画面遷移時に、未ログイン時はナビゲーションを中断してログインページに遷移、ログインが成功した場合のみだけ本来行おうとした遷移を実行、失敗した場合は中止しています。
class AuthGuard extends AutoRouteGuard {
@override
void onNavigation (NavigationResolver resolver, StackRouter router) {
if (authenticated) {
resolver.next (true );
} else {
resolver.redirect (LoginRoute (onResult: (success) {
resolver.next (success);
}));
}
}
}
たとえばWinticketではVIPユーザーと呼ばれるユーザーには、 /campaign/vip
に遷移させるということをさせていることがあるようです。具体的には、以下のようにAutoRouteを定義しているそうです。
AutoRoute (
path: '/campaign/vip' ,
page: SecretVIPPage ,
guards: [
IsLoginGuard ,
IsVerificationGuard ,
IsVIPGuard ,
]
),
これによって、ログイン済み、本人確認済み、VIPユーザーの条件を満たす、という3つのガードを画面遷移に適用して、さらにVIPユーザーでない場合はそれ用のページに飛ばす、といった動作が実現できているようです(こういう形で組み合わせできるのはいいですね)。
また、WinTicketではフィーチャーフラグを使ったtrunk-basedなブランチ運用をされているようなのですが、フィーチャーフラグの状態を判定するのにも AutoRouteGuard
を使われているそうです。
また、キーボードが閉じられるまで画面遷移を遅延させる AutoRouteGuard
も定義されているようです(これも面白い)(閉じる判定 WidgetsBinding.instance.window.viewInsets.bottom == 0
でいいんだ…)。
個人的には、ナビゲーションには go_router
か素のナビゲーションを使うことが多いのですが、go_router
にも未ログイン時にログインページにリダイレクトさせるような機能(参考 )があるようで、使ったことがなかったので気づける良いきっかけになりました!
質問コーナー
Q. go_router
ではなく auto_router
を選定したのはなぜ?
当時の判断らしい
発表者の なるお(naruogram) さんは、2024年入社予定。サイバーエージェント 24卒内定者の方で、Ameba マンガに所属。Flutter だけでなく、 iOS アプリの開発も行ってらっしゃるそうです。
UIの品質とは、「そのサービスがユーザーにとってどれだけ使いやすく、理解しやすく、効率的であるか」と定義されているようで、具体的には以下のチェックリストが考えられるとのことです。
なるおさんの所属する Ameba マンガ では、 UIの品質を Visual Regression Test を使って、改善しているそうです。テストツールは playbook-ui/playbook-flutter
というサイバーエージェント の方が関わられてるツールと、 reg-suit を使っているそうです。
github.com
VRT では、以下のような点をチェックしているそうです。
異なるデバイス でもUIが崩れないか→VRTテストにおいてテストする端末を増やす
異なる状況でもUIが崩れないか→長い文字列や通信環境が悪くて画像が取得できなかったケースのStoryを用意する
端末の文字サイズによってUIが崩れないか→ TextScaleFactor
の値を設定し、複数パターンの文字サイズをテストする
また、 integration_test
を使ってパフォーマンス計測を行う取り組みもやってらっしゃるそうです。
await binding.traceAction(
() async {
await tester.scrollUntilVisible(
itemFinder,
500.0,
scrollable: listFinder,
);
},
reportKey: 'scrolling_timeline',
);
計測するためには、Widget Test のコード上で、 IntegrationTestWidgetsFlutterBinding.ensureinitialized()
で取得したインスタンス に対して traceAction()
を使用し、引数に reportKey
を文字列で指定します。
import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart' ;
Future < void > main () {
return integrationDriver (
responseDataCallback: (data) async {
if (data != null ) {
final timeline = driver.Timeline .fromJson (
data['scrolling_timeline' ] as Map < String , dynamic > ,
);
final summary = driver.TimelineSummary .summarize (timeline);
await summary.writeTimelineToFile (
'scrolling_timeline' ,
pretty: true ,
includeSummary: true ,
);
}
},
);
}
また、上記のテストドライバーを別ファイルに保存します。
もしテスト自体を integration_test/scrolling_test.dart
、テストドライバーをtest_driver/perf_driver.dart
というパスで保存していた場合、テストドライバーを指定して、以下のように実行するようです。
flutter drive \
--driver=test_driver/perf_driver.dart \
--target=integration_test/scrolling_test.dart \
--profile
さらに、おそらく上記例の summarize のかわりに summaryJson というプロパティを使用すると、平均/最長フレームビルド時間、平均/最長フレーム描画時間、平均CPU使用率、平均メモリ使用率などをJSON 形式で出力することができるようです。(これはやってみたい、、!)
懇親会で登壇者のなるおさんに質問させていただいたのですが、公式の下記の Performance profiling
という記事が参考になりそうです。
docs.flutter.dev
個人的には、Flutter で VRT や PlayBook をガッツリ使った事例は貴重だと思うのでとても聞けてよかったです!また、 integration_test
でプロファイリングするのはぜひやってみたい!
発表者のKoki Masuyama(masssun) さんは、今年1月に中途入社されて、AI 事業本部アプリ運用カンパニーというところでモバイルアプリチームのリードエンジニアをされているそうです。
アプリ運用カンパニーは小売り企業の方々と協業して、アプリを中心としたデジタルでの購買体験の実現を行う事業部とのことでした。
アクセシビリティ の重要性からはじまり、まずエンジニア主導で行える Flutter におけるアクセシビリティ 対応としては、
の対応がありそうとのことでした。
スクリーンリーダー
Widget ツリーに、「このテキストは何であるか」「ボタンをタップすると何が起こるのか」などの情報を付与します。
たとえば具体的には、Semantics
ウィジェット で囲んで label を設定するということができるようです。
ただ、AppBar
, Text
など標準Widget の多くはそもそも Semantics
でラップされているようなので、使っていれば対応が済んでいるとのことでした。
そのため、Flutterでは特別にアクセシビリティ 対応を行わなくても、スクリーンリーダーが対応ができる範囲が広いようです。しかし、それを踏まえた上で、いくつかのTipsがあるようでした。
cached_network_image
を使用している画像について、 Semantics
で囲む (Image の場合は semanticsLabel という引数がある)
複数のスタイルが適用されている Text
は、別の Text
として認識されてしまうが、まとめたい場合は MergeSemantics
や RichText
などを使う
アイコン付きのボタンについて、アイコン部分については読み上げさせたくないので、ExcludeSemantics
ウィジェット でアイコン部分を囲む
文字サイズ
Text ウィジェット を使っていれば、フォントサイズはOSの設定に基づいてFlutterが自動的に計算してくれるので、これも特別な設定は必要なさそうとのことでした。
しかし、Layout overflow を起こさないような設計にする必要はあるとのことでした。そのための対応として、
SizedBox
で width, height を固定にしない
ConstrainedBox
で minWidth, minHeight を適切に設定
Column
の children に設定されているWidget は Wrap/Extended でラップすることで、画面端で改行されるようにする
などの工夫を行っているそうです。
個人的には、アクセシビリティ はどうしても自分が当事者でないときに見落としがちな部分なので、Flutterでの具体的な事例や、気をつける観点などを知れる良い機会になったなと思いました!
おわりに
今回は CA.flutter の1回目の開催で、発表者の方々は新卒1年目、中途1年目、内定者の方ということでフレッシュな感じかと思いきや、かなり腕利きの方々という感じでとても勉強になりました!また、今所属しているYOUTRUSTでやっている勉強会(12/9(金)に第4回目をやります 今週 11/17(金) 13:00〜募集開始です)や、FlutterKaigi でお話した人とまた懇親会でお話できたりする機会になったのも嬉しかったです。また機会があれば参加したいです!