はじめに
React Nativeで一からウィジェットを作ろうとすると、ネイティブコードが必要で大変💦
そこでこの記事では、ネイティブコードを書かずに簡単にAndroid用のウィジェットを実装する方法をまとめる😊
作るもの
「Hello」と表示するだけの最小限のウィジェットを作成する。
結論
✅パッケージ「React Native Android Widget」を使う。
✅TypeScript(JavaScript)のファイルを編集するだけで実装できる。
React Native Android Widgetって何?
✅Androidのウィジェットを簡単に作れるパッケージ。
iOSは非対応?
✅Androidのみ対応。
(iOSは別途作成が必要。今回はAndroidのみ解説する。)
iOS用のパッケージもいくつか存在する。
最小限のウィジェットの作り方
以下のとおり順番に真似すれば最小限のウィジェットが作れる😊
準備(使用する手法・技術)
事前に用意しておくもの
- React Native(Expo)のプロジェクト
今からインストールするもの
- React Native Android Widget
【手順1】React Native Android Widgetをインストールする
npm install --save react-native-android-widget
【手順2】ウィジェットのコンポーネントを作成する
✅「Hello」と表示するコンポーネントを作成する。
components/widget/HelloWidget.tsx
(ファイルの場所はどこでもOK。コピペでOK。)
import React from 'react';
// ウィジェット専用のコンポーネント
import { FlexWidget, TextWidget } from 'react-native-android-widget';
export function HelloWidget() {
return (
<FlexWidget
style={{
height: 'match_parent',
width: 'match_parent',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ffffff',
borderRadius: 16,
}}
>
<TextWidget
text="Hello"
style={{
fontSize: 32,
fontFamily: 'Inter',
color: '#000000',
}}
/>
</FlexWidget>
);
}
【解説】ウィジェット専用のコンポーネント
// ウィジェット専用のコンポーネント
import { FlexWidget, TextWidget } from 'react-native-android-widget';
公式ドキュメントに記載されているウィジェット専用のコンポーネントを使う必要がある!
【手順3】タスクハンドラーを作成する
✅ウィジェットにはタスクハンドラーが必須。
【補足】タスクハンドラーとは
主に2つの役割をするファイル。
- ウィジェットをホーム画面に追加/更新するロジック
- ウィジェットのクリック処理
components/widget/widget-task-handler.tsx
(ファイルの場所はどこでもOK。コピペでOK。)
import React from 'react';
import type { WidgetTaskHandlerProps } from 'react-native-android-widget';
import { HelloWidget } from './HelloWidget';
// ①ウィジェットの名前 と コンポーネント の紐づけ
const nameToWidget = {
Hello: HelloWidget,
};
export async function widgetTaskHandler(props: WidgetTaskHandlerProps) {
const widgetInfo = props.widgetInfo;
const Widget =
nameToWidget[widgetInfo.widgetName as keyof typeof nameToWidget];
// ②イベント処理
switch (props.widgetAction) {
case 'WIDGET_ADDED':
props.renderWidget(<Widget />);
break;
case 'WIDGET_UPDATE':
// Not needed for now
break;
case 'WIDGET_RESIZED':
// Not needed for now
break;
case 'WIDGET_DELETED':
// Not needed for now
break;
case 'WIDGET_CLICK':
// Not needed for now
break;
default:
break;
}
}
【解説】①ウィジェットの名前 と コンポーネント の紐づけ
// ①ウィジェットの名前 と コンポーネント の紐づけ
const nameToWidget = {
Hello: HelloWidget,
};
先ほど作ったコンポーネント「HelloWidget」を「Hello」というウィジェット名に設定する。
【解説】②イベント処理
// ②イベント処理
switch (props.widgetAction) {
case 'WIDGET_ADDED':
props.renderWidget(<Widget />);
break;
case 'WIDGET_UPDATE':
// Not needed for now
break;
case 'WIDGET_RESIZED':
// Not needed for now
break;
case 'WIDGET_DELETED':
// Not needed for now
break;
case 'WIDGET_CLICK':
// Not needed for now
break;
default:
break;
}
ウィジェットを追加/更新/リサイズ/削除/クリックしたときの処理を定義する。
【手順4】タスクハンドラーを登録する
✅先ほど作ったタスクハンドラーを登録する。
-
エントリーポイントをindex.tsに変更する。
package.json
{ "name": "my-expo-app", "main": "index.ts", // これ ... }
-
空のindex.tsファイルを作成する。
-
一旦、デフォルトのエントリーポイント「node_modules/expo/AppEntry.js」をコピペする。
index.ts
import registerRootComponent from 'expo/build/launch/registerRootComponent'; // パスは変更する import App from '.App'; registerRootComponent(App);
-
タスクハンドラーを登録する処理を追記する。
index.ts
import registerRootComponent from 'expo/build/launch/registerRootComponent'; // ✅追記 import { registerWidgetTaskHandler } from 'react-native-android-widget'; import App from './App'; // ✅追記 import { widgetTaskHandler } from './widget-task-handler'; registerRootComponent(App); // ✅追記 registerWidgetTaskHandler(widgetTaskHandler);
【補足】素のReact Nativeの場合
基本は上記と同じ。
index.tsの内容だけ以下に変更する。
import { AppRegistry } from 'react-native';
import { registerWidgetTaskHandler } from 'react-native-android-widget';
import { name as appName } from './app.json';
import App from './App';
import { widgetTaskHandler } from './widget-task-handler';
AppRegistry.registerComponent(appName, () => App);
registerWidgetTaskHandler(widgetTaskHandler);
【補足】Expo Routerを使用している場合
基本は上記と同じ。
index.tsの内容だけ以下に変更する。
(node_modules/expo-router/entry.jsを元にして、registerWidgetTaskHandlerを追記する。)
// `@expo/metro-runtime` MUST be the first import to ensure Fast Refresh works
// on web.
import '@expo/metro-runtime';
import { App } from 'expo-router/build/qualified-entry';
import { renderRootComponent } from 'expo-router/build/renderRootComponent';
import { registerWidgetTaskHandler } from 'react-native-android-widget';
import { widgetTaskHandler } from '@/components/widget/widget-task-handler';
// This file should only import and register the root. No components or exports
// should be added here.
renderRootComponent(App);
registerWidgetTaskHandler(widgetTaskHandler);
【手順5】ウィジェットを登録する
✅app.config.tsにウィジェットの情報を登録するだけでOK。
(Expoのconfig pluginがサポートされているので簡単に実装できる。)
app.config.ts
import type { ConfigContext, ExpoConfig } from 'expo/config';
import type { WithAndroidWidgetsParams } from 'react-native-android-widget';
const widgetConfig: WithAndroidWidgetsParams = {
// 【任意】カスタムフォントのパス(アイコンウィジェットを使う場合に必要)
// fonts: ['./assets/fonts/Inter.ttf'],
widgets: [
{
name: 'Hello', // ウィジェットの名前(タスクハンドラーに書いたウィジェット名に合わせる)
label: 'My Hello Widget', // ウィジェットピッカーに表示されるラベル
minWidth: '320dp',
minHeight: '120dp',
// これは、ウィジェットのデフォルトのサイズがtargetCellWidthとtargetCellHeight属性で指定された5x2セルであることを意味します。
// または、Android 11以下のデバイスではminWidthとminHeightで指定された320×120dpであることを意味します。
// 定義されている場合、targetCellWidthとtargetCellHeightの属性はminWidthやminHeightの代わりに使用されます。
targetCellWidth: 5,
targetCellHeight: 2,
description: 'This is my first widget', // ウィジェットピッカーに表示される説明
previewImage: './assets/widget-preview/hello.png', // 【任意】ウィジェットプレビュー画像へのパス
// このAppWidgetが更新される頻度(ミリ秒単位)。
// タスクハンドラはwidgetAction = 'UPDATE_WIDGET'で呼び出されます。
// デフォルトは0(自動更新なし)
// 最小は1800000(30分 == 30 * 60 * 1000)。
updatePeriodMillis: 1800000,
},
],
};
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: 'My Expo App Name',
plugins: [['react-native-android-widget', widgetConfig]],
});
【補足】素のReact Nativeの場合
【手順6】実行する
✅Expo Goでは実行できないため、開発ビルドで動作確認する。
-
開発ビルドのビルド設定を追加
eas.json
{ "build": { ... // 追記 "development": { "developmentClient": true, "distribution": "internal", "android": { "buildType": "apk" } }, ... }, }
-
開発ビルドで必要なパッケージ「expo-dev-client」をインストール
npx expo install expo-dev-client
-
開発ビルドを実行
※EASのアカウントが必要(無料)
eas build --platform android --profile development
-
ビルドが完成したら実機またはエミュレータにインストールする。
(ビルド結果のWebページからapkファイルをダウンロードすればOK)
-
動作確認
開発PCで
npx expo start --dev-client
を実行した状態で、実機またはエミュレータでウィジェットが追加できるか動作確認する。
【補足】素のReact Nativeの場合
普通にアプリを再ビルドして実機またはエミュレータにインストールすればOK。
まとめ
React Native Android Widgetを使って、ネイティブコードを書かずにウィジェットを作ることができた✨
ポイント
- 見た目やロジックをJavaScriptで書くことができる。
- ネイティブコードを知らなくてもウィジェットが作れる。
- 順番にコピペするだけで動作するので敷居が低い。
またウィジェットの設定画面の作成などもできるので、実用的なウィジェットも作成できそう😊