简体   繁体   English

Flutter:加载资产错误,除非热重新加载(BLOC + MultiImagePicker)

[英]Flutter: Loading asset error unless hot reload (BLOC + MultiImagePicker)

I've got a strange problem where loading a file converted from MultiImagePicker only loads successfully after hot reloading the page, otherwise it returns the following error:我有一个奇怪的问题,加载从 MultiImagePicker 转换的文件只有在热重新加载页面后才能成功加载,否则返回以下错误:

Unhandled Exception: Unable to load asset: /storage/emulated/0/DCIM/Camera/IMG_20191105_104542.jpg未处理的异常:无法加载资产:/storage/emulated/0/DCIM/Camera/IMG_20191105_104542.jpg

Steps:脚步:

  1. initState loads existing image Strings from Firestore, which are cached to File via DefaultCacheManager and added to tmpGalleryImages list. initState 从 Firestore 加载现有的图像字符串,这些字符串通过 DefaultCacheManager 缓存到 File 并添加到 tmpGalleryImages 列表中。

  2. Adding images uses MultiImagePicker, which are then converted from Asset to File and added to tmpGalleryImages list.添加图像使用 MultiImagePicker,然后将其从 Asset 转换为 File 并添加到 tmpGalleryImages 列表中。

  3. I created a SetState button to test reloading the state, but still getting the above error after calling SetState - so I'm extremely confused why it only works after hot reload?我创建了一个 SetState 按钮来测试重新加载 state,但在调用 SetState 后仍然出现上述错误 - 所以我非常困惑为什么它只能在热重载后工作?

Note: Going through the hassle of converting to File allows me to combine both local images (Asset) and Firestore images (String) into one List that can be edited and re-uploaded to Firestore注意:通过转换为文件的麻烦,我可以将本地图像(资产)和 Firestore 图像(字符串)合并到一个可以编辑并重新上传到 Firestore 的列表中

initState:初始化状态:

 @override
void initState() {
super.initState();
setState(() {
  _galleryBloc.getGalleryImages(
      docRef: Firestore.instance.collection("galleries").document("gal_${widget.docId}"),
      tmpGalleryImages: widget.tmpGalleryImages,
      callback: widget.callback);
});

} }

StreamBuilder:流生成器:

@override
Widget build(BuildContext context) {
return StreamBuilder<List<File>>(
  stream: _galleryBloc.multipleImageStream,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.none) {
      print("none");
    }
    if (snapshot.connectionState == ConnectionState.waiting) {
      print("Waiting");
    }

    return Column(
      children: <Widget>[
        GridView.builder(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4),
          itemCount: snapshot.data.length < widget.imageLimit ? snapshot.data.length + 1 : snapshot.data.length,
          itemBuilder: (context, index) {
            return index < snapshot.data.length
                ? GalleryStepperThumbnail(
                    file: snapshot.data[index],
                    onTap: () {
                      _showGalleryStepperOptions(
                        context: context,
                        tmpGalleryImages: snapshot.data,
                        imageLimit: widget.imageLimit,
                        file: snapshot.data[index],
                        fileName: "img_${index + 1}",
                      );
                    })
                : snapshot.data.length < widget.imageLimit
                    ? InkWell(
                        onTap: () => _showGalleryStepperOptions(
                          context: context,
                          tmpGalleryImages: snapshot.data,
                          imageLimit: widget.imageLimit,
                          fileName: "img_${index + 1}",
                        ),
                        child: Card(
                          child: Center(
                            child: Icon(Icons.add),
                          ),
                        ),
                      )
                    : Offstage();
              },
            ),

BLOC:集团:

class GalleryBloc {
  final _multipleImageController = StreamController<List<File>>.broadcast();
  Stream<List<File>> get multipleImageStream => _multipleImageController.stream;

// -----------------------------------------------------------------------------
// Load existing gallery images
// -----------------------------------------------------------------------------
  Future<void> getGalleryImages({DocumentReference docRef, List<File> tmpGalleryImages, Function callback}) async {
    try {
      await docRef.get().then(
        (value) async {
          if (value.data != null && tmpGalleryImages.length == 0) {
            for (var img in value.data['gallery_images']) {
              File fetchedFile = await DefaultCacheManager().getSingleFile(img);
          tmpGalleryImages.add(fetchedFile);
            }
          }
        },
       );
    } catch (e) {
      print(e.toString());
    }
    callback(tmpGalleryImages);
    _multipleImageController.sink.add(tmpGalleryImages);
   }


// -----------------------------------------------------------------------------
// Convert File to Asset
// -----------------------------------------------------------------------------
  Future<File> _convertAssetToFile(String path) async {
    final byteData = await rootBundle.load(path);

    final file = File(path);
    await file.writeAsBytes(byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));
    return file;
  }

// -----------------------------------------------------------------------------
// Select Multiple Images
// -----------------------------------------------------------------------------
  Future<void> pickMultipleImages({List<File> tmpGalleryImages, int imageLimit, Function callback}) async {

    try {
      await MultiImagePicker.pickImages(
        maxImages: imageLimit - tmpGalleryImages.length,
  ).then((chosenImages) async {
        for (var path in chosenImages) {
         await _convertAssetToFile(await path.filePath).then(
        (convertedFile) {
              tmpGalleryImages.add(convertedFile);
             },
          );
        }
      });
    } on Exception catch (e) {
      print(e.toString());
    }

    _multipleImageController.sink.add(tmpGalleryImages);
    callback(tmpGalleryImages);
  }

If anyone could offer some guidance on where I'm going wrong, I would really appreciate that!如果有人可以就我哪里出错提供一些指导,我将不胜感激!

UPDATED CODE BASED ON IGOR'S ANSWER (WORKING):根据 IGOR 的回答(工作)更新代码:

 // -----------------------------------------------------------------------------
// Convert File to Asset
// -----------------------------------------------------------------------------
  Future<File> _convertAssetToFile(String path) async {

    final file = File(path);

    return file;
  }

Do not use rootBundle to open files that were not packed with the app via pubspec.yaml.不要使用rootBundle打开未通过 pubspec.yaml 与应用打包的文件。 Open them with File class.使用文件 class 打开它们。

The rootBundle contains the resources that were packaged with the application when it was built. rootBundle 包含在构建应用程序时与应用程序一起打包的资源。 To add resources to the rootBundle for your application, add them to the assets subsection of the flutter section of your application's pubspec.yaml manifest.要将资源添加到应用程序的 rootBundle,请将它们添加到应用程序 pubspec.yaml 清单的 flutter 部分的 assets 子部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM