Slide 1

Slide 1 text

Material Design 
 と 
 Product UI Design


Slide 2

Slide 2 text

大西克己 
 @katsummy
 Android Engineer since Donut
 Flutter Engineer Now
 I ♥Mt.

Slide 3

Slide 3 text

Introduce


Slide 4

Slide 4 text

Introduce


Slide 5

Slide 5 text

Introduce
 Android Kotlin


Slide 6

Slide 6 text

Introduce
 タクシーフードデリバリー サービス
 - ローンチに向けて Flutterで開発中

Slide 7

Slide 7 text

Agenda
 1. デザインシステム / マテリアルデザインおさらい
 2. マテリアルデザインの要素
 3. アプリテーマのカスタマイズ
 4. プロダクトへマテリアルデザインの適用


Slide 8

Slide 8 text

デザインシステム
 デザインの原則、概念、ガイド、コンポーネントなど、デザインに関するルールを定め たもの
 - Color 
 - Typography
 - Interaction (Animation)
 - Icon
 - Component
 - Button / List / ListItem / Divider / Card / Shadow (Elevation) / 
 - etc.
 一貫したデザインで、UIと操作/体験をユーザに伝える。


Slide 9

Slide 9 text

デザインシステム
 - フラットデザイン - Apple?
 - マテリアルデザイン - Google
 - メトロUI - Microsoft?
 - Polaris - Shopify
 - etc.


Slide 10

Slide 10 text

Material is the metaphor
 - 光があたると影が現れるなど、物理的な世界を、二次 元に取り入れている
 - 素材の表面は紙とインクで表現
 - フィードバック/モーション
 Material Design / Material System


Slide 11

Slide 11 text

Material Design / Material System
 Component / Typography / Color / Elevation / States

Slide 12

Slide 12 text

Material Design / Material System
 Components

Slide 13

Slide 13 text

Material Design / Material System Components

Slide 14

Slide 14 text

Product UI Design
 フォントとサイズは拘りたい
 カラーは仮の当てなので、ブランディング/トンマナ調整後は修正したい。
 ボタンなどコンポーネントデザインはプロダクトの雰囲気に合わせたい。
 - コンポーネントの角丸を大きく
 - Elevationはデフォルトとは変えたい
 - ボタン - Enable/Stable時の表示
 - Radio / Check のカスタム
 - カスタムコンポーネント
 


Slide 15

Slide 15 text

Product UI Design への対応
 Product の Design System を作成する?
 デフォルトは Material Design を使用する!
 カスタムして Product UI Design へ適用!
 - Typography
 - Component
 - Color
 - Elevation
 - States
 マテリアルコンポーネントのテーマをカスタム!
 カスタムコンポーネント!


Slide 16

Slide 16 text

アプリテーマのカスタマイズ
 Flutter アプリテーマのカスタマイズ - Qiita https://qiita.com/granoeste/items/352d19157c21cd35e21f


Slide 17

Slide 17 text

アプリテーマのカスタマイズ


Slide 18

Slide 18 text

アプリテーマのカスタマイズ


Slide 19

Slide 19 text

アプリテーマのカスタマイズ


Slide 20

Slide 20 text

Typographyのカスタマイズ


Slide 21

Slide 21 text

Typography.material2018() black, white, englishLike, dense, tall プラットフォーム


Slide 22

Slide 22 text

Typography factory Typography._withPlatform( TargetPlatform? platform, TextTheme? black, TextTheme? white, TextTheme englishLike, TextTheme dense, TextTheme tall, ) { switch (platform) { case TargetPlatform.iOS: black ??= blackCupertino; white ??= whiteCupertino; break; case TargetPlatform.android: case TargetPlatform.fuchsia: black ??= blackMountainView; white ??= whiteMountainView; break; case TargetPlatform.windows: black ??= blackRedmond; white ??= whiteRedmond; break; case TargetPlatform.macOS: black ??= blackRedwoodCity; white ??= whiteRedwoodCity; break; case TargetPlatform.linux: black ??= blackHelsinki; white ??= whiteHelsinki; break; case null: break; } return Typography._(black!, white!, englishLike, dense, tall); } プラットフォーム毎に Facetype が定義されている
 fontFamily: '.SF UI Display/Text' fontFamily: 'Roboto' fontFamily: 'Segoe UI' fontFamily: '.AppleSystemUIFont' fontFamily: 'Roboto', fontFamilyFallback: _helsinkiFontFallbacks, _helsinkiFontFallbacks = ['Ubuntu', 'Cantarell', 'DejaVu Sans', 'Liberation Sans', 'Arial']

