7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FlutterAdvent Calendar 2024

Day 14

【Flutter】(Androidユーザーが思う)アプリに対応してほしい機能と対応方法について

Last updated at Posted at 2024-12-13

はじめに

日常生活や仕事において、スマホアプリを頻繁に利用する方は、iPhoneユーザーが多いように感じます。

それによってAndroid向けの機能が軽視されているというわけではありませんが、優先順位はやや低い傾向にあると思います。

そこで、普段からAndroidを利用している身として、
「対応されていたら嬉しい!」 と思える機能を、Flutterを用いた実現方法も含めていくつかご紹介します。

🔨アイコン - Themed App Icons

テーマ ライトモード ダークモード
デフォルト デフォルト -
青系の壁紙 青系の壁紙 青系の壁紙(ダークモード)
赤系の壁紙 赤系の壁紙 青系の壁紙(ダークモード)

Android13から追加された機能です。

設定を行うことで、壁紙や設定した色、テーマモードに応じてアイコンが切り替わります。
少し前の内容にはなりますが、概要はツルオカさんが記事にまとめて下さっています。
そちらをご覧下さい。

筆者にとっては、アプリをホームに配置する動機となる機能です。

実装

概要は前述した記事で掲載されていますが、
現在ではパッケージ開発が進んだこともあり、flutter_launcher_iconsパッケージでも対応されました。

ただ、私は環境ごと(開発/本番)のThemed App Iconへの対応など、細かい設定が好みでicons_launcherを利用しています。

私の個人開発アプリを例に設定していきます。

1. アプリアイコン画像の作成

まずはアプリアイコンの画像を作成します。
私はFigmaのテンプレートを利用してアプリアイコンを作成しています。

作成する画像は2種類です

  • 通常のアプリアイコン
  • モノクロ化されたアプリアイコン
通常 モノクロ
通常のアプリアイコン モノクロのアプリアイコン

任意のフォルダに作成したアプリアイコンを配置します。

2. icons_launcherパッケージのセットアップ

セットアップはパッケージに記載されている方法の通り対応していきます。

3. アイコンの配置/生成

Themed App Iconへの対応は、adaptive_monochrome_image:の記載を追加することで対応できます。

icons_launcher.yaml
icons_launcher:
  image_path: "assets/launcher_icon/icon.png"
  platforms:
    android:
      enable: true
      image_path: "assets/launcher_icon/icon.png"
      adaptive_background_color: '#FFFFFF'
      adaptive_foreground_image: "assets/launcher_icon/icon_adaptive.png"
      adaptive_round_image: "assets/launcher_icon/icon_adaptive.png"
      # 用意したモノクロ写真の設定を追加
      adaptive_monochrome_image: "assets/launcher_icon/icon_adaptive_monochrome.png"
    ios:
      enable: true
      image_path: "assets/launcher_icon/icon.png"

設定が完了したらコマンドを叩いてプラットフォーム別のアイコンを生成します。

ターミナル
dart run icons_launcher:create

Themed App Iconに対応した画像ファイルが生成されました🎉

🎨テーマカラー - Dynamic Color

アプリテーマ テーマカラー緑
アプリテーマ 緑色を選択
青系の壁紙 赤系の壁紙
青系の壁紙 赤系の壁紙

Android13から追加された機能です。

設定を行うことで、壁紙や設定した色、テーマモードに応じてアプリのテーマカラーが切り替わります。
前述した機能のアプリテーマカラー版です。

こちらの機能は賛否両論あり、
大手ではGoogle製アプリしか対応されていない印象です。
私も 「企業(アプリ)のテーマカラーが定着し辛い」 と思い、最初は躊躇していました。

しかし、monoca2 の影響を受け、今はとても気に入ってます。
monoca2では、テーマカラーを選択でき、その一種としてダイナミックカラーが提供されています。

実装

基本はdynamic_colorパッケージで対応されています。
しかし、Flutter 3.22.0で行われたColorSchemeの変更1に対応されていません。

実際にアプリに組み込むことを想定し、2つの対応を行った例を記載します。

  • Flutter 3.22.0で行われた破壊的変更への対応
  • テーマカラーの一種として提供する方法

例では状態管理としてriverpodを利用しています。

1. dynamic_colorパッケージの追加

公式の手順を参考にパッケージの追加を行います。

2. アプリのテーマカラーを選択可能にする

まずはアプリのテーマカラーを選択できるように対応します。
テーマカラーをEnumで定義し、Notifierによって状態管理を行います。

