Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Material Design と Product UI Design

Material Design と Product UI Design

Katsumi Onishi

March 18, 2021
Tweet

More Decks by Katsumi Onishi

Other Decks in Technology

Transcript

  1. Material Design 

    と 

    Product UI Design


    View Slide

  2. 大西克己 

    @katsummy

    Android Engineer since Donut

    Flutter Engineer Now

    I ♥Mt.

    View Slide

  3. Introduce


    View Slide

  4. Introduce


    View Slide

  5. Introduce

    Android Kotlin


    View Slide

  6. Introduce

    タクシーフードデリバリー サービス

    - ローンチに向けて Flutterで開発中

    View Slide

  7. Agenda

    1. デザインシステム / マテリアルデザインおさらい

    2. マテリアルデザインの要素

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

    4. プロダクトへマテリアルデザインの適用


    View Slide

  8. デザインシステム

    デザインの原則、概念、ガイド、コンポーネントなど、デザインに関するルールを定め
    たもの

    - Color 

    - Typography

    - Interaction (Animation)

    - Icon

    - Component

    - Button / List / ListItem / Divider / Card / Shadow (Elevation) / 

    - etc.

    一貫したデザインで、UIと操作/体験をユーザに伝える。


    View Slide

  9. デザインシステム

    - フラットデザイン - Apple?

    - マテリアルデザイン - Google

    - メトロUI - Microsoft?

    - Polaris - Shopify

    - etc.


    View Slide

  10. Material is the metaphor

    - 光があたると影が現れるなど、物理的な世界を、二次
    元に取り入れている

    - 素材の表面は紙とインクで表現

    - フィードバック/モーション

    Material Design / Material System


    View Slide

  11. Material Design / Material System

    Component / Typography / Color / Elevation / States

    View Slide

  12. Material Design / Material System

    Components

    View Slide

  13. Material Design / Material System
    Components

    View Slide

  14. Product UI Design

    フォントとサイズは拘りたい

    カラーは仮の当てなので、ブランディング/トンマナ調整後は修正したい。

    ボタンなどコンポーネントデザインはプロダクトの雰囲気に合わせたい。

    - コンポーネントの角丸を大きく

    - Elevationはデフォルトとは変えたい

    - ボタン - Enable/Stable時の表示

    - Radio / Check のカスタム

    - カスタムコンポーネント


    View Slide

  15. Product UI Design への対応

    Product の Design System を作成する?

    デフォルトは Material Design を使用する!

    カスタムして Product UI Design へ適用!

    - Typography

    - Component

    - Color

    - Elevation

    - States

    マテリアルコンポーネントのテーマをカスタム!

    カスタムコンポーネント!


    View Slide

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

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


    View Slide

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


    View Slide

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


    View Slide

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


    View Slide

  20. Typographyのカスタマイズ


    View Slide

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


    View Slide

  22. 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']

    View Slide

  23. 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 );

    View Slide

  24. 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),
    );

    View Slide

  25. 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),
    );

    View Slide

  26. 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),
    );

    View Slide

  27. 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


    View Slide

  28. Typography - カスタマイズ
    AppのTextStyle

    black と dense をマージ

    個々にマージ

    textTheme に設定


    View Slide

  29. Typography - Tree


    View Slide

  30. Color

    深淵


    View Slide

  31. Color
    個々に定義

    AppのColorStyle

    colorScheme に

    primary / secondary を定義

    深淵


    View Slide

  32. Component

    プロダクトで使用するコンポーネントをピックアップ
    - AppBar
    - Buttons
    - Bottom Navigation
    - Cards
    - Chips
    - TextField
    - etc...
    カスタムコンポーネント

    - Material Design に無いコンポーネント作成
    - テーマのカスタムでは実現できないコンポーネント...
    - Radio/Check
    Material Design の
    テーマをカスタム


    View Slide

  33. Component

    プロダクトで使用するコンポーネントをピックアップ
    - AppBar
    - Buttons
    - Bottom Navigation
    - Cards
    - Chips
    - TextField
    - etc...
    カスタムコンポーネント

    - Material Design に無いコンポーネント作成
    - テーマのカスタムでは実現できないコンポーネント
    - Radio/Check
    Material Design の
    テーマをカスタム


    View Slide

  34. Component - AppBar

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


    View Slide

  35. Component - Button

    Flutter 1.22 ~ 


    View Slide

  36. 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 で値を変更


    View Slide

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


    View Slide

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


    View Slide

  39. Component - Button - TextButton 

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


    View Slide

  40. Component - TextField 

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

    フォーカス

    フォーカス


    View Slide

  41. Component - TextField 

    アンダーライン

    背景


    View Slide

  42. Component - TextField - Typography

    View Slide

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

    デフォルト

    こうしたい


    View Slide

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

    デフォルト

    こうしたい

    テーマ/スタイルの定義ではカスタムできません!!

    画像で実装できるけど... インタラクション(アニメーション)が消えてる...


    View Slide

  45. Component - Radio/Checkbox
    SDK ソースコードをコピーしてカスタム...

    大きく

    丸く

    チェックマークのパスのポ
    ジションを調整

    プライベート定数 

    const 定数


    View Slide

  46. Component - Radio/Checkbox

    SDK ソースコードをコピーしてカスタム...

    大きく

    プライベート定数 


    View Slide

  47. Elevation

    テーマ設定できるものは、Elevationを使用する。 

    カスタムコンポーネントはboxShadowを使用する 


    View Slide

  48. Elevation


    View Slide

  49. 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),
    ],

    View Slide

  50. Elevation

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


    View Slide

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


    View Slide

  52. まとめ
    アプリ開発初めには、

    - カラー/トンマナは決まってない。

    - ブランディング/マーケティングでかわってくる。

    - アプリ名/アプリアイコンも。


    作成途中の機能とデザインを元に実装を進めなければならないことがある。


    - コンポーネント志向!! デザイナとエンジニアで共有する

    - スタイルはテーマ化を意識して実装する

    - 足りないUIパーツは、カスタムコンポーネント化していく


    デモソースコード
    granoeste/flutter_custom_theme https://github.com/granoeste/flutter_custom_theme

    View Slide

  53. View Slide

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


    View Slide