Slide 23

Slide 23 text

defaultTargetPlatform
 /// The dart:io implementation of [platform.defaultTargetPlatform]. platform.TargetPlatform get defaultTargetPlatform { platform.TargetPlatform? result; if (Platform.isAndroid) { result = platform.TargetPlatform.android; } else if (Platform.isIOS) { result = platform.TargetPlatform.iOS; } else if (Platform.isFuchsia) { result = platform.TargetPlatform.fuchsia; } else if (Platform.isLinux) { result = platform.TargetPlatform.linux; } else if (Platform.isMacOS) { result = platform.TargetPlatform.macOS; } else if (Platform.isWindows) { result = platform.TargetPlatform.windows; } assert(() { if (Platform.environment.containsKey('FLUTTER_TEST')) result = platform.TargetPlatform.android; return true; }()); if (platform.debugDefaultTargetPlatformOverride != null) result = platform.debugDefaultTargetPlatformOverride; if (result == null) { throw FlutterError( 'Unknown platform.\n' '${Platform.operatingSystem} was not recognized as a target platform. ' 'Consider updating the list of TargetPlatforms to include this platform.' ); } return result!; } final typography = Typography.material2018(platform: defaultTargetPlatform );

Slide 24

Slide 24 text

Typography - englishLike2018
 /// Defines text geometry for [ScriptCategory.englishLike] scripts, such as /// English, French, Russian, etc. /// /// The font sizes, weights, and letter spacings in this version match the /// [latest Material Design specification](https://material.io/go/design-typography#typography-styles). static const TextTheme englishLike2018 = TextTheme( headline1 : TextStyle(debugLabel: 'englishLike headline1 2018', fontSize: 96.0, fontWeight: FontWeight.w300, textBaseline: TextBaseline.alphabetic, letterSpacing: -1.5), headline2 : TextStyle(debugLabel: 'englishLike headline2 2018', fontSize: 60.0, fontWeight: FontWeight.w300, textBaseline: TextBaseline.alphabetic, letterSpacing: -0.5), headline3 : TextStyle(debugLabel: 'englishLike headline3 2018', fontSize: 48.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic, letterSpacing: 0.0), headline4 : TextStyle(debugLabel: 'englishLike headline4 2018', fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic, letterSpacing: 0.25), headline5 : TextStyle(debugLabel: 'englishLike headline5 2018', fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic, letterSpacing: 0.0), headline6 : TextStyle(debugLabel: 'englishLike headline6 2018', fontSize: 20.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic, letterSpacing: 0.15), bodyText1 : TextStyle(debugLabel: 'englishLike bodyText1 2018', fontSize: 16.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic, letterSpacing: 0.5), bodyText2 : TextStyle(debugLabel: 'englishLike bodyText2 2018', fontSize: 14.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic, letterSpacing: 0.25), subtitle1 : TextStyle(debugLabel: 'englishLike subtitle1 2018', fontSize: 16.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic, letterSpacing: 0.15), subtitle2 : TextStyle(debugLabel: 'englishLike subtitle2 2018', fontSize: 14.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic, letterSpacing: 0.1), button : TextStyle(debugLabel: 'englishLike button 2018', fontSize: 14.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic, letterSpacing: 1.25), caption : TextStyle(debugLabel: 'englishLike caption 2018', fontSize: 12.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic, letterSpacing: 0.4), overline : TextStyle(debugLabel: 'englishLike overline 2018', fontSize: 10.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic, letterSpacing: 1.5), );

Slide 25

Slide 25 text

Typography - dense2018
 /// Defines text geometry for dense scripts, such as Chinese, Japanese /// and Korean. /// /// The font sizes, weights, and letter spacings in this version match the /// latest [Material Design specification](https://material.io/go/design-typography#typography-styles). static const TextTheme dense2018 = TextTheme( headline1 : TextStyle(debugLabel: 'dense headline1 2018', fontSize: 96.0, fontWeight: FontWeight.w100, textBaseline: TextBaseline.ideographic), headline2 : TextStyle(debugLabel: 'dense headline2 2018', fontSize: 60.0, fontWeight: FontWeight.w100, textBaseline: TextBaseline.ideographic), headline3 : TextStyle(debugLabel: 'dense headline3 2018', fontSize: 48.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), headline4 : TextStyle(debugLabel: 'dense headline4 2018', fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), headline5 : TextStyle(debugLabel: 'dense headline5 2018', fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), headline6 : TextStyle(debugLabel: 'dense headline6 2018', fontSize: 21.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic), bodyText1 : TextStyle(debugLabel: 'dense bodyText1 2018', fontSize: 17.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), bodyText2 : TextStyle(debugLabel: 'dense bodyText2 2018', fontSize: 15.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), subtitle1 : TextStyle(debugLabel: 'dense subtitle1 2018', fontSize: 17.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), subtitle2 : TextStyle(debugLabel: 'dense subtitle2 2018', fontSize: 15.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic), button : TextStyle(debugLabel: 'dense button 2018', fontSize: 15.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.ideographic), caption : TextStyle(debugLabel: 'dense caption 2018', fontSize: 13.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), overline : TextStyle(debugLabel: 'dense overline 2018', fontSize: 11.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.ideographic), );

