You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter应用启动时集成permission_handler权限处理及相关报错解决

Flutter启动存储权限申请问题修复方案

根因分析&修复思路

对应遇到的4个问题,处理逻辑如下:

  • 重复申请权限报错:将权限校验的Future预先存在State变量中,避免FutureBuilder重绘时重复触发校验逻辑
  • 从设置页返回不识别权限:监听应用生命周期resume事件,用户从设置页返回应用时自动重新校验权限状态
  • 不再询问后无限循环:权限申请前判断isPermanentlyDenied状态,该状态下直接弹出引导去设置的弹窗,不再调用系统权限申请接口
  • 启动加载页体验差:将闪屏页作为默认展示的UI,异步做权限校验,校验过程中用户看到的是正常闪屏,不会出现加载圈或者黑屏
  • 高版本Android权限适配:Android13及以上存储权限规则变更,需要在Manifest中补充对应权限声明,解决no permissions defined报错

代码修改

1. AndroidManifest.xml权限适配

补充Android13及以上的媒体权限声明,兼容全版本系统:

<!--  Android 12及以下  -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!--  Android 13及以上  -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

2. permission_handler.dart完整修改代码

import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:status_saver/utils/splash_screen.dart';

class PermissionHandler extends StatefulWidget {
  const PermissionHandler({Key? key}) : super(key: key);

  @override
  _PermissionHandlerState createState() => _PermissionHandlerState();
}

class _PermissionHandlerState extends State<PermissionHandler> with WidgetsBindingObserver {
  // 预先存储权限校验Future,避免重复触发
  late Future<void> _permissionCheckFuture;
  // 权限状态标记
  bool _isPermissionGranted = false;
  // 是否需要展示设置引导弹窗
  bool _showSettingDialog = false;

  @override
  void initState() {
    super.initState();
    // 注册生命周期监听
    WidgetsBinding.instance.addObserver(this);
    _permissionCheckFuture = _checkPermission();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    // 应用从后台回到前台时重新校验权限
    if (state == AppLifecycleState.resumed && !_isPermissionGranted) {
      _checkPermission();
    }
  }

  Future<void> _checkPermission() async {
    PermissionStatus status;
    // 根据系统版本判断要校验的权限
    if (Theme.of(context).platform == TargetPlatform.android) {
      final androidInfo = await WidgetsBinding.instance.platformDispatcher.androidInfo;
      if (androidInfo.version.sdkInt >= 33) {
        status = await Permission.photos.status;
      } else {
        status = await Permission.storage.status;
      }
    } else {
      // iOS权限按需补充,这里默认过
      status = PermissionStatus.granted;
    }

    if (status.isGranted) {
      setState(() {
        _isPermissionGranted = true;
        _showSettingDialog = false;
      });
      return;
    }

    // 永久拒绝状态,直接弹设置引导
    if (status.isPermanentlyDenied) {
      setState(() {
        _showSettingDialog = true;
      });
      return;
    }

    // 其他未授权状态,申请权限
    PermissionStatus requestStatus;
    if (Theme.of(context).platform == TargetPlatform.android) {
      final androidInfo = await WidgetsBinding.instance.platformDispatcher.androidInfo;
      if (androidInfo.version.sdkInt >= 33) {
        requestStatus = await Permission.photos.request();
      } else {
        requestStatus = await Permission.storage.request();
      }
    } else {
      requestStatus = PermissionStatus.granted;
    }

    if (requestStatus.isGranted) {
      setState(() {
        _isPermissionGranted = true;
        _showSettingDialog = false;
      });
    } else if (requestStatus.isPermanentlyDenied) {
      setState(() {
        _showSettingDialog = true;
      });
    } else {
      // 普通拒绝,重新触发校验
      _checkPermission();
    }
  }

  @override
  void dispose() {
    // 移除生命周期监听
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 已授权直接返回闪屏页
    if (_isPermissionGranted) {
      return const SplashScreen();
    }

    return FutureBuilder(
      future: _permissionCheckFuture,
      builder: (ctx, snapshot) {
        // 校验过程中直接展示闪屏页UI,避免加载圈
        if (snapshot.connectionState != ConnectionState.done || !_showSettingDialog) {
          return const SplashScreen();
        }

        // 需要展示引导弹窗的情况
        return Scaffold(
          body: Stack(
            children: [
              const SplashScreen(),
              AlertDialog(
                title: const Text('权限申请'),
                content: const Text('需要存储权限才能正常使用应用功能,请前往设置开启'),
                actions: [
                  TextButton(
                    onPressed: () {
                      openAppSettings();
                      setState(() {
                        _showSettingDialog = false;
                      });
                    },
                    child: const Text('去开启'),
                  ),
                  TextButton(
                    onPressed: () {
                      setState(() {
                        _showSettingDialog = false;
                      });
                      // 拒绝的话重新校验,持续申请
                      _checkPermission();
                    },
                    child: const Text('取消'),
                  ),
                ],
              ),
            ],
          ),
        );
      },
    );
  }
}

优化点说明

  • 全程校验过程中默认展示闪屏页,不会出现加载圈或者黑屏,符合体验要求
  • 权限申请失败后如果不是永久拒绝,会自动重新申请,直到用户授权或者点不再询问
  • 监听应用生命周期,用户从设置页返回后自动校验权限,授权后直接进入后续流程
  • 兼容Android 13及以上系统的权限规则,解决Manifest权限不识别的报错

内容的提问来源于stack exchange,提问作者user12680761

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 15:45:09