Flutter开发Instagram克隆:Android14存储权限请求失效问题
问题描述
我用Flutter开发Instagram克隆应用,需要访问设备存储让用户选择图片发布,但应用设置中的存储权限呈灰色不可用状态。跟着YouTube教程写的代码,仅在Android 11模拟器上能收到权限请求,Android 14真机既不弹出权限请求,权限还置灰,已经在AndroidManifest中添加了uses-permission,代码如下:
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:photo_manager/photo_manager.dart'; class NewPostScreen extends StatefulWidget { const NewPostScreen({Key? key}) : super(key: key); @override State<NewPostScreen> createState() => _NewPostScreenState(); } class _NewPostScreenState extends State<NewPostScreen> { final List<Widget> _mediaList = []; final List<File> path = []; File? _file; int currentPage = 0; int? lastPage; _fetchNewMedia() async { lastPage = currentPage; final PermissionState ps = await PhotoManager.requestPermissionExtend(); if (ps.isAuth) { List<AssetPathEntity> album = await PhotoManager.getAssetPathList(onlyAll: true); List<AssetEntity> media = await album[0].getAssetListPaged(page: currentPage, size: 60); for (var asset in media) { if (asset.type == AssetType.image) { final file = await asset.file; if (file != null) { path.add(File(file.path)); _file = path[0]; } } } List<Widget> temp = []; for (var asset in media) { temp.add(FutureBuilder( future: asset.thumbnailDataWithSize(const ThumbnailSize( 200, 200, )), builder: (context, snapshot) { print('Thumbnail data: ${snapshot.data}'); if (snapshot.connectionState == ConnectionState.done) { return Container( child: Stack( children: [ Positioned.fill( child: Image.memory( snapshot.data!, fit: BoxFit.cover, )) ], ), ); } return Container(); })); } setState(() { _mediaList.addAll(temp); currentPage++; }); } } @override void initState() { super.initState(); _fetchNewMedia(); } int indexx = 0; Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).brightness == Brightness.light ? Colors.white : Colors.black, title: const Text( 'New Post', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, ), ), toolbarHeight: 40, centerTitle: true, automaticallyImplyLeading: false, ), body: SafeArea( child: SingleChildScrollView( child: Column( children: [ SizedBox( height: 410, child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 1, mainAxisSpacing: 1, crossAxisSpacing: 1), itemBuilder: (context, index) { return _mediaList[indexx]; }, ), ), Container( width: double.infinity, height: 40, child: Row( children: [ SizedBox( width: 10, ), Text( 'Recent', style: TextStyle(fontWeight: FontWeight.w500), ) ], ), ), GridView.builder( shrinkWrap: true, itemCount: _mediaList.length, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 1, crossAxisSpacing: 2), itemBuilder: (context, index) { return GestureDetector(onTap: () { setState(() { indexx = index; }); }, child: _mediaList[index], ); }, ) ], ), ), ), ); } }
问题原因
Android 13(API 33)开始彻底重构存储权限机制,废弃了旧的READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE,改用细分的媒体权限:READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO。Android 14进一步收紧权限策略,旧的存储权限会被系统直接忽略,导致权限选项置灰且无法发起请求。
解决步骤
更新AndroidManifest权限配置
替换旧的存储权限,添加适配Android 13+的媒体权限,同时兼容低版本:<!-- Android 13+ 读取图片权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- 兼容Android 12及以下版本 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />不需要添加
WRITE_EXTERNAL_STORAGE,现在选择图片仅需读权限;若需发布后保存到相册,使用MediaStoreAPI即可,无需申请写存储权限。升级photo_manager包版本
确保使用的photo_manager是适配Android 13/14的最新稳定版,旧版本没有处理新权限逻辑:photo_manager: ^2.7.2 # 替换为pub上的最新版本优化权限请求逻辑
在请求权限前先检查状态,确保用户明确授权,拒绝时给出提示:_fetchNewMedia() async { // 先检查当前权限状态 PermissionState ps = await PhotoManager.checkPermissionExtend(); if (!ps.isAuth) { // 发起权限请求 ps = await PhotoManager.requestPermissionExtend(); if (!ps.isAuth) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('需要开启图片访问权限才能发布内容')), ); } // 权限永久拒绝时,引导用户去设置页开启 if (ps == PermissionState.permanentlyDenied) { await PhotoManager.openSetting(); } return; } } // 后续媒体加载逻辑... }Android 14精细权限适配(可选)
Android 14新增READ_MEDIA_VISUAL_USER_SELECTED权限,允许用户仅授权特定相册访问。如需支持该特性,可在请求时指定:final PermissionState ps = await PhotoManager.requestPermissionExtend( permission: PermissionState.accessMediaLocation, // 可选,如需获取媒体位置信息 );
内容的提问来源于stack exchange,提问作者Burner Jet

