Android 13 以降で .NET MAUI アプリを作っていると、「AndroidManifest に POST_NOTIFICATIONS は書いてあるのに、通知許可ダイアログが一度も出ない」「ユーザーが設定アプリから手動で通知を ON にしないとプッシュ通知が届かない」といった状況になりがちです。本記事では、その原因と .NET MAUI での具体的な実装方法を、サンプルコードとチェックリスト付きで詳しく解説します。
Android(.NET MAUI)で通知許可ダイアログが出ない典型的な症状
まず、よくある状況を整理しておきます。
- Android 13 以上の端末でアプリをインストール
- 初回起動しても「通知を許可しますか?」ダイアログが表示されない
- FCM やローカル通知を送っても、画面に何も出てこない
- 設定アプリ → アプリ名 → 通知 を開き、ユーザーが手動で ON にすると急に通知が届くようになる
そして AndroidManifest.xml には、たとえば次のように権限が書いてあるはずです。
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
それでも「許可しますか?」が出ない理由は、Android 13 以降の通知が 実行時権限(Runtime Permission) になったことと、.NET MAUI アプリ側でその要求処理を実装していないことにあります。
原因:Android 13 の通知は「実行時権限」で、アプリが明示的にリクエストしないとダイアログは出ない
Android 13(API 33)からは、通知もカメラや位置情報と同様に「危険権限」として扱われます。つまり:
- Manifest に宣言するだけでは不十分
- アプリが実行中に 明示的に権限リクエスト API を呼び出したタイミングで、システムが通知許可ダイアログを表示する
- アプリ側がリクエストを呼ばなければ、ダイアログは一度も出ない
Android バージョンごとの違いをざっくり整理すると、次のようになります。
| Android バージョン | 通知の扱い | 必要な実装 |
|---|---|---|
| Android 13 以降(API 33+) | 通知は実行時権限(POST_NOTIFICATIONS) | Manifest に権限を宣言し、アプリ起動後に 権限を明示的にリクエスト する |
| Android 8~12 | POST_NOTIFICATIONS の実行時許可は不要だが、通知チャンネルは必須 | Manifest の権限宣言+通知チャンネルの作成。ユーザーが通知を OFF にしている可能性あり |
| Android 7 以前 | 通知チャンネルなし、POST_NOTIFICATIONS もなし | 古い端末向けの基本的な通知実装のみ |
つまり「Manifest に書いておけば OS がよしなにダイアログを出してくれる」というのは、Android 13 以降では通用しません。.NET MAUI アプリ側で「今から通知を使うので、権限をください」とお願いするコードを書く必要があるということです。
.NET MAUI アプリでまず確認すべき設定(Manifest / target / compile)
.NET MAUI の通知権限まわりで最低限チェックしたい項目をまとめると、次の 3 つです。
| 項目 | チェック内容 | 例 |
|---|---|---|
| Manifest の権限宣言 | POST_NOTIFICATIONS が宣言されているか | <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> |
| targetSdkVersion | 33 以上になっているか | .csproj に <TargetSdkVersion>33</TargetSdkVersion> 以上を指定 |
| compileSdkVersion | 33 以上でビルドされているか | 通常は .NET MAUI の SDK に合わせて自動設定されるが、必要に応じて調整 |
AndroidManifest.xml の確認
.NET MAUI では、Android 固有の設定は Platforms/Android/AndroidManifest.xml に記述します。通知権限として、次の行が入っているか確認します。
<manifest ...>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application ...>
...
</application>
</manifest>
targetSdkVersion / compileSdkVersion を 33 以上にする
Android 13 の通知権限を正しく扱うには、targetSdkVersion / compileSdkVersion を 33 以上 にしておくことが重要です。多くの場合は .NET MAUI テンプレートがよしなに設定してくれますが、もし明示的に指定したい場合は .csproj に次のように記述します。
<PropertyGroup>
<TargetFramework>net8.0-android</TargetFramework>
<UseMaui>true</UseMaui>
<ApplicationId>com.example.maui.notifications</ApplicationId>
<TargetSdkVersion>34</TargetSdkVersion>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
</PropertyGroup>
ここで <TargetSdkVersion> を 33 以上にしておけば、Android 13 以降の通知権限が正しく適用されます。
実装パターン 1:.NET MAUI 標準 Permissions API(Permissions.PostNotifications)を使う
.NET MAUI には Permissions API が用意されており、権限の確認やリクエストを共通の書き方で扱えます。通知権限の型(例:Permissions.PostNotifications)が利用できる場合は、これを使うのが最もシンプルです。
基本フロー
- 現在の権限状態を確認する
- 未許可であれば、必要に応じてラショナル(説明ダイアログ)を表示する
- 権限をリクエストする
- 結果に応じて通知の処理を続行/中断する
サンプルコード:通知権限をリクエストするメソッド
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls;
public static class NotificationPermissionHelper
{
public static async Task<PermissionStatus> EnsureNotificationPermissionAsync()
{
// すでに許可済みか確認
var status = await Permissions.CheckStatusAsync<Permissions.PostNotifications>();
if (status == PermissionStatus.Granted)
{
return status;
}
// ラショナル(事前説明)が必要かどうか
if (Permissions.ShouldShowRationale<Permissions.PostNotifications>())
{
await MainThread.InvokeOnMainThreadAsync(async () =>
{
await Application.Current.MainPage.DisplayAlert(
"通知の許可のお願い",
"お知らせや更新情報を受け取るには、通知を許可してください。",
"OK");
});
}
// 実際に権限をリクエスト
status = await Permissions.RequestAsync<Permissions.PostNotifications>();
return status;
}
}
ボタン操作に紐づけてダイアログを出す例
「通知を受け取る」ボタンをタップしたタイミングで権限を聞く実装例です。
private async void OnSubscribeButtonClicked(object sender, EventArgs e)
{
var status = await NotificationPermissionHelper.EnsureNotificationPermissionAsync();
if (status == PermissionStatus.Granted)
{
// 通知トークンの取得やサーバー登録などを実行
await DisplayAlert("完了", "通知が許可されました。", "OK");
}
else
{
await DisplayAlert("通知が許可されませんでした",
"後から設定画面で通知を ON にすることもできます。", "OK");
}
}
このように「文脈のある操作」に紐づけて権限ダイアログを出すと、ユーザーにとって自然な体験になります。
実装パターン 2:カスタム NotificationPermission クラスを定義する
環境によっては Permissions.PostNotifications がまだ提供されていない、あるいは Android 13 未満では何もしない といった細かい制御を行いたい場合もあります。その場合は、自分で権限クラスを定義する方法が便利です。
Platforms/Android/NotificationPermission.cs
using Microsoft.Maui.ApplicationModel;
using Android;
using Android.OS;
public class NotificationPermission : Permissions.BasePlatformPermission
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions
=> OperatingSystem.IsAndroidVersionAtLeast(33)
? new[] { (Manifest.Permission.PostNotifications, true) }
: Array.Empty<(string androidPermission, bool isRuntime)>();
}
このクラスでは、Android 13(API 33)以上のときだけ POST_NOTIFICATIONS を実行時権限として扱い、それ未満のバージョンでは何もしないようにしています。
カスタム権限クラスを使ったリクエスト例
public static class NotificationPermissionHelper
{
public static async Task<PermissionStatus> EnsureNotificationPermissionAsync()
{
var status = await Permissions.CheckStatusAsync<NotificationPermission>();
if (status == PermissionStatus.Granted)
{
return status;
}
if (Permissions.ShouldShowRationale<NotificationPermission>())
{
await MainThread.InvokeOnMainThreadAsync(async () =>
{
await Application.Current.MainPage.DisplayAlert(
"通知の許可のお願い",
"プッシュ通知を受け取るには通知を許可してください。",
"OK");
});
}
status = await Permissions.RequestAsync<NotificationPermission>();
return status;
}
}
使い方は、標準の Permissions.PostNotifications とほぼ同じです。
権限ダイアログを出す「タイミング」と UX の考え方
通知権限は、いつリクエストしても技術的には動作しますが、ユーザー体験の面ではタイミングが非常に重要です。
おすすめのリクエストタイミング
- 通知が必要になる操作を行った直後
- 例:ベルアイコンをタップした、ユーザーをフォローした、注文を確定した
- 機能紹介画面やオンボーディングの 2~3 画面目
- 「◯◯のお知らせを受け取ると便利ですよ」と説明した直後に権限を聞く
- 通知に関連する設定画面を開いたとき
- 「プッシュ通知」トグルを ON にしようとした瞬間にダイアログを表示
逆に避けたいのは、次のようなパターンです。
- アプリ起動直後、何の説明もなく権限ダイアログを連発する
- ユーザーが何のメリットも理解していない段階で「通知を許可してください」とだけ表示する
比較しやすいように、簡単な表にまとめると次のようになります。
| タイミング | メリット | デメリット |
|---|---|---|
| 起動直後 | 実装は簡単 | 拒否されやすい、意味が伝わらない |
| 関連機能の操作直後 | なぜ必要かが伝わりやすく、許可率が高い | コード上の実装ポイントが増える |
| 設定画面でのトグル操作時 | ユーザーが自分の意思で通知を ON にしようとしている瞬間なので自然 | 設定画面を開かないユーザーには届きにくい |
ユーザーに拒否された場合の扱いと、通知設定画面への誘導
通知権限は、一度ユーザーに明示的に拒否されると、アプリ側から再度ダイアログを出せない状態(いわゆる「今後は表示しない」)になることがあります。その場合は、アプリ固有の通知設定画面へ誘導する導線を用意するのが定番です。
Android のアプリ通知設定画面を開くヘルパー
using Android.Content;
using Android.Provider;
using Microsoft.Maui.ApplicationModel;
public static class AndroidNotificationSettings
{
public static void OpenAppNotificationSettings()
{
#if ANDROID
var activity = Platform.CurrentActivity;
if (activity is null) return;
var intent = new Intent(Settings.ActionAppNotificationSettings)
.PutExtra(Settings.ExtraAppPackage, activity.PackageName);
activity.StartActivity(intent);
#endif
}
}
これを利用して、権限が拒否されている場合にダイアログから誘導することができます。
private async Task ShowNotificationDeniedDialogAsync()
{
bool openSettings = await DisplayAlert(
"通知が無効になっています",
"通知を受け取るには、端末の設定から通知を有効にしてください。",
"設定を開く",
"閉じる");
if (openSettings)
{
AndroidNotificationSettings.OpenAppNotificationSettings();
}
}
権限リクエストの結果が PermissionStatus.Denied の場合にこのダイアログを呼び出すことで、ユーザーに「どこを直せばよいか」を明確に示すことができます。
通知チャンネル(Notification Channel)の作成と重要度設定
Android 8.0(API 26)以降では、通知を表示するには 通知チャンネル の作成が必須です。チャンネルを作成していない、または重要度(importance)が低すぎると、通知が届いていてもユーザーの目に触れないことがあります。
通知チャンネル作成のサンプル(.NET MAUI / Android)
using Android.App;
using Android.Content;
using Android.OS;
using Microsoft.Maui.ApplicationModel;
public static class NotificationChannelHelper
{
public const string DefaultChannelId = "default";
public static void CreateDefaultChannel()
{
#if ANDROID
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
return;
var context = Platform.CurrentActivity ?? Application.Context;
var channel = new NotificationChannel(
DefaultChannelId,
"一般通知",
NotificationImportance.Default)
{
Description = "アプリからのお知らせを表示します。"
};
var manager = (NotificationManager)context.GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(channel);
#endif
}
}
このメソッドは、アプリ起動時(たとえば MainActivity.OnCreate 内)で一度だけ呼び出しておけば十分です。
重要度(importance)の注意点
- NotificationImportance.Min / Low にすると、バナー表示されず、ステータスバーにもほとんど出ない
- 一度作成したチャンネルの重要度は、コードから変更できない
- 変更したい場合は、アプリの再インストールか、ユーザーに設定アプリから変更してもらう必要がある
「通知を送っているはずなのに、どうしても画面に出てこない」というときは、チャンネルの作成状況と重要度を必ず確認しましょう。
簡単なローカル通知の送信サンプル
権限とチャンネルが正しく設定できているかを確認するために、ローカル通知を送ってみると切り分けがしやすくなります。
using Android.App;
using Android.Content;
using AndroidX.Core.App;
using Microsoft.Maui.ApplicationModel;
public static class LocalNotificationTester
{
public static void ShowTestNotification()
{
#if ANDROID
var context = Platform.CurrentActivity ?? Application.Context;
var builder = new NotificationCompat.Builder(context, NotificationChannelHelper.DefaultChannelId)
.SetContentTitle("テスト通知")
.SetContentText("通知権限とチャンネルの設定は正しく動作しています。")
.SetSmallIcon(Resource.Drawable.ic_stat_notify) // 適切なアイコンに置き換え
.SetAutoCancel(true);
var manager = NotificationManagerCompat.From(context);
manager.Notify(1001, builder.Build());
#endif
}
}
テストボタンのクリックイベントで ShowTestNotification() を呼び出し、通知が実際に表示されるかどうかを確認してみてください。
それでも通知が出ない場合のチェックリスト
ここまで実装しても通知が出ない場合、次のチェックリストを順番に確認すると原因を特定しやすくなります。
| 確認項目 | よくある原因 | チェック方法 |
|---|---|---|
| POST_NOTIFICATIONS 権限 | Manifest に宣言されていない/実行時リクエストをしていない | コードと AndroidManifest.xml を確認し、権限リクエストが呼ばれているかログで追跡 |
| 通知チャンネル | チャンネル未作成/重要度が低すぎる | ローカル通知テストでチャンネル名が表示されるか、設定アプリからチャンネル一覧を確認 |
| アプリ全体の通知 ON/OFF | ユーザーが設定で通知を OFF にしている | 端末の「アプリ情報」→「通知」でアプリの通知が有効か確認 |
| OS バージョン差分 | Android 13 未満でも同じコードで扱おうとしている | 端末バージョンを確認し、Android 13 以外では POST_NOTIFICATIONS をリクエストしないようにする |
| 端末メーカー独自の省電力設定 | バックグラウンド制限などでプッシュ通知がブロックされている | 別の端末/エミュレーターで再現するか確認し、端末設定も確認 |
テスト時に権限状態をリセットするコマンド例
開発中に何度もテストする場合、端末に残った権限状態が影響することがあります。ADB を使って権限をリセットすると、初回起動時の挙動を何度でも再現できます。
# POST_NOTIFICATIONS 権限を再度聞き直したい場合
adb shell pm revoke your.package.name android.permission.POST_NOTIFICATIONS
# アプリをアンインストールして完全に状態をリセットする場合
adb uninstall your.package.name
your.package.name の部分は、実際の ApplicationId(例:com.example.maui.notifications)に置き換えてください。
.NET MAUI での実装フローをまとめたサンプル構成
ここまでの内容を、シンプルなサンプルフローとしてまとめると次のようになります。
- 起動時(MainActivity.OnCreate など)で通知チャンネルを作成する
- ユーザーの操作(「通知を受け取る」ボタンなど)に合わせて通知権限をリクエストする
- 権限が許可されたら、FCM 登録やローカル通知の送信を行う
- 拒否されたら、ヘルプダイアログから通知設定画面への導線を用意する
MainPage の簡易サンプル
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void OnTestNotificationClicked(object sender, EventArgs e)
{
// 1. 通知権限を確認・リクエスト
var status = await NotificationPermissionHelper.EnsureNotificationPermissionAsync();
if (status == PermissionStatus.Granted)
{
// 2. テスト通知を送信
LocalNotificationTester.ShowTestNotification();
}
else
{
// 3. 拒否されている場合は通知設定画面への導線を用意
await DisplayAlert(
"通知が無効になっています",
"通知が拒否されているため、テスト通知を表示できませんでした。",
"OK");
await ShowNotificationDeniedDialogAsync();
}
}
}
このように、「権限チェック → 必要ならラショナル → 権限リクエスト → 結果に応じた分岐」 の流れを 1 つのヘルパーにまとめておくと、画面が増えても再利用しやすくなります。
まとめ:Manifest だけでは足りない。実行時権限+通知チャンネルまで実装しよう
Android 13 以降で .NET MAUI アプリの通知がうまく出ない原因は、多くの場合次の 3 点に集約されます。
- 通知は実行時権限になったのに、アプリ側で権限リクエストをしていない
- POST_NOTIFICATIONS の宣言はあるが、target/compile が 33 未満のまま
- 通知チャンネルの作成や重要度設定が不適切
逆に言えば、以下のポイントを押さえて実装すれば、通知許可ダイアログが出ない問題は解消できます。
- AndroidManifest.xml に
POST_NOTIFICATIONSを宣言する - targetSdkVersion / compileSdkVersion を 33 以上に設定する
- .NET MAUI の Permissions API(またはカスタム権限クラス)で 実行時に通知権限をリクエスト する
- Android 8 以降では通知チャンネルを作成し、適切な重要度を設定する
- 拒否された場合のために、アプリの通知設定画面への導線を用意する
これらを一度しっかり組み込んでおけば、新しい画面や機能を追加するときにも同じパターンを適用でき、Android 13 以降の端末でも安定してプッシュ通知を届けられるようになります。

コメント