Slide 1

Slide 1 text

気をつけたい!Desktop対応で 陥りやすい罠とその対策 後藤孝輔

Slide 2

Slide 2 text

自己紹介 - 後藤 孝輔 (@Gotchi0001) - 2023年新卒でスタディプラス株式会社に入社 - クライアントグループでFlutterアプリの開発 2

Slide 3

Slide 3 text

はじめに 3

Slide 4

Slide 4 text

Studyplusについて MISSON - 学ぶ喜びをすべての人へ SERVICE - 学習管理型SNS - 受験生の2人に1人が利用 - iOS/Android/Webで展開 4

Slide 5

Slide 5 text

StudyplusアプリのFlutterリプレイス 5 2022 プロジェクト開始

Slide 6

Slide 6 text

Mobileである程度開発したアプリをWebに対応 6 2023/12 2024/11 2022 プロジェクト開始 Studyplus Webリプレイス完了 Studyplus Androidリプレイス完了

Slide 7

Slide 7 text

このセッションについて - MobileのFlutterアプリをWeb対応した際の事例を元に実際に直面した課題とその 解決策をお話しします - ※Desktop対応はWebも含む広義の意味で使用しています - 基本はDesktop全般に関わる話、マルチデバイス対応時に役立つポイントも含みま す 7

Slide 8

Slide 8 text

目次 ● 入力方法について ○ 文字数制限をオーバーしても入力できてしまう ○ 横スクロールができない ● 画面レイアウトについて ○ BottomNavigationBarだと見栄えが悪い ○ 左右の余白が足りずコンテンツが見づらい ○ Gridのアイテムが大きく表示されてしまう ● Web特有の課題 ○ iOS/Safariブラウザでの問題 ■ autofocusでキーボードが表示されない ■ スワイプバック時に余分なページが表示される ○ パッケージ周りの対応 ■ GoogleSignIn ■ FirebaseCrashlytics ■ Drift ○ 絵文字がうまく表示されない ● その他 ○ Widgetのデフォルト表示が違う ● まとめ 9

Slide 9

Slide 9 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 10

Slide 10

Slide 10 text

Mobileプラットフォームの入力方法 操作 - タッチスクリーン 文字入力 - ソフトウェアキーボード 11 入力方法の違い

Slide 11

Slide 11 text

Desktopプラットフォームの入力方法 操作 - マウス - トラックパッド - タッチスクリーン 文字入力 - ハードウェアキーボード 12 入力方法の違い

Slide 12

Slide 12 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 13

Slide 13

Slide 13 text

文字数制限のあるTextField 14 文字数制限をオーバーしても入力できてしまう問題 TextField( …, maxLength: 4, keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], ),

Slide 14

Slide 14 text

Desktopプラットフォームで確認すると... 15 文字数制限をオーバーしても入力できてしまう問題

Slide 15

Slide 15 text

入力言語を切り替えた際に発生...なぜ? 16 文字数制限をオーバーしても入力できてしまう問題

Slide 16

Slide 16 text

問題の要因 17 文字数制限をオーバーしても入力できてしまう問題 これが関係してそう? https://api.flutter.dev/flutter/material/TextField-class.html

Slide 17

Slide 17 text

maxLengthEnforcement maxLengthをどのように適用するかを指定するプロパティ MaxLengthEnforcement 18 文字数制限をオーバーしても入力できてしまう問題 https://api.flutter.dev/flutter/services/MaxLengthEnforcement.html

Slide 18

Slide 18 text

この辺が関係してそう 19 https://github.com/flutter/flutter/blob/728cedc62a795efa6eadd45a3a5d194fcd76023e/packages/flutter/lib/src/services/text_formatter.dart#L525 文字数制限をオーバーしても入力できてしまう問題

Slide 19

Slide 19 text

maxLengthEnforcementにenforcedを設定 TextField( …, maxLength: 4, keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly] maxLengthEnforcement: MaxLengthEnforcement.enforced, ), 20 文字数制限をオーバーしても入力できてしまう問題

