Breaking News

Default Placeholder Default Placeholder

はじめに

携帯端末のホームスクリーン(インストールしたアプリが並んでる画面)にウィジェットを表示させたいと思って home_widgetというパッケージを導入したのですが、うまくいかず、色々試行錯誤してようやくウィジェット操作をすることができました。FlutterのバージョンなのかDartのバージョンなのか、はたまた別の要因なのか、パッケージの公式ページのやり方でどうもうまくできなかったので本記事でまとめていきたいと思います。まずはAndroid版のまとめです。iOSは今後書いていこうと思います。

ウィジェットという表現を多々使いますが、本記事ではホームウィジェットのことを指します。Flutterのwidgetではないので注意

また、ウィジェットはネイティブの部分を触ることが多い箇所となります。なので、XMLファイルを作ったりkotlinを触ったりします。

目標

本記事ではFlutterのUIに設置したボタンを押下すると、Androidのホームウィジェットに現在の時間を表示する仕組みを作る

環境

  • MacOS BigSur 11.4
  • Flutter 2.5.3
  • Dart 2.14.3
  • home_widget 0.1.4

やることリスト

  • パッケージインストール
  • AndroidManifest編集
  • ウィジェットのXMLファイルを作成
  • ウィジェットレイアウトのXMLファイルを作成
  • ウィジェット背景用XMLを作成
  • ウィジェットを操作するプログラムをkotlinで作成
  • アプリ内のボタン作成

パッケージインストール

以下のコマンドを使ってライブラリをインストールします。

flutter pub add home_widget

AndroidManifest編集

android/app/src/main/AndroidManifest.xmlreceiverの設定を行います。

以下のここからここまでという部分をAndroidManifestに貼り付けてください。ファイル名は適宜変更してください。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.test">
   <application
        android:label="test"
        android:icon="@mipmap/ic_launcher">
       <!-- ここから -->
        <receiver android:name="TestHomeWidgetProvider" > <!-- ① -->
          <intent-filter>
              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
          </intent-filter>
          <meta-data android:name="android.appwidget.provider"
              android:resource="@xml/test_home_widget_example" /> <!-- ② -->
        </receiver>
<!-- ここまで -->

① ウィジェットを操作するkotlinのファイル名

② ウィジェットの定義を記載するXMLファイル名

ウィジェットの定義を記載するXMLファイルを作成

↑の②で出てきたファイルを作っていきます。

android/app/src/main/res/xml/test_home_widget_example.xmlを作成してください。↑の②に記載したのと同じ文字列をファイル名にしてください。

ファイルの中身はこんな感じにしてください。

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="320dp"
    android:minHeight="320dp"
    android:updatePeriodMillis="900000"
    android:initialLayout="@layout/test_layout"
    android:widgetCategory="home_screen">
</appwidget-provider>
  • minWidthminHeightはウィジェットのサイズを指します。こちらを参考にしてください。

https://stuff.mit.edu/afs/sipb/project/android/docs/guide/practices/ui_guidelines/widget_design.html#:~:text=Determining%20a%20size%20for%20your%20widget から抜粋

  • updatePeriodMillsはウィジェットの自動更新間隔を示しています。例は900000となっているので900秒、つまり15分となっています。
  • initialLayoutはウィジェットが作成されたときに自動で適用されるレイアウトファイル名を指定しています。ここでは test_layoutという名前のレイアウトファイルを使うよう定義されています。

ウィジェットレイアウトのXMLファイルを作成

ウィジェットの見た目を決めるレイアウトXMLを作ります。

android/app/src/main/res/layout/test_layout.xmlを作成してください。

中身はテキストを表示するだけのシンプルなものにします。

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="8dp"
    android:orientation="vertical"
    android:background="@drawable/widget_background"
    android:padding="8dp"
    android:id="@+id/widget_container">
    <TextView
        android:id="@+id/widget_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="36sp"
        android:textStyle="bold"
        tools:text="Text" />
</LinearLayout>

ウィジェット背景用XMLを作成

↑の android:backgroundで指定した背景用のXMLを作成します。ここに背景色などを書くことでウィジェットの背景のデザインが決まります。

android/app/src/main/res/drawable/widget_background.xmlを作成してください。中身は単純に背景色 の設定とウィジェットの周りをちょっと丸みを帯びた形にする設定をしていきます。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF"/>
    <corners android:radius="16dp"/>
</shape>

ウィジェットを操作するプログラムをkotlinで作成

kotlinである必要はないのですが、ここではkotlinでのやり方を書いていきます。

android/app/src/main/kotlin/com/example/test/TestHomeWidgetProvider.ktを作成してください。

TestHomeWidgetProviderを覚えていますでしょうか?最初に編集した AndroidManifest.xmlに記載したものと同じですね。ここが異なっているとうまく動かないのでファイル名(というかクラス名)には注意しましょう。

このkotlinファイルには以下を記載してください。

package com.example.test
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.widget.RemoteViews
import es.antonborri.home_widget.HomeWidgetBackgroundIntent
import es.antonborri.home_widget.HomeWidgetLaunchIntent
import es.antonborri.home_widget.HomeWidgetProvider

class TestHomeWidgetProvider : HomeWidgetProvider() {

    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, widgetData: SharedPreferences) {
        appWidgetIds.forEach { widgetId ->
            val views = RemoteViews(context.packageName, R.layout.test_layout).apply {
                setTextViewText(R.id.widget_text, widgetData.getString("text", null)
                        ?: "No Text Set")
            }

            appWidgetManager.updateAppWidget(widgetId, views)
        }
    }
}

アプリ内のボタン作成

ここでようやくFlutterの登場です。Flutterで、押下するとその時の日時をウィジェットに表示するボタンを作っていきます。

以下を貼っていただき、皆さんが作成したボタンのonPressedでこの_sendAndUpdateメソッドを呼んでいただくだけで動きます。

Future<void> _sendData() async {
    DateTime now = DateTime.now();
    try {
      await Future.wait([
        HomeWidget.saveWidgetData<String>('text', now.toString()),
      ]);
    } on PlatformException catch (exception) {
      debugPrint('Error Sending Data. $exception');
    }
  }

Future<void> _updateWidget() async {
    try {
      await HomeWidget.updateWidget(
          name: 'TestHomeWidgetProvider',
          androidName: 'TestHomeWidgetProvider',
          iOSName: 'TestHomeWidgetProvider');
    } on PlatformException catch (exception) {
      debugPrint('Error Updating Widget. $exception');
    }
  }

Future<void> _sendAndUpdate() async {
    await _sendData();
    await _updateWidget();
  }

ボタンを押下するとこんな感じになります。

以上、長々となりましたが、Flutter x Android x home_widgetパッケージでホームウィジェットを操作する方法まとめでした。最後までお読みいただきありがとうございました。