Slide 1

Slide 1 text

はじめてのMaterial3 Expressive chuka / Yume Hakamada 1 DroidKaigi 2025 9/11

Slide 2

Slide 2 text

自己紹介 2 chuka / Yume Hakamada X: @YkOxc 24卒でSTORES 株式会社に入社 STORES 決済 Androidの開発に従事 DroidKaigiスタッフもやってます おいしいちゅうかがたべたい 🦐

Slide 3

Slide 3 text

セッションの目的 ・Material3 Expressiveについてなんとなく理解する ・Jetpack Composeでの実装方法が分かる ・Material3 Expressiveをプロダクトに導入するか  どうかの議論ができる ・Material3 Expressiveを「ちょっと試してみよう」と思える 3

Slide 4

Slide 4 text

セッションの注意 ・本セッションはMaterial3 1.5-alpha03時点での  内容となっております ・今後のアップデートで変更がありうるのでご了承ください ・本セッションは公式ドキュメントやガイドラインなどを  ベースにしていますが、私はデザイナーではありません⚠  Androidエンジニア視点で、実際に触ってみて感じたことを  中心にお話します 4

Slide 5

Slide 5 text

目次 ・Material3 Expressiveとは ・Material3 Expressiveの魅力 ・アップデート概要 ・Component紹介 ・エンジニアの声 ・プロダクト特性とMaterial3 Expressive ・Material3 Expressive導入判断の観点 ・まとめ 5

Slide 6

Slide 6 text

目次 ・Material3 Expressiveとは ・Material3 Expressiveの魅力 ・アップデート概要 ・Component紹介 ・エンジニアの声 ・プロダクト特性とMaterial3 Expressive ・Material3 Expressive導入判断の観点 ・まとめ 6

Slide 7

Slide 7 text

Material3 Expressiveとは 2025年5月にGoogle I/Oで公開された新しいデザインシステム 従来の「クリーン」で「退屈」なデザインから脱却し、 「表現力豊か」で「より感情に訴えかける」UXを目指している ComponentやShape, Motionなどのアップデートがされた 7

Slide 8

Slide 8 text

8 https://www.youtube.com/watch?v=n17dnMChX14

Slide 9

Slide 9 text

目次 ・Material3 Expressiveとは ・Material3 Expressiveの魅力 ・アップデート概要 ・Component紹介 ・エンジニアの声 ・プロダクト特性とMaterial3 Expressive ・Material3 Expressive導入判断の観点 ・まとめ 9

Slide 10

Slide 10 text

Material3 Expressiveの魅力 10 ・感情に訴える「表現力豊かな」モーションとスタイル ・ユーザビリティ・アクセシビリティの向上 ・ブランドや個性を表現できる柔軟性

Slide 11

Slide 11 text

Material3 Expressiveの魅力 ・より生き生きとして滑らかなモーション ・遊び心のある表現  ・Wavyなローディングインジケーター  ・ボタン押下時のアニメーション 11 感情に訴えかける「表現力豊かな」モーション 「遊び心」「親しみやすさ」といった ポジティブな感情をユーザーに与えられる

Slide 12

Slide 12 text

Material3 Expressiveの魅力 ・ユーザー調査に基づく改善  ・主要なUI要素を発見するまでの時間が最大4倍速く  ・年齢に関わらず直感的に操作できるように ・アクセシビリティを意識したデザイン  ・ボタンやタップ領域を大きくすることで押し間違いを減らす  ・コンテナの色や形で情報をグループ化し、画面構造を明確に 12 ユーザビリティ・アクセシビリティの向上 直感的で誰にでも使いやすいUIを実現 https://design.google/library/expressive-material-design-google-research

Slide 13

Slide 13 text

Material3 Expressiveの魅力 ・豊富なコンポーネントとスタイル  ・35種類の新しいShape  ・多様なボタンサイズや鮮やかなダイナミックカラー ・「ヒーローモーメント」の演出  ・特別な瞬間を印象づけるUI 13 ブランドや個性を表現できる柔軟性 「似たようなUIばかり」という 従来の課題を解消 https://youtu.be/n17dnMChX14?t=22

Slide 14

Slide 14 text

目次 ・Material3 Expressiveとは ・Material3 Expressiveの魅力 ・アップデート概要 ・Component紹介 ・エンジニアの声 ・プロダクト特性とMaterial3 Expressive ・Material3 Expressive導入判断の観点 ・まとめ 14

Slide 15

Slide 15 text

M3 Expressiveのアップデート 15 ・Component ・Motion ・Shape ・Typography ・Color System 5つのアップデート

Slide 16

Slide 16 text

M3 Expressiveのアップデート 16 ・Component ・Motion ・Shape ・Typography ・Color System 5つのアップデート

Slide 17

Slide 17 text

M3 Expressiveのアップデート - Component - 14種類の新規&アップデートされたコンポーネント 17 https://m3.material.io/blog/building-with-m3-expressive

Slide 18

Slide 18 text

・新しいプリセットMotionScheme:  StandardとExpressive ・CustomMotionSchemeの作成も可能 ・Themeに設定することでアプリ全体に適用 ・新しいAnimationSpec: SpatialとEffect  → 移動などにはSpatial,    消失などにはEffect 18 M3 Expressiveのアップデート - Motion - https://m3.material.io/styles/motion/overview/how-it-works

Slide 19

Slide 19 text

@ExperimentalMaterial3ExpressiveApi @Composable fun MaterialTheme( colorScheme: ColorScheme = ... motionScheme: MotionScheme = MaterialTheme.motionScheme, shapes: Shapes = ... typography: Typography = ... content: @Composable () -> Unit, ) {...} 19 ・MaterialThemeにMotionSchemeが追加 ・MotionScheme.expressive()またはMotionScheme.standard()を設定 ・デフォルトはStandard ・MaterialExpressiveThemeが追加  → MotionSchemeがデフォルトでExpressive M3 Expressiveのアップデート - Motion -

Slide 20

Slide 20 text

20 https://m3.material.io/styles/shape/overview-principles M3 Expressiveのアップデート - Shape - ・MaterialShapesライブラリの追加 ・35種類のシェイプとモーフィング ・画像などの切り抜きや、  興味を惹くための装飾として使用

Slide 21

Slide 21 text

目次 ・Material3 Expressiveとは ・Material3 Expressiveの魅力 ・アップデート概要 ・Component紹介 ・エンジニアの声 ・プロダクト特性とMaterial3 Expressive ・Material3 Expressive導入判断の観点 ・まとめ 21

Slide 22

Slide 22 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 22 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 23

Slide 23 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 23 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders https://developer.android.com/reference/kotlin/androidx/compose/material3 Android DevelopersのMaterial3のページに 各Componentのサンプルコードが掲載されています。 実際に試したい場合はそちらを参照してください◎

Slide 24

Slide 24 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 24 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 25

Slide 25 text

