【问题标题】:Displaying an Image.file in flutter from firebase从firebase中显示一个Image.file
【发布时间】:2019-10-29 06:48:19
【问题描述】:

我在将 Image.file 从我的 firebase 调用到我的应用时遇到问题。我尝试将其转换为字符串和很多东西,但没有任何效果。

这是我从 firebase 获取数据的方式:(图像是一个文件,所以我将它包装在文件中)

Future<void> fetchAndSetCars() async {
    const url = 'https://mylink.firebaseio.com/cars.json';
    try {
      final response = await http.get(url);
      final extractedData = json.decode(response.body) as Map<String, dynamic>;
      final List<AddCar> loadedCars = [];
      extractedData.forEach((carId, carData) {
        loadedCars.add(AddCar(
          // other data
          image: File(carData['image']),
        ));
      });
      _cars = loadedCars;
      notifyListeners();
    } catch (error) {
      throw (error);
    }
  }

这是我传递给 firebase 的数据:(我将 Image.toString() 转换为将文件传递给 firebase 以调用它的错误方式吗?)

void addCar(AddCar car) {
    const url = 'https://mylink.firebaseio.com/cars.json';
    http.post(
      url,
      body: json.encode({
        // other data
        'image': car.image.toString(),
      }),
    );
    final newCar = AddCar(
      // other data
      image: car.image,
    );
    _cars.insert(0, newCar);

    notifyListeners();
  }

这是使用Image.file 显示图像的方式:

 child: Image.file(
                    image,
                    fit: BoxFit.fill,
                  ),

最后使用ImagePicker 从图库中选择file Image

String img;

  static Future<String> fileToB64(File f) async {
    List<int> imageBytes = f.readAsBytesSync();

    return base64Encode(
      imageBytes,
    );
  }

  Future<void> _takePicture() async {
    final imageFile = await ImagePicker.pickImage(
      source: ImageSource.gallery,
    );
    setState(() {
      data.image = imageFile;
    });
    fileToB64(imageFile).then((d) {
      setState(() {
        img = d; //base64Decode(d);
      });
    });
  }

// the button in my code
 child: FlatButton(
                      child: Text(AppLocalizations.of(context).createAddImages),
                      onPressed: _takePicture,
                    ),

这是堆栈跟踪:

Restarted application in 3,377ms.
E/flutter (12041): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: NoSuchMethodError: The method 'forEach' was called on null.
E/flutter (12041): Receiver: null
E/flutter (12041): Tried calling: forEach(Closure: (String, dynamic) => Null)
E/flutter (12041): #0      Mobiles.fetchAndSetMobiles 
package:flutter_app/providers/mobile_provider.dart:180
E/flutter (12041): <asynchronous suspension>
E/flutter (12041): #1      _MobilesAreaState.initState.<anonymous closure> 
package:flutter_app/home_parts/mobiles_area.dart:17
E/flutter (12041): #2      _rootRunUnary  (dart:async/zone.dart:1132:38)
E/flutter (12041): #3      _CustomZone.runUnary  (dart:async/zone.dart:1029:19)
E/flutter (12041): #4      _FutureListener.handleValue  (dart:async/future_impl.dart:137:18)
E/flutter (12041): #5      Future._propagateToListeners.handleValueCallback  (dart:async/future_impl.dart:678:45)
E/flutter (12041): #6      Future._propagateToListeners  (dart:async/future_impl.dart:707:32)
E/flutter (12041): #7      Future._complete  (dart:async/future_impl.dart:512:7)
E/flutter (12041): #8      new Future.delayed.<anonymous closure>  (dart:async/future.dart:313:16)
E/flutter (12041): #9      _rootRun  (dart:async/zone.dart:1120:38)
E/flutter (12041): #10     _CustomZone.run  (dart:async/zone.dart:1021:19)
E/flutter (12041): #11     _CustomZone.runGuarded  (dart:async/zone.dart:923:7)
E/flutter (12041): #12     _CustomZone.bindCallbackGuarded.<anonymous closure>  (dart:async/zone.dart:963:23)
E/flutter (12041): #13     _rootRun  (dart:async/zone.dart:1124:13)
E/flutter (12041): #14     _CustomZone.run  (dart:async/zone.dart:1021:19)
E/flutter (12041): #15     _CustomZone.bindCallback.<anonymous closure>  (dart:async/zone.dart:947:23)
E/flutter (12041): #16     Timer._createTimer.<anonymous closure>  (dart:async-patch/timer_patch.dart:21:15)
E/flutter (12041): #17     _Timer._runTimers  (dart:isolate-patch/timer_impl.dart:382:19)
E/flutter (12041): #18     _Timer._handleMessage  (dart:isolate-patch/timer_impl.dart:416:5)
E/flutter (12041): #19     _RawReceivePortImpl._handleMessage  (dart:isolate-patch/isolate_patch.dart:172:12)

