質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Q&A

解決済

2回答

4116閲覧

Flutter:端末内に出力したcsvファイルをPCで取り込む方法

nugio.kutusita

総合スコア21

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

0グッド

0クリップ

投稿2022/04/02 16:07

編集2022/04/09 16:41

プログラミング初心者(VBA,Python経験あり)、Flutter初学者です。
開発環境はWindows10, Android studioです。

①「項目名入力→値入力」のセットを任意回数行って二重リストに格納
②リストをcsv形式に書き出して端末内に保存
③PCで取り込む(以後はPCのExcelで管理・利用)

というAndroidアプリを作りたいです。
出先で入力→PCで管理という買い物メモやデータ収集ツールのようなイメージです。
ネット環境がないところで使いたいため、端末への保存が必要です。

諸々の情報収集したところ、
ファイル出力にはpath_providerプラグインを用いる方法が一般的なようで、
getApplicationDocumentsDirectory()を用いてアプリの固有フォルダに保存するという手法を試しました。

(最初はgetExternalStorageDirectory()の方法がより良いと考えたのですが、上手くいかず。
調べたところAndroidバージョン更新によるセキュリティ強化で使えなくなったという事でしょうか。)

以下コードの抜粋です。

Flutter/Dart

1 2//実行UI部分の抜粋 3ElevatedButton( 4 onPressed: () { 5 out = ''; 6 for (var item in rawMotherList) { 7 out = out + item[0]+','+item[1]+'\n'; 8 } 9 getFilePath().then((File file) { 10 file.writeAsString(out); 11 print(out); 12 } 13 ); 14 }, 15 child: 16 const Text('結果出力')// 17 ), 18 19//使用する関数 20Future<File> getFilePath() async { 21 final directory = await getApplicationDocumentsDirectory(); 22 debugPrint(directory.path); 23 return File(directory.path + '/result.csv'); 24} 25

Android studioのエミュレータ上では問題なく動作しており、
/data/user/0/com.example.test1/app_flutter の直下に出力されているようなのですが、

このファイルをPCから開くことができません。
①エミュレータ内のファイラーアプリではアプリ固有フォルダの中身にはアクセスできませんでした

②apkにビルドして実機にインストールしてファイル出力し、実機をUSB接続してPCで覗いてみたところ、
PC名\端末名\内部共有ストレージ\Android\data の中には /com.example.test1/ が見つかりません

/data/user/0/ は PC名\端末名\内部共有ストレージ\Android\data とは違う場所なのでしょうか。
PCからこの保存ファイルにアクセスする方法、
あるいは根本的な考え方の誤りをご教示いただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

色々と調べた結果、以下のプラグインを使用することで無事に目的達成できました。
https://pub.dev/packages/external_path

端末のダウンロードフォルダに保存することで、端末内・端末外からアクセスできました。
どなたかの参考になるかもと思い、コード残しておきます。
pubdevのexampleをそのまま使っただけですが。
英文の情報に当たれるようにならないと厳しいことを痛感しました。。。

Flutter/Dart