theme_color.dart
/// ユーザーが選択可能なテーマカラー
enum ThemeColor {
  appColor(null, 'アプリ内テーマ'),
  dynamicColor(null, 'デバイスのテーマカラー'),
  blue(Colors.blue, 'ブルー'),
  purple(Colors.purple, 'パープル'),
  pink(Colors.pink, 'ピンク'),
  red(Colors.red, 'レッド'),
  orange(Colors.orange, 'オレンジ'),
  yellow(Colors.yellow, 'イエロー'),
  green(Colors.green, 'グリーン');

  const ThemeColor(this.seedColor, this.description);

  static List<ThemeColor> get colorValues =>
      values.where((i) => i.seedColor != null).toList();
  static List<ThemeColor> get systemValues =>
      values.where((i) => i.seedColor == null).toList();

  final Color? seedColor;
  final String description;
}
theme_color_notifier_provider.dart
/// アプリ内テーマカラー
/// 本来は`SharedPreferences`などで情報を永続化する
/// サンプルプログラムのためインメモリで保持
@riverpod
class ThemeColorNotifier extends _$ThemeColorNotifier {
  @override
  ThemeColor build() => ThemeColor.appColor;

  void update({required ThemeColor themeColor}) {
    state = themeColor;
  }
}

今回はインメモリで管理していますが、実際にはshared_preferencesなどを利用して、設定を永続化させます。

3. デバイスのテーマカラーを管理するProviderの定義

テーマカラーの一種として提供するのに管理し辛いため、DynamicBuilderは利用しません。

DynamicBuilderの内部実装を参考にします。
デバイスのテーマカラーを取得するためには、DynamicColorPlugin.getCorePalette()という処理を呼びします。

取得したテーマカラーをProviderで管理します。

dynamic_core_pallete_provider.dart
/// DynamicColorのCorePaletteを管理
@Riverpod(keepAlive: true)
Future<CorePalette?> dynamicCorePalette(Ref ref) async {
  // 未対応のWebでは取得処理を呼び出さない
  if (kIsWeb) {
    return null;
  }

  return DynamicColorPlugin.getCorePalette();
}

4. 利用するColorSchemeを管理するProviderを定義する

アプリ内のテーマカラーを切り替えるために、アプリ内のColorSchemeをProviderで管理します。
前述した2つのProviderを用いて、利用されるColorSchemeを決定する役割です。

まずは完成コードを記載します。

app_color_scheme_provider.dart
/// アプリ内のカラースキーマを管理
@riverpod
ColorScheme appColorScheme(Ref ref, {required Brightness brightness}) {
  // 「選択されたテーマカラー」と「デバイスのテーマカラー」を基に設定
  final themeColor = ref.watch(themeColorNotifierProvider);
  final dynamicCorePalette = ref.watch(dynamicCorePaletteProvider).value;

  // テーマカラーに応じたカラースキーマを取得
  final colorScheme = _colorScheme(brightness, themeColor, dynamicCorePalette);

  // Dynamic Colorに対応している場合、背景色とエラー関連の色を調和する
  final isDynamicColorSupported = dynamicCorePalette != null;
  return isDynamicColorSupported ? colorScheme.harmonized() : colorScheme;
}

詳細を記載していきます。

ColorSchemeの決定

// テーマカラーに応じたカラースキーマを取得
final colorScheme = _colorScheme(brightness, themeColor, dynamicCorePalette);

ここでは以下の情報を基に利用されるColorSchemeを取得しています。

  • テーマモード
  • 選択されたテーマカラー
  • デバイスのテーマカラー

詳細な条件は下記の通り記載しています。

/// テーマカラーに応じたカラースキーマ
ColorScheme _colorScheme(
  Brightness brightness,
  ThemeColor themeColor,
  CorePalette? dynamicCorePalette,
) {
  switch (themeColor) {
    case ThemeColor.appColor:
      return _defaultColorScheme(brightness);
    case ThemeColor.dynamicColor:
      // DynamicColorに対応していない場合は、アプリのテーマカラーを設定
      if (dynamicCorePalette == null) {
        return _defaultColorScheme(brightness);
      }

      // デバイスのテーマカラーを基にカラースキーマを生成
      final dynamicColorScheme =
          dynamicCorePalette.toColorScheme(brightness: brightness);

      // [暫定対応] Flutter3.22.0で行われた`ColorScheme`の破壊的変更への対応
      return generateDynamicColourSchemes(dynamicColorScheme);

    case ThemeColor.blue:
    case ThemeColor.purple:
    case ThemeColor.pink:
    case ThemeColor.red:
    case ThemeColor.orange:
    case ThemeColor.yellow:
    case ThemeColor.green:
      // テーマカラーが選択された場合は、対応する色を基に生成
      return ColorScheme.fromSeed(
        seedColor: themeColor.seedColor!,
        brightness: brightness,
      );
  }
}

