はじめに
React Nativeで一からウィジェットを作ろうとすると、ネイティブコードが必要で大変💦
そこでこの記事では、ネイティブコードを書かずに簡単にAndroid用のウィジェットを実装する方法をまとめる😊
作るもの
結論
✅パッケージ「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]],
});
【手順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で書くことができる。
 - ネイティブコードを知らなくてもウィジェットが作れる。
 - 順番にコピペするだけで動作するので敷居が低い。
 
またウィジェットの設定画面の作成などもできるので、実用的なウィジェットも作成できそう😊








