【问题标题】:How to initialize a class with async如何用异步初始化一个类
【发布时间】:2021-07-21 14:22:44
【问题描述】:

我正在 Dart 中使用 Flutter 创建个人资料页面。用户信息存储在 Firebase Firestore 中,图像存储在 Firebase Storage 中。 Firestore 包含配置文件图像在 Firebase 存储中的路径 (profileImages/{userId}/profile.jpg)。正如您在下面看到我的代码,可下载的图片 url 是在 ProfileImage 类中获得的。

我想在初始化用户类时getDownloadUrl。不在 ProfileImage 类中。

class User {
  final profilePathInFirebaseStorage;
  User({this.profilePathInFirebaseStorage});
  factory User.fromFirestore(DocumentSnapshot snapshot) {
    return User(profilePathInFirebaseStorage: snapshot.data['imagePath']);
  }
}

class ProfileImage extends StatelessWidget {
  final String profilePathInFirebaseStorage;

  const ProfileImage(
      {Key key,
      this.profilePathInFirebaseStorage = '',)
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: firebase_storage.FirebaseStorage.instance
            .ref(profilePathInFirebaseStorage)
            .getDownloadURL(),  // Can this operation go into User class?
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          if (!snapshot.hasData) {
            return CircularProgressIndicator();
          }
          return Container(child: NetworkImage(snapshot.data));
        });
  }
}

【问题讨论】:

  • 我不确定我是否理解问题所在。你不能在你的User 类中添加一个返回getDownloadURL() 调用结果的方法吗?
  • 感谢您的评论。我不想每次显示个人资料图片时都打电话给getDownloadUrl()。它没有效果。所以我希望User 实例保留可下载的 URL。我认为应该在初始化用户实例时完成。

标签: firebase flutter dart firebase-storage


【解决方案1】:

由于您不能使用异步构造函数,因此这里有一个使用异步静态方法创建 User 类的模式。

class User {
  String userData;

  User._privateConstructor();

  static Future<User> createAsync() async {
    var user = User._privateConstructor();
    print('User.createAsync() called');
    return user._initAsync();
  }

  /// Simulates a long-loading process such as remote DB connection or device
  /// file storage access.
  Future<User> _initAsync() async {
    // ↓ do your Firebase call here ↓
    userData = await Future.delayed(Duration(seconds: 2), () => 'Some Firebase user data');
    print('User._initAsync done');
    return this;
  }
}

要实例化您的User 类,请调用公开的异步方法:

User user = await User.createAsync();

当这返回您的 user 实例时,它将拥有它需要的 Firestore 数据。

StatefulWidget 和FutureBuilder 中的示例(不使用await):

class ProfileImagePage extends StatefulWidget {
  @override
  _ProfileImagePageState createState() => _ProfileImagePageState();
}

class _ProfileImagePageState extends State<ProfileImagePage> {
  Future<User> user;

  @override
  void initState() {
    super.initState();
    user = User.createAsync();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: FutureBuilder<User>(
          future: user,
            builder: (context, snapshot) {
              if (snapshot.hasData)
                return Center(child: Text(snapshot.data.userData),);
              else
                return Center(child: Text('Loading...'));
            }),
      ),
    );
  }
}

【讨论】:

    【解决方案2】:

    听起来你想在用户类中存储下载地址,然后只初始化一次。

    这样的事情应该可以工作:

    class User {
      final profilePathInFirebaseStorage;
      Future<String> downloadURL
      User({this.profilePathInFirebaseStorage});
      factory User.fromFirestore(DocumentSnapshot snapshot) {
        return User(profilePathInFirebaseStorage: snapshot.data['imagePath']);
      }
    
      Future<String> getDownloadURL() {
        if (downloadURL == null) {
          downloadURL = firebase_storage.FirebaseStorage.instance
                .ref(profilePathInFirebaseStorage)
                .getDownloadURL()
        }
        return downloadURL;
      }
    }
    

    所以这会在第一次请求时按需初始化下载 URL。如果你愿意,你可以在构造函数中做同样的事情。但是没有办法让它不是Future,因为下载 URL 是异步确定的,你需要一个 Future 来处理有人在 URL 可用之前请求 URL 的情况。

    【讨论】:

    • 谢谢。你将如何初始化它并设置可下载的 URL?
    • 谢谢。你能完成你的代码,包括构造函数/工厂方法吗?我想确保我完全理解。
    猜你喜欢
    • 2015-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-13
    • 1970-01-01
    相关资源
    最近更新 更多