29

Here is my pubspec.yaml:

  assets:
    - assets/mySecertImage.png

Here is how I read back the assets:

  var data = await PlatformAssetBundle().load('assets/mySecertImage.png');

Instead of reading it directly, can I get the file path instead? If it is not possible to do so, it is possible to change the data to become a File object? Thanks.

DNB5brims
  • 29,344
  • 50
  • 131
  • 195
  • Would it solve your problem reading from the bundle and saving it to a real file when starting the app (in the main method)? If so let me know and I'll show you the code to do just that. – Marcelo Glasberg Sep 16 '18 at 20:51
  • This code will work perfectly Future getImageFileFromAssets(String path) async { final byteData = await rootBundle.load('assets/$path'); final buffer = byteData.buffer; Directory tempDir = await getTemporaryDirectory(); String tempPath = tempDir.path; var filePath = tempPath + '/file_01.tmp'; // file_01.tmp is dump file, can be anything return File(filePath) .writeAsBytes(buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes)); } – Harjinder Bains Feb 19 '22 at 16:06

4 Answers4

27

Have you found a solution ? (I am looking for the same as well).

Here is a workaround that I pursue (out of lack of a better idea)... :

I do:

  • create a new File-path to your Documents-directory (named app.txt in the below code-example)
  • copy the File sitting in your assets folder to this Documents-directory location
  • work with the copied file from now on (where you now have File-path and even Byte-content if needed)

Here is the Dart-code:

import 'package:path/path.dart';

Directory directory = await getApplicationDocumentsDirectory();
var dbPath = join(directory.path, "app.txt");
ByteData data = await rootBundle.load("assets/demo.txt");
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await File(dbPath).writeAsBytes(bytes);

Some people also work with the getDatabasesPath() instead of getApplicationDocumentsDirectory(). But I figured that on an iOS-Simulator this ends up being the exact same location. (not verified on Android)... So, it would say:

var dbDir = await getDatabasesPath();
var dbPath = join(dbDir, "app.txt");
ByteData data = await rootBundle.load("assets/demo.txt");
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await File(dbPath).writeAsBytes(bytes);

For an iOS-Simulator: In both above examples, you can find your copied file under the folder:

/Users/username/Library/Developer/CoreSimulator/Devices/AE5C3275-CD65-3537-9B32-53533B97477C/data/Containers/Data/Application/7BE2B2EE-4E45-3783-B4BD-341DA83C43BD/Documents/app.txt

(of course, the UUID's being different in your case...)

And if you want to copy the file only if it does not exist already, you could write:

Directory directory = await getApplicationDocumentsDirectory();
var dbPath = join(directory.path, "app.txt");
if (FileSystemEntity.typeSync(dbPath) == FileSystemEntityType.notFound) {
  ByteData data = await rootBundle.load("assets/demo.txt");
  List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
  await File(dbPath).writeAsBytes(bytes);
}
iKK
  • 6,394
  • 10
  • 58
  • 131
  • What is 'rootBundle' then? – Oliver Dixon Apr 15 '19 at 09:54
  • @OliverDixon rootBundle is a Flutter library. import 'package:flutter/services.dart' show rootBundle; See here for more info : https://flutter.dev/docs/development/ui/assets-and-images – mikechambers May 05 '19 at 16:23
  • 4
    please don't forget to add first the dependency `path_provider: ^1.3.0` to `pubspec.yaml` other the `getApplicationDocumentsDirectory()` will give you error. – ArifMustafa Sep 18 '19 at 04:22
10

You can not get the file path of an asset file, because assets, that are bundled with your app, are stored inside an archive.

See the "Asset building" section here:

https://flutter.dev/docs/development/ui/assets-and-images#asset-bundling

During a build, Flutter places assets into a special archive called the asset bundle that apps read from at runtime.

Cristi
  • 1,488
  • 1
  • 14
  • 14
4

I had the same issue but fixed by calling this.

Future<File> getImageFileFromAssets(String path) async {
 final byteData = await rootBundle.load('assets/$path');
 final buffer = byteData.buffer;
 Directory tempDir = await getTemporaryDirectory();
 String tempPath = tempDir.path;
 var filePath =
  tempPath + '/file_01.tmp'; // file_01.tmp is dump file, can be anything
 return File(filePath)
  .writeAsBytes(buffer.asUint8List(byteData.offsetInBytes, 
 byteData.lengthInBytes));
}
Harjinder Bains
  • 356
  • 3
  • 7
3

An alternative to Harjinder's implementation: It handles the case that the path includes directories and it avoids rewriting the file

  Future<File> _getImageFileFromAssets(String path) async {
    Directory tempDir = await getTemporaryDirectory();
    String tempPath = tempDir.path;
    var filePath = "$tempPath/$path";
    var file = File(filePath);
    if (file.existsSync()) {
      return file;
    } else {
      final byteData = await rootBundle.load('assets/$path');
      final buffer = byteData.buffer;
      await file.create(recursive: true);
      return file
          .writeAsBytes(buffer.asUint8List(byteData.offsetInBytes,
          byteData.lengthInBytes));
    }
  }
Navnav
  • 982
  • 3
  • 11
  • 25