Slide 20

Slide 20 text

対応後 21 文字数制限をオーバーしても入力できてしまう問題

Slide 21

Slide 21 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 23

Slide 22

Slide 22 text

横スクロール 24 横スクロールができない問題

Slide 23

Slide 23 text

マウスでスクロールできる手段がない 25 横スクロールができない問題 - マウスホイールでのスクロール❌ - ドラッグによるスクロール❌

Slide 24

Slide 24 text

対応 - マウスホイールによる横スクロールを可能にする →難しかったため断念 - マウスでドラッグによるスクロールを可能にする 26 横スクロールができない問題

Slide 25

Slide 25 text

ドラッグによるスクロール - Flutter2.5からデフォルトでドラッグスクロール可能なデバイスに変更が入っていた https://docs.flutter.dev/release/breaking-changes/default-scroll-behavior-drag#summary 27 横スクロールができない問題

Slide 26

Slide 26 text

マウスによるドラッグスクロールを有効化 MaterialApp( …, scrollBehavior: const MaterialScrollBehavior().copyWith( dragDevices: { PointerDeviceKind.mouse, PointerDeviceKind.touch, }, ), ), 28 横スクロールができない問題

Slide 27

Slide 27 text

対応後 29 横スクロールができない問題

Slide 28

Slide 28 text

画面レイアウトについて 30

Slide 29

Slide 29 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 31

Slide 30

Slide 30 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 32

Slide 31

Slide 31 text

BottomNavigationBar 複数の主要な画面や機能を持つアプリ ↓ 機能間を直感的でわかりやすく、素早く移動したい ↓ BottomNavigationBarを採用 33 BottomNavigationBarだと見栄えが悪い問題

Slide 32

Slide 32 text

そのままDesktopで使うと... 34 BottomNavigationBarだと見栄えが悪い問題

Slide 33

Slide 33 text

使い勝手が悪くなる 35 BottomNavigationBarだと見栄えが悪い問題

Slide 34

Slide 34 text

NavigationRail https://www.youtube.com/watch?v=y9xchtVTtqQ https://api.flutter.dev/flutter/material/NavigationRail-class.html 36 BottomNavigationBarだと見栄えが悪い問題

Slide 35

Slide 35 text

NavigationRailを導入 38 BottomNavigationBarだと見栄えが悪い問題

Slide 36

Slide 36 text

Mobileプラットフォームでの表示 39 BottomNavigationBarだと見栄えが悪い問題

Slide 37

Slide 37 text

画面サイズに応じてNavigationを切り替える - M3のWindow class(width)を 基準に分岐 - NavigationBarと NavigationRailを出し分け - NavigationRailのextendedと labelTypeの変更 40 Scaffold( body: Row( children: [ if (screenSize != ScreenSize.compact) NavigationRail( extended: screenSize == ScreenSize.expanded labelType: (screenSize == ScreenSize.medium) ? NavigationRailLabelType.none : null, ), Expanded( child: child, ), ], ), bottomNavigationBar: screenSize == ScreenSize.compact ? NavigationBar(...) : null, ), https://m3.material.io/foundations/layout/applying-layout/window-size-classes#4ee6b7bb-864a-440c-9903-f738cc94fd58 BottomNavigationBarだと見栄えが悪い問題

Slide 38

Slide 38 text

アダプティブなNavigation 41 BottomNavigationBarだと見栄えが悪い問題

Slide 39

Slide 39 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 42 → デザイン要件周りの問題 - NavigationRail最下部への要素配置 - サイドメニューの実装

Slide 40

Slide 40 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 43 → デザイン要件周りの問題 - NavigationRail最下部への要素配置 - サイドメニューの実装

Slide 41

Slide 41 text

デザイナーさんの要望 44 NavigationRail最下部への要素配置問題

Slide 42

Slide 42 text

デザイナーさんの要望 45 NavigationRail最下部への要素配置問題

