如何使用异步初始化 class

How to initialize a class with async

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

中获取

我想在初始化用户 class 时 getDownloadUrl。不在 ProfileImage class.

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));
        });
  }
}

听起来你想把下载URL存储在用户class中,然后只初始化一次。

像这样的东西应该可以工作:

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 的情况可用。

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

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 class,请调用公开的异步方法:

User user = await User.createAsync();

当这个 returns 您的 user 实例将拥有它需要的 Firestore 数据时。

StatefulWidgetFutureBuilder 的示例(不使用 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...'));
            }),
      ),
    );
  }
}