mcpField
M
maui-permissions

maui-permissions

github.com/davidortinau开发17663 次安装16 周前更新
代码仓库

关于 maui-permissions

面向 .NET MAUI 开发者的权限处理指南,整理常见陷阱与推荐模式,涵盖 iOS、Android 和 Windows 的平台差异。

主要功能

  1. 01请求前检查权限状态
  2. 02处理拒绝、受限和有限状态
  3. 03考虑 iOS 与 Android 行为差异
  4. 04使用平台对应的权限声明

适用场景

  1. 01避免重复请求已授予的权限
  2. 02拒绝后引导 iOS 用户前往设置
  3. 03重新请求前显示 Android 说明
  4. 04在 Android 13+ 选择媒体权限

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/davidortinau/maui-skills --skill maui-permissions

安装后在 Claude Code 中输入/maui-permissions确认已加载。

附带文件references/permissions-api.md

详细介绍

.NET MAUI 权限——注意事项与最佳实践

关键反模式

1. 未先检查就请求
// ❌ Shows prompt even if already granted
var status = await Permissions.RequestAsync<Permissions.Camera>();

// ✅ Check first — avoids unnecessary prompts
var status = await Permissions.CheckStatusAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
    status = await Permissions.RequestAsync<Permissions.Camera>();
2. 在构造函数中调用权限 API

权限 API 是异步的,需要 UI 上下文。构造函数中无法使用 await。

// ❌ Blocks the UI thread or throws
public MyViewModel()
{
    var status = Permissions.RequestAsync<Permissions.Camera>().Result;
}

// ✅ Use OnAppearing, a command, or async initialization
protected override async void OnAppearing()
{
    await CheckAndRequestPermissionAsync<Permissions.Camera>();
}
3. 忽略 Denied 和 Restricted 状态
// ❌ Only checks for Granted — misses edge cases
if (status == PermissionStatus.Granted) { /* proceed */ }

// ✅ Handle all relevant statuses
switch (status)
{
    case PermissionStatus.Granted: /* proceed */ break;
    case PermissionStatus.Limited: /* iOS partial access */ break;
    case PermissionStatus.Denied:
    case PermissionStatus.Restricted:
        await ShowSettingsPromptAsync(); break;
}

平台注意事项

⚠️ iOS:权限对话框只显示一次

iOS 对每种权限只会显示一次系统权限对话框。一旦用户拒绝,RequestAsync 就会立即返回 Denied,而不会显示界面。你必须引导用户前往设置 → App → 权限。

⚠️ Android:ShouldShowRationale 的调用时机

ShouldShowRationale 仅会在用户之前拒绝过权限后返回 true(但用户选择“不再询问”后不会)。再次请求前,可使用它显示说明界面:

if (Permissions.ShouldShowRationale<Permissions.Camera>())
{
    await Shell.Current.DisplayAlert("Permission needed",
        "Camera access is required to scan barcodes.", "OK");
}
status = await Permissions.RequestAsync<Permissions.Camera>();
⚠️ Android API 33+:StorageRead/StorageWrite 已失效

在 Android 13+ 上,StorageRead 和 StorageWrite 始终返回 Granted(分区存储使它们失去意义)。请改用细分的媒体权限:

// ❌ Always returns Granted on API 33+ — gives false confidence
await Permissions.RequestAsync<Permissions.StorageRead>();

// ✅ Use the specific media permission
await Permissions.RequestAsync<Permissions.Photos>();       // photo access
await Permissions.RequestAsync<Permissions.Media>();         // audio/video
⚠️ Windows:大多数权限始终返回 Granted

Windows 的大多数功能没有运行时权限对话框。请改为在 Package.appxmanifest 中声明功能。

始终先检查再请求的模式

推荐的模式可处理 iOS 权限对话框只显示一次的情况,以及 Android 的理由提示:

public async Task<PermissionStatus> CheckAndRequestPermissionAsync<T>()
    where T : Permissions.BasePermission, new()
{
    var status = await Permissions.CheckStatusAsync<T>();
    if (status == PermissionStatus.Granted)
        return status;

    if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
        return status; // iOS won't show dialog again

    if (Permissions.ShouldShowRationale<T>())
    {
        await Shell.Current.DisplayAlert("Permission needed",
            "This feature requires the requested permission.", "OK");
    }

    return await Permissions.RequestAsync<T>();
}

检查清单

  • 每次调用 RequestAsync 前都调用 CheckStatusAsync
  • 不在构造函数中调用权限 API——使用 OnAppearing 或命令
  • 处理所有 PermissionStatus 值(Denied、Restricted、Limited)
  • Android:再次请求前显示 ShouldShowRationale 提示
  • iOS:为被拒绝的权限提供前往“设置”的引导
  • Android API 33+:使用 Photos/Media,而不是 StorageRead/StorageWrite
  • 清单/plist 声明与运行时权限请求相匹配

评论

支持 Markdown