Slide 43

Slide 43 text

trailingプロパティを使ってみる NavigationRail( selectedIndex: selectedIndex, destinations: [...], onDestinationSelected: (value) {...}, trailing: // これを使ってみる ); 46 理想 NavigationRail最下部への要素配置問題

Slide 44

Slide 44 text

イメージ:Material DesignのWebサイト 47 https://m3.material.io/

Slide 45

Slide 45 text

思ってたのと違う 48 理想 現実 NavigationRail( selectedIndex: selectedIndex, destinations: [...], onDestinationSelected: (value) {...}, trailing: Row( children: [ IconButton( onPressed: () {}, icon: const Icon(Icons.account_circle), ), const SizedBox( width: 8, ), const Text('プロフィール '), ], ), ); NavigationRail最下部への要素配置問題

Slide 46

Slide 46 text

思ってたのと違う 49 理想 現実 NavigationRail( selectedIndex: selectedIndex, destinations: [...], onDestinationSelected: (value) {...}, trailing: Row( children: [ IconButton( onPressed: () {}, icon: const Icon(Icons.account_circle), ), const SizedBox( width: 8, ), const Text('プロフィール '), ], ), ); NavigationRail最下部への要素配置問題

Slide 47

Slide 47 text

最下部に配置するには... 50 NavigationRail最下部への要素配置問題 NavigationRail ( selectedIndex: selectedIndex , destinations: [...], onDestinationSelected: (value) {...}, trailing: Expanded( child: Container( color: Colors.orangeAccent, alignment: Alignment.bottomCenter, width: 256 - 16 * 2, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ IconButton( onPressed: () {}, icon: const Icon(Icons.account_circle), ), const SizedBox( width: 16, ), const Text('プロフィール'), ], ), ), ), ); ←Expandedで伸ばして ←幅を広げて

Slide 48

Slide 48 text

最下部に配置するには... 51 NavigationRail最下部への要素配置問題 NavigationRail ( selectedIndex: selectedIndex , destinations: [...], onDestinationSelected: (value) {...}, trailing: Expanded( child: Container( color: Colors.orangeAccent, alignment: Alignment.bottomCenter, width: 256 - 16 * 2, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ IconButton( onPressed: () {}, icon: const Icon(Icons.account_circle), ), const SizedBox( width: 16, ), const Text('プロフィール'), ], ), ), ), ); ←ここも可変にする必要がある

Slide 49

Slide 49 text

最下部に配置する場合 - そのほかの方法についてもコードが複雑化してしまう - FlutterのNavigationRailにおいてはその位置に配置することが想定されていない 52 NavigationRail最下部への要素配置問題

Slide 50

Slide 50 text

最終的に... デザイナーさんに相談 →一番上に配置するデザインに変更 53 最下部への要素配置がうまくいかない問題

Slide 51

Slide 51 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 54 → デザイン要件周りの問題 - NavigationRail最下部への要素配置 - サイドメニューの実装

Slide 52

Slide 52 text

デザイナーさんの要望 55 サイドメニューの実装問題

Slide 53

Slide 53 text

デザイナーさんの要望 56 サイドメニューの実装問題

Slide 54

Slide 54 text

どう実現する? 57 サイドメニューの実装問題

Slide 55

Slide 55 text

ここからDrawerを出せればいけそう? 58 サイドメニューの実装問題

Slide 56

Slide 56 text

Drawerを表示させてみる 59 サイドメニューの実装問題

Slide 57

Slide 57 text

NavigationRailに重なる形で出入りしてしまう 60 サイドメニューの実装問題

Slide 58

Slide 58 text

問題となった点 - Drawerが出入りする際にNavigationRailに重なってしまう - Drawerのアニメーション 61 サイドメニューの実装問題

Slide 59

Slide 59 text