App bars ・Screen名やナビゲーションを表示 ・画面に関連するアクションを提供 ・Top app barからApp barに名前が変更 ・Search app barの追加 ・Subtitleの追加 ・TitleTextAlignの追加 ・MediumとLargeの廃止 ・Medium flexibleとLarge flexibleの追加 https://m3.material.io/components/app-bars/overview 25

Slide 26

Slide 26 text

App bars - Small app bar - TopAppBar( title = { Text("Small app bar") }, subtitle = { Text("Subtitle") }, navigationIcon = {...}, actions = {...}, titleHorizontalAlignment = Alignment.CenterHorizontally, ) TopAppBar( title = { Text("Small app bar") }, subtitle = { Text("Subtitle") }, navigationIcon = {...}, actions = {...}, ) 26

Slide 27

Slide 27 text

TopAppBar( title = { Text("Small app bar") }, subtitle = { Text("Subtitle") }, navigationIcon = {...}, actions = {...}, titleHorizontalAlignment = Alignment.CenterHorizontally, ) TopAppBar( title = { Text("Small app bar") }, subtitle = { Text("Subtitle") }, navigationIcon = {...}, actions = {...}, ) 27 App bars - Small app bar -

Slide 28

Slide 28 text

App bars - Search app bar- TopAppBar( title = { AppBarWithSearch(...) ExpandedFullScreenSearchBar(...) }, subtitle = { }, navigationIcon = {...}, actions = {...}, ) ・Componentはまだ用意されていない ・TopAppBarのtitleにAppBarWithSearchと  ExpandedFullScreenSearchBarを 組み合わせることで実装可能 28

Slide 29

Slide 29 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 29 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 30

Slide 30 text

Common buttons ・タップすることでアクションを実行するためのComponent ・DefaultとToggleの2つのスタイル ・タップ時のモーションが追加 ・RoundとSquareのオプション ・5種類のサイズ  Extra small, Small, Medium, Large, Extra large 30 https://whttps://m3.material.io/components/buttons/overview

Slide 31

Slide 31 text

31 val size = ButtonDefaults.MediumContainerHeight Button( onClick = {...}, shapes = ButtonDefaults.shapes(), modifier = Modifier.heightIn(size), contentPadding = ButtonDefaults.contentPaddingFor(size), ) { Text("Button", style = ButtonDefaults.textStyleFor(size)) } ToggleButton( checked = checked, onCheckedChange = { checked = it }, modifier = Modifier.heightIn(size), shapes = ToggleButtonDefaults.shapesFor(size), contentPadding = ButtonDefaults.contentPaddingFor(size), ) { Text("ToggleButton", style = ButtonDefaults.textStyleFor(size)) } Common buttons - DefaultとToggle -

Slide 32

Slide 32 text

32 val size = ButtonDefaults.MediumContainerHeight Button( onClick = {...}, shapes = ButtonDefaults.shapes(), modifier = Modifier.heightIn(size), contentPadding = ButtonDefaults.contentPaddingFor(size), ) { Text("Button", style = ButtonDefaults.textStyleFor(size)) } ToggleButton( checked = checked, onCheckedChange = { checked = it }, modifier = Modifier.heightIn(size), shapes = ToggleButtonDefaults.shapesFor(size), contentPadding = ButtonDefaults.contentPaddingFor(size), ) { Text("ToggleButton", style = ButtonDefaults.textStyleFor(size)) } Common buttons - DefaultとToggle -

Slide 33

Slide 33 text

33 val size = ButtonDefaults.MediumContainerHeight Button( onClick = {...}, shapes = ButtonDefaults.shapes(), modifier = Modifier.heightIn(size), contentPadding = ButtonDefaults.contentPaddingFor(size), ) { Text("Button", style = ButtonDefaults.textStyleFor(size)) } ToggleButton( checked = checked, onCheckedChange = { checked = it }, modifier = Modifier.heightIn(size), shapes = ToggleButtonDefaults.shapesFor(size), contentPadding = ButtonDefaults.contentPaddingFor(size), ) { Text("ToggleButton", style = ButtonDefaults.textStyleFor(size)) } 押下時の アニメーションを付与 Common buttons - DefaultとToggle -

Slide 34

Slide 34 text

34 Button( onClick = {...}, shapes = ButtonDefaults.shapes(), ) { Text(...) } Button( onClick = {...}, // 未設定もしくはButtonDefaults.shape shape = ButtonDefaults.shape ) { Text(...) } Button( onClick = {...}, shape = ButtonDefaults.squareShape, ) { Text(...) } Common buttons - RoundとSquare-

Slide 35

Slide 35 text

35 Button( onClick = {...}, shapes = ButtonDefaults.shapes(), ) { Text(...) } Button( onClick = {...}, // 未設定もしくはButtonDefaults.shape shape = ButtonDefaults.shape ) { Text(...) } Button( onClick = {...}, shape = ButtonDefaults.squareShape, ) { Text(...) } Common buttons - RoundとSquare-

Slide 36

Slide 36 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 36 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 37

Slide 37 text

Icon buttons ・アイコンが表示されたボタン ・タップでアクションを実行する ・DefaultとToggleの2つのスタイル ・タップ時のモーションが追加 ・RoundとSquareのオプション ・5種類のサイズ  Extra small, Small, Medium, Large, Extra large ・3種類の幅  Narrow, Default, Wide 37 https://m3.material.io/components/icon-buttons/overview

Slide 38

Slide 38 text

Icon buttons - DefaultとToggle - 38 FilledIconButton( onClick = {...}, shapes = IconButtonDefaults.shapes(), modifier = Modifier.size( IconButtonDefaults.largeContainerSize(), ), ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null, modifier = Modifier.size(IconButtonDefaults.largeIconSize), ) }

Slide 39

Slide 39 text

Icon buttons - DefaultとToggle - 39 FilledIconButton( onClick = {...}, shapes = IconButtonDefaults.shapes(), modifier = Modifier.size( IconButtonDefaults.largeContainerSize(), ), ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null, modifier = Modifier.size(IconButtonDefaults.largeIconSize), ) }

Slide 40

Slide 40 text

40 FilledIconButton( onClick = {...}, shapes = IconButtonDefaults.shapes(), modifier = Modifier.size( IconButtonDefaults.largeContainerSize(), ), ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null, modifier = Modifier.size(IconButtonDefaults.largeIconSize), ) } Icon buttons - DefaultとToggle -

Slide 41

Slide 41 text

41 FilledIconToggleButton( checked = checked, onCheckedChange = { checked = it }, shapes = IconButtonDefaults.toggleableShapes(), modifier = Modifier.size( IconButtonDefaults.largeContainerSize(), ), ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null, modifier = Modifier.size(IconButtonDefaults.largeIconSize), ) } Icon buttons - DefaultとToggle -

Slide 42

Slide 42 text