1UI部分 2ElevatedButton( 3 onPressed: () { 4 out = ''; 5 for (var item in rawMotherList) { 6 out = out + item[0]+','+item[1]+'\n'; 7 } 8 getPubDirFile().then((File file) { 9 file.writeAsString(out); 10 setState(() { 11 _status = 'ファイル出力\n'+file.path; 12 }); 13 }); 14 }, 15 child: 16 const Text('csv結果出力')// 17 ),],

Flutter/Dart

1関数 2Future<String> getPublicDirectoryPath() async { 3 String path; 4 path = await ExternalPath.getExternalStoragePublicDirectory( 5 ExternalPath.DIRECTORY_DOWNLOADS); 6 print(path); // /storage/emulated/0/Download 7 return path; 8} 9Future<File> getPubDirFile() async { 10 final directory = await getPublicDirectoryPath(); 11 return File(directory + '/result.csv');} 12 13

投稿2022/04/09 07:12

nugio.kutusita

総合スコア21

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

Flutter は全く分からないのですが、 Android で「アプリの固有フォルダ」と言えば、そのアプリ以外からはアクセス出来ないと思います。

仰られるように、 Android はバージョン毎に「公開したいファイルをどこに作ればいいのか」がコロコロ変わっている印象ですので、お使いの Android のバージョンと Flutter でのフォルダ/ディレクトリの得方をしっかりマッチングさせて調べる必要があるように思います。

(Android10 以前では ) メディアファイル以外のファイルは ストレージ アクセス フレームワーク を用いるのが良いかもしれません。
Flutter プラグインを探しましたら以下がありました。※ホンモノかどうか私には分かりませんので、一応ご注意ください。
saf: ^1.0.3+3


以下の環境を構築してプラグインを作って見ました。
main.dart を実行すると(?) StrageAccessFramework のアクティビティで Downloads フォルダが表示されます。フォルダやファイル名を変更しSAVE ボタンを押すとファイルが作られ、 Flutter の画面が出て(戻って) 該当ファイルのコンテンツプロバイダでの URI が表示されます。
…で、これはどうすれば(pub なんとかに公開する以外で)プラグインとして GitHub とかで公開できるんでしょうか(X_X;;;

Flutter

plain

1>flutter --version 2Flutter 2.10.4 • channel stable • https://github.com/flutter/flutter.git 3Framework • revision c860cba910 (2 weeks ago) • 2022-03-25 00:23:12 -0500 4Engine • revision 57d3bac3dd 5Tools • Dart 2.16.2 • DevTools 2.9.2

AndroidStudio

plain

1Android Studio Bumblebee | 2021.1.1 Patch 2 2Build #AI-211.7628.21.2111.8193401, built on February 17, 2022 3Runtime version: 11.0.11+9-b60-7590822 amd64 4VM: OpenJDK 64-Bit Server VM by Oracle Corporation 5Windows 10 10.0 6GC: G1 Young Generation, G1 Old Generation 7Memory: 1280M 8Cores: 6 9Registry: external.system.auto.import.disabled=true 10Non-Bundled Plugins: Dart (211.7811), org.jetbrains.kotlin (211-1.6.20-release-275-AS7442.40), io.flutter (66.0.1)

AndroidStudio Plugin

  • Flutter flutter.dev 66.0.1
  • Dart JetBrains 211.7811

plugin サンプル生成コマンド(うろ覚え...)

plain

1> flutter create --org com.teratail.q_jmx3uadrrp6bi3 -t plugin --platforms android -a java saf_plugin_sample

サンプルから修正したコード

saf_plugin_sample/example/lib/main.dart

dart

1import 'package:flutter/material.dart'; 2import 'dart:async'; 3 4import 'package:flutter/services.dart'; 5import 'package:saf_plugin_sample/saf_plugin_sample.dart'; 6 7void main() { 8 runApp(const MyApp()); 9} 10 11class MyApp extends StatefulWidget { 12 const MyApp({Key? key}) : super(key: key); 13 14 15 State<MyApp> createState() => _MyAppState(); 16} 17 18class _MyAppState extends State<MyApp> { 19 String _uri = 'Unknown'; 20 21 22 void initState() { 23 super.initState(); 24 initPlatformState(); 25 } 26 27 // Platform messages are asynchronous, so we initialize in an async method. 28 Future<void> initPlatformState() async { 29 String uri; 30 try { 31 uri = await SafPluginSample.createDocument("text/csv", "test.csv", "AAA,BBB,CCC\n111,222,333") ?? 'Canceled'; 32 } on PlatformException { 33 uri = 'Exception'; 34 } 35 36 if (!mounted) return; 37 38 setState(() { 39 _uri = uri; 40 }); 41 } 42 43 44 Widget build(BuildContext context) { 45 return MaterialApp( 46 home: Scaffold( 47 appBar: AppBar( 48 title: const Text('Plugin example app'), 49 ), 50 body: Center( 51 child: Text('Running on: $_uri\n'), 52 ), 53 ), 54 ); 55 } 56}

saf_plugin_sample/lib/saf_plugin_sample.dart

dart

1import 'dart:async'; 2 3import 'package:flutter/services.dart'; 4 5class SafPluginSample { 6 static const MethodChannel _channel = MethodChannel('com.teratail.q_jmx3uadrrp6bi3/saf_plugin_sample'); 7 8 static Future<String?> createDocument(String type, String title, String contents) async { 9 final String? uri = await _channel.invokeMethod( 10 'createDocument', 11 <String, dynamic>{ 12 'type': type, 13 'title': title, 14 'contents': contents 15 }); 16 return uri; 17 } 18}

saf_plugin_sample/android/src/main/java/com/teratail/q_jmx3uadrrp6bi3/saf_plugin_sample/SafPluginSamplePlugin.java

java

1package com.teratail.q_jmx3uadrrp6bi3.saf_plugin_sample; 2 3import android.app.Activity; 4import android.content.Intent; 5import android.net.Uri; 6 7import androidx.annotation.NonNull; 8 9import java.io.*; 10 11import io.flutter.embedding.engine.plugins.FlutterPlugin; 12import io.flutter.embedding.engine.plugins.activity.*; 13import io.flutter.plugin.common.*; 14import io.flutter.plugin.common.MethodChannel.MethodCallHandler; 15import io.flutter.plugin.common.MethodChannel.Result; 16 17/** SafPluginSamplePlugin */ 18public class SafPluginSamplePlugin implements FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener { 19 private static final int REQUEST_CODE = 1; 20 21 private MethodChannel channel; 22 private ActivityPluginBinding binding; 23 private Result result; 24 private String contents; 25 26 @Override 27 public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { 28 channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "com.teratail.q_jmx3uadrrp6bi3/saf_plugin_sample"); 29 channel.setMethodCallHandler(this); 30 } 31 32 @Override 33 public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { 34 channel.setMethodCallHandler(null); 35 } 36 37 @Override 38 public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { 39 if(call.method.equals("createDocument")) { 40 if(binding != null) { 41 this.result = result; 42 43 String type = call.argument("type"); 44 String title = call.argument("title"); 45 contents = call.argument("contents"); 46 47 Intent intent = new Intent(); 48 intent.setAction(Intent.ACTION_CREATE_DOCUMENT); 49 intent.setType(type); 50 intent.putExtra(Intent.EXTRA_TITLE, title); 51 //intent.putExtra(EXTRA_CONTENTS, contents); 52 binding.getActivity().startActivityForResult(intent, REQUEST_CODE); 53 } 54 } else { 55 result.notImplemented(); 56 } 57 } 58 59 @Override 60 public boolean onActivityResult(int requestCode, int resultCode, Intent data) { 61 switch(requestCode) { 62 case REQUEST_CODE: 63 if(resultCode != Activity.RESULT_OK) { 64 result.success(null); //CANCEL? 65 return true; 66 } 67 Uri uri = data.getData(); 68 try(OutputStream os = binding.getActivity().getContentResolver().openOutputStream(uri); 69 PrintStream ps = new PrintStream(os);) { 70 ps.print(contents); 71 result.success(uri.toString()); 72 } catch(IOException e) { 73 result.error("IOException", e.getLocalizedMessage(), e); 74 } 75 return true; 76 } 77 return false; 78 } 79 80 @Override 81 public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { 82 this.binding = binding; 83 binding.addActivityResultListener(this); 84 } 85 86 @Override 87 public void onDetachedFromActivity() { 88 binding.removeActivityResultListener(this); 89 binding = null; 90 } 91 92 @Override 93 public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { 94 onAttachedToActivity(binding); 95 } 96 97 @Override 98 public void onDetachedFromActivityForConfigChanges() { 99 onDetachedFromActivity(); 100 } 101}

投稿2022/04/03 18:03

編集2022/04/09 07:40
jimbe

総合スコア12543

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nugio.kutusita

2022/04/04 12:47

ありがとうございます。 アプリ専用フォルダにはPCからもアクセスできないのですね… android11の環境でテストしていたのですが、ストレージアクセスフレームワークを使えるのもandroid10までということでしょうか。 そうであれば、android10のシミュレータでsafプラグインを試してみようと思います。 今後、11以降の環境でローカルストレージにファイルを持つことは諦めた方がよいのでしょうか。
jimbe

2022/04/04 13:40

もしアプリのデータを外部に漏らしたら最悪個人データの流出ということになりかねません。だからこそ、アプリの開発のし易さを多少犠牲にしてでも万一の事態にならないようにアレやコレやとデータアクセス方法を模索している段階では無いかと考えています。 ストレージフレームワーク以外では「対象範囲別ストレージ」というのが公式ドキュメントに出ているのですが、手元の実機が Android10 ということもあって使ったことがありませんで、どうなっているのか分かりません。
nugio.kutusita

2022/04/07 14:10

ご紹介いただいたsafプラグインを調べて試してみましたが、正直に言ってよく理解できませんでした。 exampleのコードは動いたのですが、permissionしたフォルダの中身を見る(開く)内容のみであり、 フォルダ内にファイルを作成する方法はわかりませんでした(できない?)。 どなたか、ほかの方法などわかりましたら大変ありがたいです。
jimbe

2022/04/07 14:18

そうでしたか、申し訳ありません。 引き続き情報を求められるのでしたら、回答のベストアンサーを外して頂ければ、また「受付中」になると思います。
nugio.kutusita

2022/04/07 15:32

いえ、こちらこそ理解しきれず申し訳ありません。 ありがとうございました。
jimbe

2022/04/09 07:41 編集

試しに Flutter のプラグインの開発環境を作って StrageAccessFramework を使った createDocument を作って見たのですが、エミュレータ(Android11) で動作しました。 が、これを( pub 何とかに登録せずに ) GitHub 等に何を公開すれば良いのか分かりません(X_X; flutter コマンドから作ったプロジェクト内で修正した java や dart ファイルをこちらに出せば組み込めるものなのでしょうか...?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問