Drawerが出入りする際にNavigationRailに重なってしまう - Stackを使って無理やり解決できなくは無いが、 コードが複雑化してしまう 62 サイドメニューの実装問題 Stack( children: [ Row( children: [ switch (screenSize) { ScreenSize.compact => const SizedBox.shrink(), ScreenSize.medium => const SizedBox( width: 80, ), ScreenSize.expanded => const SizedBox( width: 256, ), }, Expanded( child: Scaffold( key: homeScaffoldKey, drawer: const HomeDrawer(), drawerEdgeDragWidth: 0, body: child, bottomNavigationBar: screenSize == ScreenSize.compact ? HomeNavigationBar( currentPage: currentPage, ) : null, ), ), ], ), if (screenSize != ScreenSize.compact) Row( children: [ HomeNavigationRail( currentPage: currentPage, ), const Spacer(), ], ), ], ) ←NavigationRailの幅分スペースを開ける必要

Slide 60

Slide 60 text

Drawerのアニメーション - 無くしたりカスタムしたりするのが厳しそう - https://github.com/flutter/flutter/issues/47316 63 サイドメニューの実装問題

Slide 61

Slide 61 text

最終的に... デザイナーさんに相談 →PopupMenuで表示する形に落ち着いた 64 サイドメニューの実装問題

Slide 62

Slide 62 text

Desktopに対応したNavigation 65 BottomNavigationBarだと見栄えが悪い問題

Slide 63

Slide 63 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 66

Slide 64

Slide 64 text

左右の余白 67 左右の余白が足りずコンテンツが見づらい問題

Slide 65

Slide 65 text

左右の余白 68 左右の余白が足りずコンテンツが見づらい問題

Slide 66

Slide 66 text

左右の余白 69 左右の余白が足りずコンテンツが見づらい問題 class CreateStudyRecordScreen extends StatelessWidget { const CreateStudyRecordScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(...), body: Padding( padding: EdgeInsets.symmetric( horizontal: 16.0, ), child: ..., ), ); } }

Slide 67

Slide 67 text

左右の余白 70 左右の余白が足りずコンテンツが見づらい問題 class CreateStudyRecordScreen extends StatelessWidget { const CreateStudyRecordScreen({ super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(...), body: Padding( padding: EdgeInsets.symmetric( horizontal: 16.0, ), child: ..., ), ); } }

Slide 68

Slide 68 text

そのままDesktopプラットフォームに持ってくると... 71 左右の余白が足りずコンテンツが見づらい問題

Slide 69

Slide 69 text

コンテンツが見づらくなってしまう 72 左右の余白が足りずコンテンツが見づらい問題

Slide 70

Slide 70 text

breakpoints_mqを導入 https://pub.dev/packages/breakpoints_mq 73 左右の余白が足りずコンテンツが見づらい問題

Slide 71

Slide 71 text

Material Designのbreakpointを元に値を分岐 74 左右の余白が足りずコンテンツが見づらい問題 enum BreakpointScreenSize { /// 0~599 extraSmall, /// 600~904 smallScaleBody , /// 905~1239 smallFixBody , /// 1240~1439 medium, /// 1440~ large, } static double margin(double width) { final size = screenSize(width); switch (size) { case BreakpointScreenSize. extraSmall: return 16; case BreakpointScreenSize. smallScaleBody : return 32; case BreakpointScreenSize. smallFixBody : return (width - 840) / 2; case BreakpointScreenSize. medium: return 200; case BreakpointScreenSize. large: return (width - 1040) / 2; } } https://github.com/koji-1009/breakpoints_mq/blob/2.1.2/lib/src/ext/breakpoints_enum_ext.dart ※弊社ではMaterial Design2のバージョンを利用

Slide 72

Slide 72 text

breakpointMarginの設定 75 左右の余白が足りずコンテンツが見づらい問題 class CreateStudyRecordScreen extends StatelessWidget { const CreateStudyRecordScreen({ super.key}); @override Widget build(BuildContext context) { final margin = MediaQuery.of(context).breakpointMargin; return Scaffold( appBar: AppBar(...), body: Padding( padding: EdgeInsets.symmetric( horizontal: margin, ), child: ..., ), ); } }

Slide 73

Slide 73 text

before 76 左右の余白が足りずコンテンツが見づらい問題

Slide 74

Slide 74 text

after 77 左右の余白が足りずコンテンツが見づらい問題

Slide 75

Slide 75 text

導入後 78 左右の余白が足りずコンテンツが見づらい問題

Slide 76

Slide 76 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字やフォントがうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 79

Slide 77

Slide 77 text

アイテムがグリッド状に並ぶ場合 80 Gridのアイテムが大きく表示されてしまう問題

Slide 78

Slide 78 text

アイテムがグリッド状に並ぶ場合 81 Gridのアイテムが大きく表示されてしまう問題

Slide 79

Slide 79 text

アイテムがグリッド状に並ぶ場合 82 GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, ), itemCount: bookshelfMaterials.length, itemBuilder: (context, index) { return BookshelfItem(...); } ) Gridのアイテムが大きく表示されてしまう問題