我非常肯定我获取图像或将其传递给 firebase 的方式是问题,但我尝试了无数种方法,但似乎没有任何效果。

【问题讨论】:

  • 该异常正在 fetchandsetmobiles 中引发,但您没有显示该代码。在该方法中,查找 foreach。它在 null 上被调用。
  • 我有 2 种获取方法,1 种用于汽车,1 种用于手机,两者的工作方式相同并给出相同的错误
  • 您确定提取的数据有效吗?使用前可以打印吗?
  • 我是否通过调用 print(response.body) 来打印它?
  • 好 - 现在添加我的答案和print(loadedCars) 中的代码

标签: firebase flutter error-handling imagepicker


【解决方案1】:

确保您使用的是firebase storage,而不是用于图像的 firebase 数据库。请参阅此SO answer。一旦您获得了实例 Firebase 存储和存储引用,就应该像在该引用上调用方法一样简单。

编辑:这里有一些未经测试的代码,显示了一种方式。

import 'package:approachbuilder/models/profile.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart' as path;

final FirebaseStorage firebaseStorage = FirebaseStorage();
final StorageReference storageReference = firebaseStorage.ref();

class UserProfileImage extends StatelessWidget {
  UserProfileImage(this.userDocument);

  /// The current user's profile document in firebase
  /// gotten through Firestore.instance.collection('users).document(userId);
  final DocumentReference userDocument;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<Profile>(
      // Unnecessary step, maps the raw profile into a class intance
      // that uses a JsonSerializable and FunctionalData model
      stream: userDocument
          .snapshots()
          .map((snapshot) => Profile.fromJson(snapshot.data)),
      builder: (context, snapshot) {
        if (!snapshot.hasData) return CircularProgressIndicator();

        return GestureDetector(
          onTap: () => _getAndSetImage(snapshot.data),
          // Traditional NetworkImage doesn't handle errors that well
          child: CachedNetworkImage(
            imageUrl: snapshot.data.photoUrl,
            placeholder: (context, url) => CircularProgressIndicator(),
            errorWidget: (context, url, error) => Icon(Icons.error),
          ),
        );
      },
    );
  }

  void _getAndSetImage(Profile profile) async {
    final imageFile = await ImagePicker.pickImage(source: ImageSource.camera);

    if (imageFile == null) return;

    final baseImageFileName = path.basename(imageFile.path);
    final imageRef = storageReference.child(baseImageFileName);
    final uploadTask = imageRef.putFile(imageFile);

    await uploadTask.onComplete.then((snapshot) async {
      // File has been successfully added to firebase storage
      final downloadUrl = await snapshot.ref.getDownloadURL();

      // Remove previous photoUrl from fb storage if there is one
      await deleteFirebaseResource(profile.photoUrl);

      await userDocument.setData(
        profile.copyWith(photoUrl: downloadUrl).toJson(),
      );
    });
  }

  Future<void> deleteFirebaseResource(String url) async {
    StorageReference storageRef =
        await firebaseStorage.getReferenceFromUrl(url);

    if (storageRef == null) return;

    await storageRef.delete();
  }
}

【讨论】:

  • 这是否意味着我可以删除 'image': car.image.toString(), 传递给数据库的即时消息?这样它给出了一个文件路径,但我确定这不是传递它的最佳方式,至于当我获取该数据时,我像这样调用它image: File(carData['image']),
  • 是的,所以 'image': car.image.toString() 会在我猜的带有 base64 图像的 fb 数据库中创建一个字段。与其使用 fb 数据库来存放实际的图像数据,不如将其存放在 fb 存储中,而是将downloadUrl 写入 fb 数据库中。这将只是一个指向您的 fb 存储的普通 url。当您想显示该图像时,它将是一个普通的NetworkImage(downloadUrl)
  • 如果这不能解决问题,请告诉我,我可以添加更多示例代码
  • 抱歉回复晚了!但是是的,你能提供更多的代码吗?
  • 如果我尝试将显示更改为NetworkImage(downloadUrl),你能提供一个sn-p吗?因为我认为它会出错,因为图像设置为File
猜你喜欢
  • 1970-01-01
  • 2021-11-24
  • 1970-01-01
  • 2020-08-02
  • 1970-01-01
  • 2019-12-24
  • 2019-09-07
  • 2018-10-20
  • 1970-01-01
相关资源
最近更新 更多