TL;DR
READ_EXTERNAL_STORAGE
パーミッションや WRITE_EXTERNAL_STORAGE
パーミッションを必要としているアプリは、 AndroidManifest.xml
に android:requestLegacyExternalStorage="true"
を設定すれば OK です。
<application
android:name="io.flutter.app.FlutterApplication"
android:label="YourAppName"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
事の発端
開発中のアプリで image_picker を使っていました。
エミュレーターでは問題なく動作していたのですが、いざ実機で動作確認をしてみると……
「イメージをピックできない!?」
となったわけです。
実はエラーが発生していた
デバッグして確認してみると、
Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/Pictures/any_image.jpg: open failed: EACCES (Permission denied)
というエラーが発生していました。
パーミッション? と思い AndroidManifest.xml
に <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
を設定してみましたが結果は変わりませんでした。
原因
じつは Issues にあがっていました。
Android バージョンが原因のようです。
変更されていた外部ストレージへのアクセス方法
Android 10 ( API レベル 29 )以降から外部ストレージへのアクセス管理が変更 1 になっているようです。
ユーザーがファイルを詳細に管理して整理できるように、Android 10(API レベル 29)以降をターゲットとするアプリには、外部ストレージ デバイスに対する特別アクセス権限がデフォルトで付与されます(対象範囲別ストレージ)
とのことで、 READ_EXTERNAL_STORAGE
パーミッションや WRITE_EXTERNAL_STORAGE
パーミッションは不要になっています。
また、他のアプリが作成したファイルへのアクセスは、
- アプリに
READ_EXTERNAL_STORAGE
パーミッションが付与されていること。- 対象ファイルが、明確に定義された次のいずれかのメディア コレクション内にあること。
- 写真 - 保存場所:
MediaStore.Images
- 動画 - 保存場所:
MediaStore.Video
- オーディオ ファイル - 保存場所:
MediaStore.Audio
のようになっています。
つまり、 Flutter のパッケージがこの対応についていっていないと外部ストレージにアクセスできない状況が発生するということです。
対策
Android デベロッパー に次のような警告があがっていました。
つまり、いずれきれいにしたいけど今は微妙な状態だから気をつけてな! ってことでしょうね。
だから上で紹介した Issues もオープン状態のままなわけで……。
そんな状態でも一応は対処方法があるようなので紹介します。
対象範囲別ストレージをオプトアウトする
Android 9( API レベル 28 )以前をターゲットにする
これはそのとおりですね。
アクセス管理変更前のバージョンを相手にすればエラーは起こりません。
requestLegacyExternalStorage
の値を true
に設定する
Android 10 以降をターゲットにしている場合は、 AndroidManifest.xml
に android:requestLegacyExternalStorage="true"
を設定すれば OK です。
<application
android:name="io.flutter.app.FlutterApplication"
android:label="YourAppName"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
両方のバージョンをターゲットにしたい場合は?
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
uses-permission
に maxSdkVersion
属性が追加されたようなので、 18
を指定すれば良さそうです。
(ただ Flutter で利用している分には maxSdkVersion
属性を指定してなくても動きそうなんだよな…… )
まとめ
Flutter の開発をしていると、エラーの解消に割と時間がかかります。
プラットフォーム依存のものが多かったり、そもそも情報が少ないのも理由かもしれません。
自分が遭遇したエラーで情報の少ないものはなるべく書き溜めていこうと思いますので、同じエラーで困っている人のお役に少しでも立てれば幸いです!
-
変更内容の詳細は Android デベロッパー に詳しく記載されています ↩