Slide 80

Slide 80 text

そのままDesktopプラットフォームに持ってくると... 83 Gridのアイテムが大きく表示されてしまう問題

Slide 81

Slide 81 text

一つ一つが必要以上に大きく表示されてしまう 84 Gridのアイテムが大きく表示されてしまう問題

Slide 82

Slide 82 text

列数を画面幅に応じて可変に 85 const materialWidth = 150; extension BoxConstraintsExt on BoxConstraints { int get crossAxisCount { return maxWidth ~/ materialWidth; } } ... LayoutBuilder( builder: (context, constraints) { return GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: constraints.crossAxisCount, ), itemCount: bookshelfMaterials.length, itemBuilder: (context, index) { return BookshelfItem(...); } } Gridのアイテムが大きく表示されてしまう問題

Slide 83

Slide 83 text

before 86 Gridのアイテムが大きく表示されてしまう問題

Slide 84

Slide 84 text

after 87 Gridのアイテムが大きく表示されてしまう問題

Slide 85

Slide 85 text

画面レイアウト デモ 88

Slide 86

Slide 86 text

Adaptive and responsive design 89 https://docs.flutter.dev/ui/adaptive-responsive

Slide 87

Slide 87 text

Web特有の問題 90

Slide 88

Slide 88 text

質問 91

Slide 89

Slide 89 text

FlutterでWebアプリを作ったことがある方🙋 92

Slide 90

Slide 90 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字がうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 93

Slide 91

Slide 91 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字がうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 94

Slide 92

Slide 92 text

WebアプリをMobileのブラウザで表示 95 iOS/Safariブラウザでの問題

Slide 93

Slide 93 text

キーボードが表示されない - autofocusを設定しても自動でキーボードが表示さ れない →カーソルをタップしないとキーボードが開かない 96 iOS/Safariブラウザでの問題>autofocus時にキーボードが表示されない問題

Slide 94

Slide 94 text

issueを探しに行く🏃 97 https://github.com/flutter/flutter/issues iOS/Safariブラウザでの問題>autofocus時にキーボードが表示されない問題

Slide 95

Slide 95 text

issueを発見 https://github.com/JedWatson/react-select/issues/3501 98 iOS/Safariブラウザでの問題>autofocus時にキーボードが表示されない問題

Slide 96

Slide 96 text

issueによると... - なぜキーボードが出ないのか - Appleが望んでいないらしい 🍎 - https://bugs.webkit.org/show_bug.cgi?id=195884#c4 99 iOS/Safariブラウザでの問題>autofocus時にキーボードが表示されない問題

Slide 97

Slide 97 text

WebかつiOSの場合のみautofocusをfalseに設定する final autoFocus = !(kIsWeb && defaultTargetPlatform == TargetPlatform.iOS); … TextField( …, autofocus: autoFocus, ), 100 autofocus時にキーボードが表示されない問題

Slide 98

Slide 98 text