generateDynamicColourSchemesは前述した破壊的変更への対応です。

デバイスのテーマカラーとの調和

// Dynamic Colorに対応している場合、エラー色を調和する
final isDynamicColorSupported = dynamicCorePalette != null;
return isDynamicColorSupported ? colorScheme.harmonized() : colorScheme;

最後に行っている処理は、エラー色の調和です。
今回は対応方法の記載なので、詳細は割愛させください。

前述したツルオカさんの記事で説明されていますので、興味のある方はぜひご覧ください。

5. アプリが利用するテーマを変更する

MaterialAppに対してProviderで管理しているColorSchemeを設定すれば完了です。

app.dart
class DynamicColorApp extends ConsumerWidget {
  const DynamicColorApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final lightColorScheme =
        ref.watch(appColorSchemeProvider(brightness: Brightness.light));
    final darkColorScheme =
        ref.watch(appColorSchemeProvider(brightness: Brightness.dark));

    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const SamplePage(),
      // Providerで管理した`ColorScheme`を基にアプリテーマを設定
      theme: ThemeData(colorScheme: lightColorScheme),
      darkTheme:
          ThemeData(colorScheme: darkColorScheme, brightness: Brightness.dark),
    );
  }
}

themeColorNotifierProviderで管理しているテーマカラーを切り替えることで、アプリの配色が変更されました🎉

🕶️【番外編】色のコントラスト - Color contrast

デフォルト
色のコントラスト:デフォルト 色のコントラスト:中 色のコントラスト:高

Android14から追加された機能です。

設定を行うことで、自分の見やすいコントラストに合わせてアプリのテーマカラーを切り替えることができます。

この機能は対応してほしいというわけではありません。
アクセシビリティのため、今後対応したい機能となります。

現状、Flutterやパッケージなどで対応されていないようですが、ネイティブ機能を呼び出すことで対応可能です。

こちらは別記事でまとめています。
興味のある方はぜひご覧下さい。

🗨️プッシュ通知 - Notification Channels

YouTube Google Play Store X (Twitter)
通知チャネル:Youtube 通知チャネル:Google Play Store 通知チャネル:X (Twitter)

Android8から追加された機能です。

対応することで、種類に応じた通知設定をAndroidの設定アプリから切り替えることができます。

通知の種類まで分かれていると理想ですが、対応規模が前述したものと比べて肥大化します。
ユーザーへの通知要件から整理しないといけないですからね。

取り上げたいのは、
「一種類しかなくともユーザーには見える」 という点です。
ユーザーにとって意味が分からない内容を表示することは避け、ユーザーフレンドリーな名称を設定したいです。

私もこちらの記事でハッとさせられました。

実装

今回はカンタンに対応できる後者への対応を例に記載します。

PUSH通知を導入する場合、firebase_messagingパッケージを利用することが多いと思います。
ですが、Androidの通知チャネルやフォアグラウンド通知は多少メンドウです。

そこで、要件を満たすのであればfcm_configパッケージをおすすめします。

通知の設定自体は趣旨が異なりますので、パッケージを参照して下さい。

本題となる通知チャネル名の設定自体は下記の1行で設定可能です。
設定の手間はほぼないので、適当な値を設定することは避けましょう。(自戒)

await FCMConfig.instance.init(
  defaultAndroidForegroundIcon: '@mipmap/ic_launcher',
  defaultAndroidChannel: AndroidNotificationChannel(
    'channel_id',
    // 本題となるチャネル名
    '[アプリ名]からのお知らせ',
    importance: Importance.high,
    sound: RawResourceAndroidNotificationSound('notification'),
  ),
);

🙇あとがき

最後まで読んでいただきありがとうございます!

主観強めの内容ではありましたが、
いちAndroidユーザーの声としてまとめてみました。

他にも「この機能が対応されていると嬉しい!」などあればコメント頂けると幸いです。
教えて頂いた機能についても、実現方法を調べたいと思います。

  1. https://docs.flutter.dev/release/breaking-changes/new-color-scheme-roles

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?