Slide 26

Slide 26 text

Typography - tall2018
 /// Defines text geometry for tall scripts, such as Farsi, Hindi, and Thai. /// /// The font sizes, weights, and letter spacings in this version match the /// latest [Material Design specification](https://material.io/go/design-typography#typography-styles). static const TextTheme tall2018 = TextTheme( headline1 : TextStyle(debugLabel: 'tall headline1 2018', fontSize: 96.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), headline2 : TextStyle(debugLabel: 'tall headline2 2018', fontSize: 60.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), headline3 : TextStyle(debugLabel: 'tall headline3 2018', fontSize: 48.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), headline4 : TextStyle(debugLabel: 'tall headline4 2018', fontSize: 34.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), headline5 : TextStyle(debugLabel: 'tall headline5 2018', fontSize: 24.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), headline6 : TextStyle(debugLabel: 'tall headline6 2018', fontSize: 21.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic), bodyText1 : TextStyle(debugLabel: 'tall bodyText1 2018', fontSize: 17.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic), bodyText2 : TextStyle(debugLabel: 'tall bodyText2 2018', fontSize: 15.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), subtitle1 : TextStyle(debugLabel: 'tall subtitle1 2018', fontSize: 17.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), subtitle2 : TextStyle(debugLabel: 'tall subtitle2 2018', fontSize: 15.0, fontWeight: FontWeight.w500, textBaseline: TextBaseline.alphabetic), button : TextStyle(debugLabel: 'tall button 2018', fontSize: 15.0, fontWeight: FontWeight.w700, textBaseline: TextBaseline.alphabetic), caption : TextStyle(debugLabel: 'tall caption 2018', fontSize: 13.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), overline : TextStyle(debugLabel: 'tall overline 2018', fontSize: 11.0, fontWeight: FontWeight.w400, textBaseline: TextBaseline.alphabetic), );

Slide 27

Slide 27 text

Typography - どこで使われてるのか?
 About applicationName, DatePicker
 AppBar
 TextFormField's hintStyle, DropdownButton, ListTile's title
 ListTile on Drawer, NavigationRail, TabBar
 Text, ListTile's subtitle
 Button
 TextFormField's errorStyle, helperStyle


Slide 28

Slide 28 text

Typography - カスタマイズ AppのTextStyle
 black と dense をマージ
 個々にマージ
 textTheme に設定


Slide 29

Slide 29 text

Typography - Tree


Slide 30

Slide 30 text

Color
 深淵


Slide 31

Slide 31 text

Color 個々に定義
 AppのColorStyle
 colorScheme に
 primary / secondary を定義
 深淵


Slide 32

Slide 32 text

Component
 プロダクトで使用するコンポーネントをピックアップ - AppBar - Buttons - Bottom Navigation - Cards - Chips - TextField - etc... カスタムコンポーネント
 - Material Design に無いコンポーネント作成 - テーマのカスタムでは実現できないコンポーネント... - Radio/Check Material Design の テーマをカスタム


Slide 33

Slide 33 text

Component
 プロダクトで使用するコンポーネントをピックアップ - AppBar - Buttons - Bottom Navigation - Cards - Chips - TextField - etc... カスタムコンポーネント
 - Material Design に無いコンポーネント作成 - テーマのカスタムでは実現できないコンポーネント - Radio/Check Material Design の テーマをカスタム


Slide 34

Slide 34 text

Component - AppBar
 AppBar は、primaryColor が適用されるので、 appBarTheme で再定義します。 AppBar の背景は white、テキストとアイコンは black を適用したい


Slide 35

Slide 35 text

Component - Button
 Flutter 1.22 ~ 


Slide 36

Slide 36 text