対応後 101 autofocus時にキーボードが表示されない問題

Slide 99

Slide 99 text

余分なページが表示される - 画面左端から右側へスワイプする動作を行った時 に発生 - 本来表示されるはずのない余分なページが表示さ れる 102 iOS/Safariブラウザでの問題>スワイプバック時に余分なページが表示される問題

Slide 100

Slide 100 text

issueを探しに行く🏃 103 https://github.com/flutter/flutter/issues iOS/Safariブラウザでの問題>スワイプバック時に余分なページが表示される問題

Slide 101

Slide 101 text

issueを発見 104 https://github.com/flutter/flutter/issues/114324 iOS/Safariブラウザでの問題>スワイプバック時に余分なページが表示される問題

Slide 102

Slide 102 text

暫定対応 - 不要なスワイプを防止する JavaScriptファイルを index.htmlに追加 105 https://github.com/flutter/flutter/issues/114324#issuecomment-1725887254 iOS/Safariブラウザでの問題>スワイプバック時に余分なページが表示される問題

Slide 103

Slide 103 text

対応後 106 iOS/Safariブラウザでの問題>スワイプバック時に余分なページが表示される問題

Slide 104

Slide 104 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字がうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 107

Slide 105

Slide 105 text

GoogleSignIn(Mobile) 108 パッケージ周りの対応 >GoogleSignIn

Slide 106

Slide 106 text

GoogleSignInボタン(Mobile) ボタンのUI - flutter_signin_buttonのSignInButton - https://pub.dev/packages/flutter_signin_button 109 パッケージ周りの対応 >GoogleSignIn SignInButton( Buttons.Google, text: text, onPressed: () async { final googleSignIn = GoogleSignIn( scopes: [ 'email', ], clientId: googleClientId , ); if (await googleSignIn.isSignedIn()) { await googleSignIn.signOut(); } final user = await googleSignIn.signIn(); if (user == null) { return; } final googleAuthentication = await user.authentication ; final idToken = googleAuthentication. idToken; ..., } )

Slide 107

Slide 107 text

GoogleSignInボタン(Mobile) 110 パッケージ周りの対応 >GoogleSignIn SignInButton( Buttons.Google, text: text, onPressed: () async { final googleSignIn = GoogleSignIn( scopes: [ 'email', ], clientId: googleClientId , ); if (await googleSignIn.isSignedIn()) { await googleSignIn.signOut(); } final user = await googleSignIn.signIn(); if (user == null) { return; } final googleAuthentication = await user.authentication ; final idToken = googleAuthentication. idToken; ..., } ) サインインフローの開始 - google_sign_inのsignIn()メソッド - https://pub.dev/packages/google_sign_in

Slide 108

Slide 108 text

Webで動作させると... →signIn()メソッドはWebでdeprecatedなので代わりの方法にしてね https://pub.dev/packages/google_sign_in_web#migrating-to-v011-and-v012-google-identity-services 111 うまくいかず、コンソールに以下が表示される

Slide 109

Slide 109 text

google_sign_in_web - ユーザー独自のSign Inボタンを作成する機能や、Sign Inのフローを開始するAPI を提供しなくなった https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_web#what-happened-to-the-signin-method-on-the-web 112 パッケージ周りの対応 >GoogleSignIn

Slide 110

Slide 110 text

代わりにrenderButton()を使う 113 パッケージ周りの対応 >GoogleSignIn class SignInWithGoogleButton extends HookConsumerWidget { const SignInWithGoogleButton({ super.key, ..., }); ... @override Widget build(BuildContext context, WidgetRef ref) { ... return (GoogleSignInPlatform.instance as GoogleSignInPlugin).renderButton(); } } renderButton(): https://github.com/flutter/packages/blob/main/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart#L188

Slide 111

Slide 111 text