42 FilledIconToggleButton( checked = checked, onCheckedChange = { checked = it }, shapes = IconButtonDefaults.toggleableShapes(), modifier = Modifier.size( IconButtonDefaults.largeContainerSize(), ), ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null, modifier = Modifier.size(IconButtonDefaults.largeIconSize), ) } Icon buttons - DefaultとToggle -

Slide 43

Slide 43 text

43 FilledIconToggleButton( checked = checked, onCheckedChange = { checked = it }, shapes = IconButtonDefaults.toggleableShapes(), modifier = Modifier.size( IconButtonDefaults.largeContainerSize(), ), ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null, modifier = Modifier.size(IconButtonDefaults.largeIconSize), ) } Icon buttons - DefaultとToggle -

Slide 44

Slide 44 text

FilledIconButton( onClick = {...}, shapes = IconButtonDefaults.shapes(), ) { Icon(...) } FilledIconButton( onClick = {...},   // 未設定もしくはIconButtonDefaults.HogeRoundShape shape = IconButtonDefaults.largeRoundShape, ) { Icon(...) } FilledIconButton( onClick = {...}, shape = IconButtonDefaults.largeSquareShape, ) { Icon(...) } 44 Icon buttons - RoundとSquare -

Slide 45

Slide 45 text

FilledIconButton( onClick = {...}, shapes = IconButtonDefaults.shapes(), ) { Icon(...) } FilledIconButton( onClick = {...},   // 未設定もしくはIconButtonDefaults.HogeRoundShape shape = IconButtonDefaults.largeRoundShape, ) { Icon(...) } FilledIconButton( onClick = {...}, shape = IconButtonDefaults.largeSquareShape, ) { Icon(...) } 45 Icon buttons - RoundとSquare -

Slide 46

Slide 46 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 46 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 47

Slide 47 text

Button groups ・Material3 Expressiveで追加されたComponent ・関連のあるボタンを視覚的にグルーピング ・StandardとConnectedの2種類 ・SegmentedButtonがdeprecatedに  → Connectedへの置き換えを推奨 https://m3.material.io/components/button-groups/overview 47

Slide 48

Slide 48 text

Button groups - Standard - ・タップ時、選択されたボタンの両隣にインタラクション ・重要な部分を強調したり、関連のあるボタンを  グルーピングしたりする役割 ・Overflow時に折り畳まれる 48

Slide 49

Slide 49 text

Button groups - Standard - 49 overflow時 ・タップ時、選択されたボタンの両隣にインタラクション ・重要な部分を強調したり、関連のあるボタンを  グルーピングしたりする役割 ・Overflow時に折り畳まれる

Slide 50

Slide 50 text

ButtonGroup( overflowIndicator = { menuState -> FilledIconButton( onClick = { if (menuState.isExpanded) { menuState.dismiss() } else { menuState.show() } }, ) { Icon(...) } }, content = { clickableItem(...) toggleableItem(...) customItem(...) }, ) Button groups - Standard - 50

Slide 51

Slide 51 text

ButtonGroup( overflowIndicator = { menuState -> FilledIconButton( onClick = { if (menuState.isExpanded) { menuState.dismiss() } else { menuState.show() } }, ) { Icon(...) } }, content = { clickableItem(...) toggleableItem(...) customItem(...) }, ) overflow時 Button groups - Standard - 51

Slide 52

Slide 52 text

Button groups - Standard - ButtonGroup( overflowIndicator = { menuState -> // overflow時に末尾に表示する Composable }, content = { clickableItem( onClick = {...}, label = "Clickable Item", icon = {...} ) toggleableItem(...) customItem(...) }, ) 52

Slide 53

Slide 53 text

ButtonGroup( overflowIndicator = { menuState -> // overflow時に末尾に表示する Composable }, content = { clickableItem(...) toggleableItem( checked = isChecked, onCheckedChange = { isChecked = it }, label = "Toggleable Item", icon = {...} ) customItem(...) }, ) Button groups - Standard - 53

Slide 54

Slide 54 text

Button groups - Standard - ButtonGroup( overflowIndicator = {...}, content = { clickableItem(...) toggleableItem(...) customItem( buttonGroupContent = { OutlinedButton( onClick = {...}, interactionSource = interactionSource, modifier = Modifier .animateWidth(interactionSource1) .defaultMinSize(minWidth = 188.dp), content = {...}, ) }, menuContent = { DropdownMenuItem( text = { Text("Custom Item") }, onClick = {...}, leadingIcon = {...} ) } ) 54

Slide 55

Slide 55 text

ButtonGroup( overflowIndicator = {...}, content = { clickableItem(...) toggleableItem(...) customItem( buttonGroupContent = { OutlinedButton( onClick = {...}, interactionSource = interactionSource, modifier = Modifier .animateWidth(interactionSource1) .defaultMinSize(minWidth = 188.dp), content = {...}, ) }, menuContent = { DropdownMenuItem( text = { Text("Custom Item") }, onClick = {...}, leadingIcon = {...} ) } ) Button groups - Standard - 55

Slide 56

Slide 56 text

ButtonGroup( overflowIndicator = {...}, content = { clickableItem(...) toggleableItem(...) customItem( buttonGroupContent = { OutlinedButton( onClick = {...}, interactionSource = interactionSource, modifier = Modifier .animateWidth(interactionSource1) .defaultMinSize(minWidth = 188.dp), content = {...}, ) }, menuContent = { DropdownMenuItem( text = { Text("Custom Item") }, onClick = {...}, leadingIcon = {...} ) } ) Button groups - Standard - 56 押されたボタンを拡大し、 隣接するボタンを縮小する

Slide 57

Slide 57 text

ButtonGroup( overflowIndicator = {...}, content = { clickableItem(...) toggleableItem(...) customItem( buttonGroupContent = { OutlinedButton( onClick = {...}, interactionSource = interactionSource, modifier = Modifier .animateWidth(interactionSource1) .defaultMinSize(minWidth = 188.dp), content = {...}, ) }, menuContent = { DropdownMenuItem( text = { Text("Custom Item") }, onClick = {...}, leadingIcon = {...} ) } ) Button groups - Standard - 57 minWidthで レイアウト崩れを防ぐ

Slide 58

Slide 58 text

ButtonGroup( overflowIndicator = {...}, content = { clickableItem(...) toggleableItem(...) customItem( buttonGroupContent = { OutlinedButton( onClick = {...}, interactionSource = interactionSource, modifier = Modifier .animateWidth(interactionSource1) .defaultMinSize(minWidth = 188.dp), content = {...}, ) }, menuContent = { DropdownMenuItem( text = { Text("Custom Item") }, onClick = {...}, leadingIcon = {...} ) } ) Button groups - Standard - 58

Slide 59

Slide 59 text

Button groups - Connected- ・オプションの選択や、Viewの切り替えに使用 ・トグルボタンを使用した単一選択または複数選択 ・選択されたボタンにのみインタラクション ・SegmentedButtonの置き換え先 59

Slide 60

Slide 60 text

Button groups - Connected- Row( horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween), ) { options.forEachIndexed { index, option -> ToggleButton( checked = selectedIndex == index, onCheckedChange = {...}, shapes = when (index) { 0 -> ButtonGroupDefaults.connectedLeadingButtonShapes() options.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes() else -> ButtonGroupDefaults.connectedMiddleButtonShapes() }, ) { Icon(...) Text(...) } } } 60

Slide 61

Slide 61 text

Row( horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween), ) { options.forEachIndexed { index, option -> ToggleButton( checked = selectedIndex == index, onCheckedChange = {...}, shapes = when (index) { 0 -> ButtonGroupDefaults.connectedLeadingButtonShapes() options.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes() else -> ButtonGroupDefaults.connectedMiddleButtonShapes() }, ) { Icon(...) Text(...) } } } Button groups - Connected- 61 ボタンの位置に応じた押下時の アニメーションを付与

Slide 62

Slide 62 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 62 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 63

Slide 63 text

Split button ・Material3 Expressiveで追加されたComponent ・メインのアクションと、関連するオプションを表示 ・5つのサイズ  Extra small, Small, Medium, Large, Extra large ・4つのスタイル  Elevated, Filled, Tonal, Outlined 63 https://m3.material.io/components/split-button/overview

Slide 64

Slide 64 text

Split button Box { SplitButtonLayout( leadingButton = { SplitButtonDefaults.LeadingButton(onClick = {...}) { Icon( Icons.Filled.Mail, modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize), contentDescription = null, ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text("Send Now") } }, trailingButton = {...}, ) DropdownMenu(...) {...} } 64

Slide 65

Slide 65 text

Split button Box { SplitButtonLayout( leadingButton = { SplitButtonDefaults.LeadingButton(onClick = {...}) { Icon( Icons.Filled.Mail, modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize), contentDescription = null, ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text("Send Now") } }, trailingButton = {...}, ) DropdownMenu(...) {...} } 65

Slide 66

Slide 66 text

Split button Box { SplitButtonLayout( leadingButton = {...}, trailingButton = { SplitButtonDefaults.TrailingButton( checked = checked, onCheckedChange = { checked = it }, ) { val rotation: Float by animateFloatAsState( targetValue = if (checked) 180f else 0f, label = "trailing rotation", ) Icon( Icons.Filled.KeyboardArrowDown, modifier = Modifier .size(SplitButtonDefaults.TrailingIconSize) .graphicsLayer { this.rotationZ = rotation }, contentDescription = null, ) } }, ) DropdownMenu(...) {...} 66

Slide 67

Slide 67 text

Split button Box { SplitButtonLayout( leadingButton = {...}, trailingButton = { SplitButtonDefaults.TrailingButton( checked = checked, onCheckedChange = { checked = it }, ) { val rotation: Float by animateFloatAsState( targetValue = if (checked) 180f else 0f, label = "trailing rotation", ) Icon( Icons.Filled.KeyboardArrowDown, modifier = Modifier .size(SplitButtonDefaults.TrailingIconSize) .graphicsLayer { this.rotationZ = rotation }, contentDescription = null, ) } }, ) DropdownMenu(...) {...} 67

Slide 68

Slide 68 text

Split button Box { SplitButtonLayout( leadingButton = {...}, trailingButton = { SplitButtonDefaults.TrailingButton( checked = checked, onCheckedChange = { checked = it }, ) { val rotation: Float by animateFloatAsState( targetValue = if (checked) 180f else 0f, label = "trailing rotation", ) Icon( Icons.Filled.KeyboardArrowDown, modifier = Modifier .size(SplitButtonDefaults.TrailingIconSize) .graphicsLayer { this.rotationZ = rotation }, contentDescription = null, ) } }, ) DropdownMenu(expanded = checked, ...) {...} 68

Slide 69

Slide 69 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 69 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 70

Slide 70 text

FABs ・FloatingActionButton ・画面で最も一般的または主要なアクションを配置 ・Mediumが追加 ・Smallがdeprecated ・Surfaceがdeprecated 70 https://m3.material.io/components/floating-action-button/overview

Slide 71

Slide 71 text

FABs -Medium FAB- 71 MediumFloatingActionButton( modifier = Modifier.animateFloatingActionButton( visible = isVisible, alignment = Alignment.BottomEnd, ), onClick = {...}, ) { Icon( Icons.Filled.Add, contentDescription = null, modifier = Modifier.size( FloatingActionButtonDefaults.MediumIconSize ), ) }

Slide 72

Slide 72 text

FABs -Medium FAB- 72 MediumFloatingActionButton( modifier = Modifier.animateFloatingActionButton( visible = isVisible, alignment = Alignment.BottomEnd, ), onClick = {...}, ) { Icon( Icons.Filled.Add, contentDescription = null, modifier = Modifier.size( FloatingActionButtonDefaults.MediumIconSize ), ) }

Slide 73

Slide 73 text

FABs -Medium FAB- 73 MediumFloatingActionButton( modifier = Modifier.animateFloatingActionButton( visible = isVisible, alignment = Alignment.BottomEnd, ), onClick = {...}, ) { Icon( Icons.Filled.Add, contentDescription = null, modifier = Modifier.size( FloatingActionButtonDefaults.MediumIconSize ), ) }

Slide 74

Slide 74 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 74 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 75

Slide 75 text

Extended FAB ・画面で最も主要なアクションを配置 ・ラベル付きのFloatingActionButton ・Small, Medium, Largeが追加 ・これまでのExtended FABはdeprecated   → Smallに置き換え ・Surfaceがdeprected 75 https://m3.material.io/components/extended-fab/overview

Slide 76

Slide 76 text

Extended FAB - Medium extended FAB - MediumExtendedFloatingActionButton( onClick = {...}, expanded = isExpanded, icon = { Icon( modifier = Modifier.size( FloatingActionButtonDefaults.MediumIconSize ), ... ) }, text = { Text(text = "Medium Extended FAB") }, ) 76

Slide 77

Slide 77 text

Extended FAB - Medium extended FAB - MediumExtendedFloatingActionButton( onClick = {...}, expanded = isExpanded, icon = { Icon( modifier = Modifier.size( FloatingActionButtonDefaults.MediumIconSize ), ... ) }, text = { Text(text = "Medium Extended FAB") }, ) 77

Slide 78

Slide 78 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 78 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 79

Slide 79 text

FAB menu ・FABをタップすることでメニューを表示  (Extended FABでは使用しない) ・Material3 Expressiveで追加されたComponent ・画面に関連する2~6個のアクションを配置 79 https://m3.material.io/components/fab-menu/overview

Slide 80

Slide 80 text

FAB menu FloatingActionButtonMenu( expanded = isExpanded, button = { ToggleFloatingActionButton( checked = isExpanded, onCheckedChange = { isExpanded = !isExpanded }, ) { val icon by remember { derivedStateOf { if (checkedProgress > 0.5f) Icons.Filled.Close else Icons.Filled.Add } } Icon( painter = rememberVectorPainter(icon), modifier = Modifier.animateIcon({ checkedProgress }), ) } }, ) { ... } 80

Slide 81

Slide 81 text

FAB menu FloatingActionButtonMenu( expanded = isExpanded, button = { ToggleFloatingActionButton( checked = isExpanded, onCheckedChange = { isExpanded = !isExpanded }, ) { val icon by remember { derivedStateOf { if (checkedProgress > 0.5f) Icons.Filled.Close else Icons.Filled.Add } } Icon( painter = rememberVectorPainter(icon), modifier = Modifier.animateIcon({ checkedProgress }), ) } }, ) { ... } 81

Slide 82

Slide 82 text

FAB menu FloatingActionButtonMenu(...) { items.forEachIndexed { index, item -> FloatingActionButtonMenuItem( onClick = {...}, icon = { Icon(item.icon, contentDescription = null) }, text = { Text(text = item.label) }, ) } } 82

Slide 83

Slide 83 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 83 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 84

Slide 84 text

Loading indicator ・Material3 Expressiveで追加されたComponent ・短いローディングの進行状況を表示 ・200ms ~ 5sで読み込みが完了する場合に使用 ・LoadingIndicatorとContainedIndicatorの2種類 ・polygonsで特定のshapeだけ表示できる ・pull-to-refreshのインジケーターとして使用可能 84 https://m3.material.io/components/loading-indicator/overview

Slide 85

Slide 85 text

Loading indicator // shapeだけのLoadingIndicator LoadingIndicator() // コンテナ付きのLoadingIndicator ContainedLoadingIndicator() 85 ・デフォルトでは7種類のshape

Slide 86

Slide 86 text

Loading indicator // shapeだけのLoadingIndicator LoadingIndicator( polygons = listOf( MaterialShapes.Flower, MaterialShapes.Cookie9Sided, MaterialShapes.Triangle ), ) // コンテナ付きのLoadingIndicator ContainedLoadingIndicator( polygons = listOf( MaterialShapes.Flower, MaterialShapes.Cookie9Sided, MaterialShapes.Triangle ), ) 86

Slide 87

Slide 87 text

Loading indicator -pull to refresh- 87 PullToRefreshBox( isRefreshing = isRefreshing, state = state, onRefresh = onRefresh, indicator = { PullToRefreshDefaults.LoadingIndicator( state = state, isRefreshing = isRefreshing, modifier = Modifier.align(Alignment.TopCenter), ) }, ) { ... }

Slide 88

Slide 88 text

Component紹介 ・App bars ・Common button ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 88 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 89

Slide 89 text

Progress indicators ・進行中のプロセスのステータスを表示 ・5s以上かかる場合に使用 ・Wavyなスタイルが追加 89 https://m3.material.io/components/progress-indicators/overview

Slide 90

Slide 90 text

Progress indicators // 円形のProgressIndicator CircularProgressIndicator() CircularWavyProgressIndicator() // 直線状のProgressIndicator LinearProgressIndicator() LinearWavyProgressIndicator() 90

Slide 91

Slide 91 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 91 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 92

Slide 92 text

Navigation bar ・アプリ内の画面遷移を提供 ・Compact~Mediumなウインドウサイズで使用 ・従来のNavigation barはdeprecatedに ・ShortNavigationBarの追加 ・Horizontalな表示の追加 92 https://m3.material.io/components/navigation-bar/overview

Slide 93

Slide 93 text

Navigation bar ・アプリ内の画面遷移を提供 ・Compact~Mediumなウインドウサイズで使用 ・従来のNavigation barはdeprecatedに ・ShortNavigationBarの追加 ・Horizontalな表示の追加 93

Slide 94

Slide 94 text

Navigation bar ShortNavigationBar { items.forEachIndexed { index, item -> ShortNavigationBarItem( icon = {...}, label = {...}, selected = selectedItem == index, onClick = { selectedItem = index }, ) } } 94

Slide 95

Slide 95 text

Navigation bar -Horizontal nav item- ShortNavigationBar { items.forEachIndexed { index, item -> ShortNavigationBarItem( iconPosition = NavigationItemIconPosition.Start, ... ) } } 95 ・Medium以上の端末で推奨

Slide 96

Slide 96 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 96 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 97

Slide 97 text

Navigation rail ・アプリ内の画面遷移を提供 ・Medium~Extra-largeなウインドウサイズで使用 ・オプショナルでFABを追加できる ・従来のNavigation railはdeprecatedに ・WideNavigationRailの追加 ・CollapsedとExpandedの2種類が追加 ・Collapsedは従来のNavigation railの置き換え先 ・ExpandedはNavigation drawerの置き換え先 97 https://m3.material.io/components/navigation-rail/overview

Slide 98

Slide 98 text

Navigation rail -Collapsed- 98 WideNavigationRail { items.forEachIndexed { index, item -> WideNavigationRailItem( icon = { Icon( imageVector = item.icon, contentDescription = null ) }, label = { Text(item.label) }, selected = selectedItem == index, onClick = { selectedItem = index }, ) } }

Slide 99

Slide 99 text

Navigation rail -Expanded- ・ExpandにはNon-ModalとModalの2スタイルがある 99 Non-Modal Modal

Slide 100

Slide 100 text

Navigation rail -Expanded Non-Modal- 100 val state = rememberWideNavigationRailState() WideNavigationRail( state = state, header = { IconButton( onClick = { scope.launch { if (state.targetValue == WideNavigationRailValue.Expanded) { state.collapse() } else { state.expand() } } }, ) { if (state.targetValue == WideNavigationRailValue.Expanded) { Icon(Icons.AutoMirrored.Filled.MenuOpen, null) } else { Icon(Icons.Filled.Menu, null) } } }, ) {...}

Slide 101

Slide 101 text

Navigation rail -Expanded Non-Modal- 101 val state = rememberWideNavigationRailState() WideNavigationRail( state = state, header = { IconButton( onClick = { scope.launch { if (state.targetValue == WideNavigationRailValue.Expanded) { state.collapse() } else { state.expand() } } }, ) { if (state.targetValue == WideNavigationRailValue.Expanded) { Icon(Icons.AutoMirrored.Filled.MenuOpen, null) } else { Icon(Icons.Filled.Menu, null) } } }, ) {...}

Slide 102

Slide 102 text

Navigation rail -Expanded Non-Modal- 102 val state = rememberWideNavigationRailState() WideNavigationRail( state = state, header = { IconButton( onClick = { scope.launch { if (state.targetValue == WideNavigationRailValue.Expanded) { state.collapse() } else { state.expand() } } }, ) { if (state.targetValue == WideNavigationRailValue.Expanded) { Icon(Icons.AutoMirrored.Filled.MenuOpen, null) } else { Icon(Icons.Filled.Menu, null) } } }, ) {...}

Slide 103

Slide 103 text

Navigation rail -Expanded Non-Modal- 103 WideNavigationRail(...) { items.forEachIndexed { index, item -> WideNavigationRailItem( railExpanded = state.targetValue == WideNavigationRailValue.Expanded, icon = { Icon( imageVector = item.icon, contentDescription = null ) }, label = { Text(item.label) }, selected = selectedItem == index, onClick = { selectedItem = index }, ) } }

Slide 104

Slide 104 text

Navigation rail -Expanded Non-Modal- 104 WideNavigationRail(...) { items.forEachIndexed { index, item -> WideNavigationRailItem( railExpanded = state.targetValue == WideNavigationRailValue.Expanded, icon = { Icon( imageVector = item.icon, contentDescription = null ) }, label = { Text(item.label) }, selected = selectedItem == index, onClick = { selectedItem = index }, ) } }

Slide 105

Slide 105 text

Navigation rail -Expanded Modal- 105 val state = rememberWideNavigationRailState() ModalWideNavigationRail( state = state, header = { IconButton( onClick = { scope.launch { if (state.targetValue == WideNavigationRailValue.Expanded) { state.collapse() } else { state.expand() } } }, ) { if (state.targetValue == WideNavigationRailValue.Expanded) { Icon(Icons.AutoMirrored.Filled.MenuOpen, null) } else { Icon(Icons.Filled.Menu, null) } } }, ) {...}

Slide 106

Slide 106 text

Navigation rail -Expanded Modal- 106 val state = rememberWideNavigationRailState() ModalWideNavigationRail( state = state, header = { IconButton( onClick = { scope.launch { if (state.targetValue == WideNavigationRailValue.Expanded) { state.collapse() } else { state.expand() } } }, ) { if (state.targetValue == WideNavigationRailValue.Expanded) { Icon(Icons.AutoMirrored.Filled.MenuOpen, null) } else { Icon(Icons.Filled.Menu, null) } } }, ) {...}

Slide 107

Slide 107 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 107 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 108

Slide 108 text

Toolbars ・Material3 Expressiveで追加されたComponent ・表示中の画面に関連する主要なアクションを表示 ・Docked toolbarとFloating toolbarの2種類 ・StandardとVibrantなカラー設定 ・Bottom app barが非推奨  → Docked toolbarへの置き換えが推奨 108 https://m3.material.io/components/toolbars/overview

Slide 109

Slide 109 text

Toolbars -docked toolbar- FlexibleBottomAppBar( containerColor = MaterialTheme.colorScheme.primaryContainer, ) { items.forEachIndexed { index, item -> if (index == 2) { FilledIconButton( modifier = Modifier.size( IconButtonDefaults.smallContainerSize( IconButtonDefaults.IconButtonWidthOption.Wide ) ), onClick = {...}, ) { Icon(item.icon, contentDescription = null) } } else { IconButton(onClick = {...}) { Icon(item.icon, contentDescription = null) } } } } 109

Slide 110

Slide 110 text

FlexibleBottomAppBar( containerColor = MaterialTheme.colorScheme.primaryContainer, ) { items.forEachIndexed { index, item -> if (index == 2) { FilledIconButton( modifier = Modifier.size( IconButtonDefaults.smallContainerSize( IconButtonDefaults.IconButtonWidthOption.Wide ) ), onClick = {...}, ) { Icon(item.icon, contentDescription = null) } } else { IconButton(onClick = {...}) { Icon(item.icon, contentDescription = null) } } } } Toolbars -docked toolbar- 110 Standard Vibrant

Slide 111

Slide 111 text

FlexibleBottomAppBar( containerColor = MaterialTheme.colorScheme.primaryContainer, ) { items.forEachIndexed { index, item -> if (index == 2) { FilledIconButton( modifier = Modifier.size( IconButtonDefaults.smallContainerSize( IconButtonDefaults.IconButtonWidthOption.Wide ) ), onClick = {...}, ) { Icon(item.icon, contentDescription = null) } } else { IconButton(onClick = {...}) { Icon(item.icon, contentDescription = null) } } } } Toolbars -docked toolbar- 111

Slide 112

Slide 112 text

FlexibleBottomAppBar( containerColor = MaterialTheme.colorScheme.primaryContainer, ) { items.forEachIndexed { index, item -> if (index == 2) { FilledIconButton( modifier = Modifier.size( IconButtonDefaults.smallContainerSize( IconButtonDefaults.IconButtonWidthOption.Wide ) ), onClick = {...}, ) { Icon(item.icon, contentDescription = null) } } else { IconButton(onClick = {...}) { Icon(item.icon, contentDescription = null) } } } } Toolbars -docked toolbar- 112

Slide 113

Slide 113 text

Toolbars -floating toolbar- 113 HorizontalFloatingToolbar( colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(), leadingContent = { IconButton(...) IconButton(...) }, trailingContent = { IconButton(...) IconButton(...) }, content = { FilledIconButton( modifier = Modifier.size( IconButtonDefaults.smallContainerSize( IconButtonDefaults.IconButtonWidthOption.Wide ) ), onClick = {...}, ) { Icon(Icons.Filled.Add, contentDescription = null) } }, )

Slide 114

Slide 114 text

Toolbars -floating toolbar- 114 HorizontalFloatingToolbar( colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(), leadingContent = { IconButton(...) IconButton(...) }, trailingContent = { IconButton(...) IconButton(...) }, content = { FilledIconButton( modifier = Modifier.size( IconButtonDefaults.smallContainerSize( IconButtonDefaults.IconButtonWidthOption.Wide ) ), onClick = {...}, ) { Icon(Icons.Filled.Add, contentDescription = null) } }, ) Standard Vibrant

Slide 115

Slide 115 text

Toolbars -floating toolbar- 115 HorizontalFloatingToolbar( colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(), leadingContent = { IconButton(...) IconButton(...) }, trailingContent = { IconButton(...) IconButton(...) }, content = { FilledIconButton( modifier = Modifier.size( IconButtonDefaults.smallContainerSize( IconButtonDefaults.IconButtonWidthOption.Wide ) ), onClick = {...}, ) { Icon(Icons.Filled.Add, contentDescription = null) } }, )

Slide 116

Slide 116 text

Toolbars -floating toolbar- 116 HorizontalFloatingToolbar( colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(), leadingContent = { IconButton(...) IconButton(...) }, trailingContent = { IconButton(...) IconButton(...) }, content = { FilledIconButton( modifier = Modifier.size( IconButtonDefaults.smallContainerSize( IconButtonDefaults.IconButtonWidthOption.Wide ) ), onClick = {...}, ) { Icon(Icons.Filled.Add, contentDescription = null) } }, )

Slide 117

Slide 117 text

Toolbars -floating toolbar- 117 HorizontalFloatingToolbar( colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(), leadingContent = { IconButton(...) IconButton(...) }, trailingContent = { IconButton(...) IconButton(...) }, content = { FilledIconButton( modifier = Modifier.size( IconButtonDefaults.smallContainerSize( IconButtonDefaults.IconButtonWidthOption.Wide ) ), onClick = {...}, ) { Icon(Icons.Filled.Add, contentDescription = null) } }, )

Slide 118

Slide 118 text

Toolbars -floating toolbar- 118 HorizontalFloatingToolbar( colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(), leadingContent = { IconButton(...) IconButton(...) }, trailingContent = { IconButton(...) IconButton(...) }, content = {...}, expanded = isExpanded, )

Slide 119

Slide 119 text

Toolbars -floating toolbar with FAB- Box( Modifier.floatingToolbarVerticalNestedScroll( expanded = expanded, onExpand = { expanded = true }, onCollapse = { expanded = false }, ), ) { HorizontalFloatingToolbar( expanded = expanded, floatingActionButton = { FloatingToolbarDefaults.VibrantFloatingActionButton( onClick = {...}, ) { Icon(Icons.Filled.Add, null) } }, colors = vibrantColors, content = { items.forEachIndexed { index, item -> IconButton(...) }, ) } 119

Slide 120

Slide 120 text

Toolbars -floating toolbar with FAB- Box( Modifier.floatingToolbarVerticalNestedScroll( expanded = expanded, onExpand = { expanded = true }, onCollapse = { expanded = false }, ), ) { HorizontalFloatingToolbar( expanded = expanded, floatingActionButton = { FloatingToolbarDefaults.VibrantFloatingActionButton( onClick = {...}, ) { Icon(Icons.Filled.Add, null) } }, colors = vibrantColors, content = { items.forEachIndexed { index, item -> IconButton(...) }, ) } 120

Slide 121

Slide 121 text

Toolbars -floating toolbar with FAB- Box( Modifier.floatingToolbarVerticalNestedScroll( expanded = expanded, onExpand = { expanded = true }, onCollapse = { expanded = false }, ), ) { HorizontalFloatingToolbar( expanded = expanded, floatingActionButton = { FloatingToolbarDefaults.VibrantFloatingActionButton( onClick = {...}, ) { Icon(Icons.Filled.Add, null) } }, colors = vibrantColors, content = { items.forEachIndexed { index, item -> IconButton(...) }, ) } 121

Slide 122

Slide 122 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 122 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 123

Slide 123 text

Sliders ・指定した範囲内で値を選択できるComponent ・音量調整や価格帯の設定、レビューなどで使用 ・Standard, Centered, Rangeの3種類のスタイル ・VerticalなSliderの追加 ・オプショナルで内側にアイコンを配置 ・XS, S, M, L, XLの5種類のサイズ 123 https://m3.material.io/components/sliders/overview

Slide 124

Slide 124 text

Sliders ・指定した範囲内で値を選択できるComponent ・音量調整や価格帯の設定、レビューなどで使用 ・Standard, Centered, Rangeの3種類のスタイル ・VerticalなSliderの追加 ・オプショナルで内側にアイコンを配置 ・XS, S, M, L, XLの5種類のサイズ 124 調べた限りでは パラメーターは生えていなかった。 独自実装は可能

Slide 125

Slide 125 text

Sliders 125 Slider( state = sliderState, interactionSource = interactionSource, thumb = { SliderDefaults.Thumb( sliderState = sliderState, interactionSource = MutableInteractionSource(), thumbSize = DpSize(4.dp, 52.dp), ) }, track = { SliderDefaults.Track( sliderState = sliderState, modifier = Modifier.height(40.dp).drawWithContent { // スタート地点にアイコンを描画する }, trackCornerSize = 12.dp, drawStopIndicator = null, ) }, )

Slide 126

Slide 126 text

Sliders 126 Slider( state = sliderState, interactionSource = interactionSource, thumb = { SliderDefaults.Thumb( sliderState = sliderState, interactionSource = MutableInteractionSource(), thumbSize = DpSize(4.dp, 52.dp), ) }, track = { SliderDefaults.Track( sliderState = sliderState, modifier = Modifier.height(40.dp).drawWithContent { // スタート地点にアイコンを描画する }, trackCornerSize = 12.dp, drawStopIndicator = null, ) }, )

Slide 127

Slide 127 text

Sliders 127 Slider( state = sliderState, interactionSource = interactionSource, thumb = { SliderDefaults.Thumb( sliderState = sliderState, interactionSource = MutableInteractionSource(), thumbSize = DpSize(4.dp, 52.dp), ) }, track = { SliderDefaults.Track( sliderState = sliderState, modifier = Modifier.height(40.dp).drawWithContent { // スタート地点にアイコンを描画する }, trackCornerSize = 12.dp, drawStopIndicator = null, ) }, ) M3 Expressiveで追加された パラメーター trackCornerSizeあり trackCornerSizeなし

Slide 128

Slide 128 text

Sliders -vertical- 128 val sliderState = rememberSliderState( steps = 3, valueRange = -50f..50f, ) VerticalSlider( state = sliderState, interactionSource = interactionSource, reverseDirection = true, )

Slide 129

Slide 129 text

Component紹介 ・App bars ・Common buttons ・Icon buttons ・Button groups ・Split button ・FABs ・Extended FAB ・FAB menu 129 ・Loading indicator ・Progress indicator ・Navigation bar ・Navigation rail ・Toolbars ・Sliders

Slide 130

Slide 130 text

目次 ・Material3 Expressiveとは ・Material3 Expressiveの魅力 ・アップデート概要 ・Component紹介 ・エンジニアの声 ・プロダクト特性とMaterial3 Expressive ・Material3 Expressive導入判断の観点 ・まとめ 130

Slide 131

Slide 131 text

エンジニアの声 131 ・Material3 Expressiveについての印象、  導入におけるメリット・デメリット, 懸念点など ・Android, iOS, Webに携わっているエンジニア ・回答者16名 Material3 Expressiveに関するアンケートを実施

Slide 132

Slide 132 text

エンジニアの声 132 ・かわいい ・わくわくする印象を与えられそう ・デザインの自由度が上がった ・ユーザー体験をリッチにできそう ・アプリ全体で統一感のあるアニメーションを適用できそう Material3 Expressiveに対するポジティブな声

Slide 133

Slide 133 text

エンジニアの声 133 ・既存のデザインシステムとの親和性が低い ・開発コストに見合うか判断が難しい ・toBアプリでは表現力の豊かさより分かりやすさを重視したい ・車載アプリのような、ユーザーの注意を引くようなコンポーネントは  使用するべきではないケースも存在する Material3 Expressiveの導入に対して慎重な声

Slide 134

Slide 134 text

エンジニアの声 134 ・既存のデザインシステムとの親和性が低い ・開発コストに見合うか判断が難しい ・toBアプリでは表現力の豊かさより分かりやすさを重視したい ・車載アプリのような、ユーザーの注意を引くようなコンポーネントは  使用するべきではないケースも存在する Material3 Expressiveの導入に対して慎重な声

Slide 135

Slide 135 text

エンジニアの声 135 ・「魅力的に感じる」という声は一定数ある ・一方で、既存プロダクトへの導入はハードルが高い  ・ブランド・既存デザインとの親和性  ・導入コストに見合う効果があるか不明 プロダクト特性によってはリスクになる恐れがある!?

Slide 136

Slide 136 text

目次 ・Material3 Expressiveとは ・Material3 Expressiveの魅力 ・アップデート概要 ・Component紹介 ・エンジニアの声 ・プロダクト特性とMaterial3 Expressive ・Material3 Expressive導入判断の観点 ・まとめ 136

Slide 137

Slide 137 text

プロダクト特性とMaterial3 Expressive 137 Material3 Expressiveは ユーザーの注意を引きつけるComponentを提供 → アプリの機能性を高め、ユーザー体験をより楽しく、   直感的で、効率的なものにすることを目指している 一方で、文脈を無視したExpressiveデザインは ユーザビリティを低下させてしまう可能性がある

Slide 138

Slide 138 text

プロダクト特性とMaterial3 Expressive 138 Expressiveが効果を発揮しやすいケース 感情的な体験が価値になるプロダクト ・アルバムアプリ  思い出をエモーショナルに表現 ・メディアプレイヤー  波状のプログレスインジケーターが  生き生きと感じさせる & 再生状態を明確に

Slide 139

Slide 139 text

プロダクト特性とMaterial3 Expressive 139 Expressiveが効果を発揮しやすいケース 重要なアクションやコンテンツを際立たせたいプロダクト ・メールアプリの送信ボタン ・タスク管理アプリの作成ボタン ・ショッピングアプリの購入ボタン 特定の操作が頻繁に行われるアプリでは、 主要なアクションにいち早くアクセスさせることができる

Slide 140

Slide 140 text

プロダクト特性とMaterial3 Expressive 140 Expressiveがリスクになりやすいケース 機能性・信頼性・正確性が優先されるプロダクト ・銀行アプリ ・toB向けの業務アプリ ユーザーの目的は「素早く・正確に仕事を完了すること」 余計なアニメーションや派手なモーションは操作速度に影響を及ぼす 派手さがブランドイメージを損なう可能性もある キーアクションの発見性向上に限定して導入するのは有効

Slide 141

Slide 141 text

プロダクト特性とMaterial3 Expressive 141 Expressiveがリスクになりやすいケース 安全性・即時性が優先されるプロダクト ・車載アプリ ・災害通知アプリ 「現実世界の状況把握」や「即時の判断」が最優先 表現豊かなUIが注意を奪うと、事故・誤操作・行動遅れにつながる Expressiveな要素は基本的に控えるべき

Slide 142

Slide 142 text

プロダクト特性とMaterial3 Expressive 142 Expressiveがリスクになりやすいケース 確立されたUIパターンが存在し、ユーザーの慣れが重要なプロダクト ・音楽プレイリスト ・SNSのタイムライン ・表計算アプリ ユーザーは慣れ親しんだUIパターンで直感的に操作している 独自のレイアウトはユーザビリティの低下につながる可能性がある 主要操作パターンは変更せず、補助的な演出に使用する

Slide 143

Slide 143 text

目次 ・Material3 Expressiveとは ・Material3 Expressiveの魅力 ・アップデート概要 ・Component紹介 ・エンジニアの声 ・プロダクト特性とMaterial3 Expressive ・Material3 Expressive導入判断の観点 ・まとめ 143

Slide 144

Slide 144 text

Material3 Expressive導入判断のための観点 144 議論のポイント ・プロダクト特性との適合性 ・ブランド・デザインシステムとの親和性 ・ユーザビリティ・アクセシビリティへの影響 ・開発・デザイン工数との費用対効果

Slide 145

Slide 145 text

145 プロダクト特性との適合性 ・表現力豊かなUIがユーザー体験を高める場合にはプラスに働く  ・写真, メデイア, SNSやエンタメなど ・重要なアクションを際立たせたい場合は発見性向上に寄与 ・機能性・効率性などが重視される領域では逆効果になるリスク ・安全性の観点から使用が危険になる場合も 利用文脈において、 表現性が価値を高めるかどうかを見極める Material3 Expressive導入判断のための観点

Slide 146

Slide 146 text

146 ブランド・デザインシステムとの親和性 ・豊富なShapeやMotionで「ブランドらしさ」を強調できる ・ブランドイメージと相性が合うか ・既存のデザインシステムに取り入れられるか ・iOS/Webとの体験が乖離する可能性 ブランド価値を強化できるか 既存システムとの齟齬を生まないかを確認する Material3 Expressive導入判断のための観点

Slide 147

Slide 147 text

147 ユーザビリティ・アクセシビリティへの影響 ・タップ領域拡大や視認性向上によるアクセシビリティ改善 ・年齢を問わない直感的な操作性 ・アニメーションやシェイプが逆に操作を複雑にしないか ・低スペック端末だとパフォーマンスに影響がある可能性も 操作性・アクセシビリティを向上させつつ 過剰な表現は避ける Material3 Expressive導入判断のための観点

Slide 148

Slide 148 text

148 開発・デザイン工数との費用対効果 ・公式ライブラリで提供されているため実装コストは下がる ・少ない変更でアプリ全体に統一感のあるアニメーションを適用できる ・新しいコンポーネントやモーションへの学習コスト ・既存コンポーネントの置き換え ・問い合わせの減少など、UX向上がどれくらい見込めるのか 導入コストに見合う効果が得られるか 慎重に判断する Material3 Expressive導入判断のための観点

Slide 149

Slide 149 text

149 「機能性やユーザー体験を高める力」と「過剰演出によるリスク」 4つの観点から総合的に評価することが重要  ・プロダクト特性との適合性  ・ブランド・デザインシステムとの親和性  ・ユーザビリティ・アクセシビリティへの影響  ・開発・デザイン工数との費用対効果 期間限定の画面や小さなComponentから試験的に導入する選択も Material3 Expressive導入判断のための観点

Slide 150

Slide 150 text

目次 150 ・Material3 Expressiveとは ・Material3 Expressiveの魅力 ・アップデート概要 ・Component紹介 ・エンジニアの声 ・プロダクト特性とMaterial3 Expressive ・Material3 Expressive導入判断の観点 ・まとめ

Slide 151

Slide 151 text

まとめ 151 ・Material3 Expressive は  「表現力豊かで感情に訴えかけるUX」を実現する新しい選択肢 ・新しい Component / Shape / Motion でUI表現の幅が広がる ・プロダクト特性によっては大きなメリットにもリスクにもなる ・4つの観点(特性・ブランド・ユーザビリティ・費用対効果)から  導入を検討することが重要 ・自社プロダクトに最適な形で選択的に導入することが重要