Component - Button
 ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( (Set states) { if (states.contains(MaterialState.pressed)) return Theme.of(context).colorScheme.primary.withOpacity(0.5); return null; // default to the component's default }, ), ), ) states で値を変更


Slide 37

Slide 37 text

Component - Button - ElevatedButton 角をもっと丸く、ボタンのDisable時のテキストはデフォルトではグレーになるので、プライマリーカラーに透過したものを適用したい!!


Slide 38

Slide 38 text

Component - Button - OutlinedButton 角をもっと丸く、アウトラインはもう少し細く、プライマリーカラーとフィルカラーを適用したい!!


Slide 39

Slide 39 text

Component - Button - TextButton 
 テキストボタンのカラーは Black を適用したい!!


Slide 40

Slide 40 text

Component - TextField 
 テキスト入力は背景カラーとアンダーラインを適用したい!!
 フォーカス
 フォーカス


Slide 41

Slide 41 text

Component - TextField 
 アンダーライン
 背景


Slide 42

Slide 42 text

Component - TextField - Typography

Slide 43

Slide 43 text

Component - Radio/Checkbox Radio / Checkbox は、円形でデフォルトより大きく、チェックマークをカスタムしたい!!
 デフォルト
 こうしたい


Slide 44

Slide 44 text

Component - Radio/Checkbox Radio / Checkbox は、円形でデフォルトより大きく、チェックマークをカスタムしたい!!
 デフォルト
 こうしたい
 テーマ/スタイルの定義ではカスタムできません!!
 画像で実装できるけど... インタラクション(アニメーション)が消えてる...


Slide 45

Slide 45 text

Component - Radio/Checkbox SDK ソースコードをコピーしてカスタム...
 大きく
 丸く
 チェックマークのパスのポ ジションを調整
 プライベート定数 
 const 定数


Slide 46

Slide 46 text

Component - Radio/Checkbox
 SDK ソースコードをコピーしてカスタム...
 大きく
 プライベート定数 


Slide 47

Slide 47 text

Elevation
 テーマ設定できるものは、Elevationを使用する。 
 カスタムコンポーネントはboxShadowを使用する 


Slide 48

Slide 48 text

Elevation


Slide 49

Slide 49 text

Elevation
 const Map> kElevationToShadow = _elevationToShadow; // to hide the literal from the docs const Color _kKeyUmbraOpacity = Color(0x33000000); // alpha = 0.2 const Color _kKeyPenumbraOpacity = Color(0x24000000); // alpha = 0.14 const Color _kAmbientShadowOpacity = Color(0x1F000000); // alpha = 0.12 const Map> _elevationToShadow = >{ // The empty list depicts no elevation. 0: [], 1: [ BoxShadow(offset: Offset(0.0, 2.0), blurRadius: 1.0, spreadRadius: -1.0, color: _kKeyUmbraOpacity), BoxShadow(offset: Offset(0.0, 1.0), blurRadius: 1.0, spreadRadius: 0.0, color: _kKeyPenumbraOpacity), BoxShadow(offset: Offset(0.0, 1.0), blurRadius: 3.0, spreadRadius: 0.0, color: _kAmbientShadowOpacity), ], 2: [ BoxShadow(offset: Offset(0.0, 3.0), blurRadius: 1.0, spreadRadius: -2.0, color: _kKeyUmbraOpacity), BoxShadow(offset: Offset(0.0, 2.0), blurRadius: 2.0, spreadRadius: 0.0, color: _kKeyPenumbraOpacity), BoxShadow(offset: Offset(0.0, 1.0), blurRadius: 5.0, spreadRadius: 0.0, color: _kAmbientShadowOpacity), ],

Slide 50

Slide 50 text

Elevation
 カスタムコンポーネントはboxShadowに kElevationToShadow[?] を指定する


Slide 51

Slide 51 text

まとめ 個別にスタイルを定義はしない。


Slide 52

Slide 52 text

まとめ アプリ開発初めには、
 - カラー/トンマナは決まってない。
 - ブランディング/マーケティングでかわってくる。
 - アプリ名/アプリアイコンも。
 
 作成途中の機能とデザインを元に実装を進めなければならないことがある。
 
 - コンポーネント志向!! デザイナとエンジニアで共有する
 - スタイルはテーマ化を意識して実装する
 - 足りないUIパーツは、カスタムコンポーネント化していく
 
 デモソースコード granoeste/flutter_custom_theme https://github.com/granoeste/flutter_custom_theme

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

採用情報| 株式会社Mobility Technologies https://mo-t.com/recruit/