サインインフロー完了後のユーザー情報取得 signIn()メソッドの場合 →戻り値として取得 114 final user = await googleSignIn.signIn(); if (user == null) { return; } final googleAuthentication = await user.authentication; final idToken = googleAuthentication.idToken; ..., パッケージ周りの対応 >GoogleSignIn

Slide 112

Slide 112 text

サインインフロー完了後のユーザー情報取得 renderButton()の場合 →GoogleSignIn.onCurrentUserChangedのStreamを監視 115 ref .watch(googleSignInProvider(googleClientId: googleClientId)) .onCurrentUserChanged .listen( (GoogleSignInAccount? user) async { if (user == null) { return; } final googleAuthentication = await user.authentication; final idToken = googleAuthentication.idToken; ..., } パッケージ周りの対応 >GoogleSignIn ※googleSignInProviderはGoogleSingInクラスのインスタンスを公開

Slide 113

Slide 113 text

対応後 116 パッケージ周りの対応 >GoogleSignIn

Slide 114

Slide 114 text

Firebase Crashlytics - クラッシュログの取得に利用 117 パッケージ周りの対応 >FirebaseCrashlytics https://pub.dev/packages/firebase_crashlytics

Slide 115

Slide 115 text

Webは対象プラットフォームに含まれていない 118 パッケージ周りの対応 >FirebaseCrashlytics https://pub.dev/packages/firebase_crashlytics

Slide 116

Slide 116 text

issueはあるが、対応目処は立っていない 119 https://github.com/firebase/flutterfire/issues/1693

Slide 117

Slide 117 text

Sentryを導入 120 パッケージ周りの対応 >FirebaseCrashlytics https://sentry.io/welcome/

Slide 118

Slide 118 text

Sentryを導入 - Flutter側のissue内で代替として名前が上がっていた - https://github.com/firebase/firebase-js-sdk/issues/710 - sentry_flutter - https://pub.dev/packages/sentry_flutter - docs - https://docs.sentry.io/platforms/flutter/ 121 パッケージ周りの対応 >FirebaseCrashlytics

Slide 119

Slide 119 text

Drift - Flutter、Dart向けのデータ永続化ライブラリ - https://pub.dev/packages/drift →弊社では教材情報などの永続化に利用 122 パッケージ周りの対応 >Drift

Slide 120

Slide 120 text

Drift - Flutter、Dart向けのデータ永続化ライブラリ - https://pub.dev/packages/drift →v2.9.0から正式にWebサポート 123 パッケージ周りの対応 >Drift https://drift.simonbinder.eu/platforms/web/

Slide 121

Slide 121 text

DriftのWeb対応 - ローカル環境では問題なく動作した - 本番環境にデプロイすると、期待通りに動作しない →解決に至らず見送る判断に 124 パッケージ周りの対応 >Drift

Slide 122

Slide 122 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字がうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 125

Slide 123

Slide 123 text

絵文字を含んだ文章 126 絵文字がうまく表示されない問題

Slide 124

Slide 124 text

白黒で表示されてしまう 127 絵文字がうまく表示されない問題

Slide 125

Slide 125 text

絵文字のフォント表示が失敗してそう? 128 絵文字がうまく表示されない問題

Slide 126

Slide 126 text

fallbackFontsを設定 - 表示に失敗した場合のフォントを定 義 - TextStyleのfontFamilyFallbackに 設定 final _fallbackFonts = [ GoogleFonts.notoColorEmoji().fontFamily ?? '', ]; ... // themeは任意のTextTheme theme.copyWith( displayLarge: theme.displayLarge?.copyWith( fontFamilyFallback: _fallbackFonts, ), displayMedium: theme.displayMedium?.copyWith( fontFamilyFallback: _fallbackFonts, ), ..., ); 129 絵文字がうまく表示されない問題

Slide 127

Slide 127 text

対応後 130 絵文字やフォントがうまく表示されない問題

Slide 128

Slide 128 text

その他 131

Slide 129

Slide 129 text

目次 - 入力方法について - 文字数制限をオーバーしても入力できてしまう - 横スクロールができない - 画面レイアウトについて - BottomNavigationBarだと見栄えが悪い - 左右の余白が足りずコンテンツが見づらい - Gridのアイテムが大きく表示されてしまう - Web特有の課題 - iOS/Safariブラウザでの問題 - autofocusでキーボードが表示されない - スワイプバック時に余分なページが表示される - パッケージ周りの対応 - GoogleSignIn - FirebaseCrashlytics - Drift - 絵文字がうまく表示されない - その他 - Widgetのデフォルト表示が違う - まとめ 132

Slide 130

Slide 130 text

並び替え可能なリスト 133 Widgetのデフォルト表示が違う問題

Slide 131

Slide 131 text

Desktopプラットフォームで表示すると... 134 Widgetのデフォルト表示が違う問題

Slide 132

Slide 132 text

アイコンが二つ表示されている? 135 Widgetのデフォルト表示が違う問題

Slide 133

Slide 133 text

該当箇所の実装 136 Widgetのデフォルト表示が違う問題 ReorderableListView .builder( itemBuilder: (context, index) { final category = categories. value[index]; return Column( key: Key(category.userCategoryId .toString()), children: [ ListTile( ..., title: Text(category.categoryName), trailing: const Icon( Icons.drag_handle, ), onTap: () {...} ), const Divider(), ], ); } ) drag_handle一個しか無いよなぁ...

Slide 134

Slide 134 text

ReorderableListViewの実装を見ると https://github.com/flutter/flutter/blob/main/packages/flutter/lib/src/material/reorderable_list.dart#L157 137 Widgetのデフォルト表示が違う問題 これか...? でもMobileでは上手く表示されてたよ な

Slide 135

Slide 135 text

ReorderableListViewの実装を見ると 138 Widgetのデフォルト表示が違う問題 Desktopだけ分岐入ってた! ← drag_handleいた https://github.com/flutter/flutter/blob/main/packages/flutter/lib/src/material/reorderable_list.dart#L333

Slide 136

Slide 136 text

buildDefaultDragHandlesをfalseに設定 - ListTileのtrailingにIconを設定 ReorderableListView.builder( buildDefaultDragHandles: false, itemBuilder: (context, index) { return ListTile( ..., trailing: ReorderableDragStartListener( index: index, child: const Icon( Icons.drag_handle, ), ), ); } ) 139 Widgetのデフォルト表示が違う問題

Slide 137

Slide 137 text

対応後 140 Widgetのデフォルト表示が違う問題

Slide 138

Slide 138 text

Widgetのプラットフォームごとの差分 - 該当のWidgetの実装を確認 - TargetPlatformによる分岐で観測 →実装をいちいち見ていくしかないのか? 141 Widgetのデフォルト表示が違う問題

Slide 139

Slide 139 text

小ネタ:仕様差分の探し方 142 Widgetのデフォルト表示が違う問題 https://api.flutter.dev/flutter/flutter_test/TargetPlatformVariant-class.html

Slide 140

Slide 140 text

小ネタ:仕様差分の探し方 143 Widgetのデフォルト表示が違う問題

Slide 141

Slide 141 text

小ネタ:仕様差分の探し方 144 Widgetのデフォルト表示が違う問題

Slide 142

Slide 142 text

小ネタ:仕様差分の探し方 145 Widgetのデフォルト表示が違う問題

Slide 143

Slide 143 text

まとめ 146

Slide 144

Slide 144 text

まとめ - Desktop対応時に起きた問題とその解決策を紹介しました - 入力方法、画面レイアウト、Web特有の問題などがあった - その対応は特別なことではない - Flutterリポジトリのissueを探しに行く - Widgetの各プロパティや実装について詳しくみる - デザイン面で難しければ相談する - 公式のドキュメントや MigrationGuideを読んでその通りに実装する 147

Slide 145

Slide 145 text

ご清聴ありがとうございました 148 資料は後ほどこのアカウントから #FlutterKaigiを付けて公開します