【问题标题】:Reading and displaying userData from Firestore - Flutter从 Firestore 读取和显示 userData - Flutter
【发布时间】:2021-01-21 21:18:53
【问题描述】:

我的 userData 模型如下:

class UserData {
  String id;
  String firstName;
  String lastName;
  String phoneNumber;
  String streetAddress;
  String city;
  String state;
  String postcode;
  String country;
  Timestamp createdAt;
  Timestamp updatedAt;

  UserData();

  UserData.fromMap(Map<String, dynamic> data) {
    id = data['id'];
    firstName = data['first_name'];
    lastName = data['last_name'];
    phoneNumber = data['phone_number'];
    streetAddress = data['street_address'];
    city = data['city'];
    postcode = data['postcode'];
    state = data['state'];
    country = data['country'];
    createdAt = data['created_at'];
    updatedAt = data['updated_at'];
  }

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'first_name': firstName,
      'last_name': lastName,
      'phone_number': phoneNumber,
      'street_address': streetAddress,
      'city': city,
      'postcode': postcode,
      'state': state,
      'country': country,
      'created_at': createdAt,
      'updated_at': updatedAt,
    };
  }
}

我有我的 Notifier 类,我用它来读取和写入 userData 到 Firestore。由于这是 userData,每个登录用户只有一个实例,因为他们只能有一个文档保存他们的个人资料信息。

class UserDataNotifier with ChangeNotifier {
  UserData _currentLoggedInUserData;

  Query userDataCollection = Firestore.instance.collection('userData');

  UserData get currentLoggedInUserData => _currentLoggedInUserData;

  set currentLoggedInUserData(UserData userData) {
    _currentLoggedInUserData = userData;
    notifyListeners();
  }

  getUserData(UserDataNotifier userDataNotifier) async {
    String userId = (await FirebaseAuth.instance.currentUser()).uid;

    await Firestore.instance.collection('userData').document(userId).get().then(
            (value) => _currentLoggedInUserData = UserData.fromMap(value.data));

    notifyListeners();
  }

  Future createOrUpdateUserData(UserData userData, bool isUpdating) async {
    String userId = (await FirebaseAuth.instance.currentUser()).uid;

    CollectionReference userDataRef = Firestore.instance.collection('userData');

    if (isUpdating) {
      userData.updatedAt = Timestamp.now();

      await userDataRef.document(userId).updateData(userData.toMap());
      print('updated userdata with id: ${userData.id}');
    } else {
      userData.createdAt = Timestamp.now();

      DocumentReference documentReference = userDataRef.document(userId);

      userData.id = documentReference.documentID;

      await documentReference.setData(userData.toMap(), merge: true);
      print('created userdata successfully with id: ${userData.id}');
    }
    notifyListeners();
  }
}

如何在我的个人资料屏幕中读取和显示我的 userData?

这是我的个人资料表单屏幕:

class ProfileFormScreen extends StatefulWidget {
  static const String id = 'profile_form';

  @override
  _ProfileFormScreenState createState() => _ProfileFormScreenState();
}

class _ProfileFormScreenState extends State<ProfileFormScreen> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  UserData _currentLoggedInUserData;

  //global declarations
  String selectedBusinessTypeDropDownValue = 'Fashion';
  String selectedCountryDropDownValue = 'India';
  String email;

  bool showSpinner = false;

  _saveUserData(BuildContext context) {
    UserDataNotifier userNotifier =
        Provider.of<UserDataNotifier>(context, listen: false);

    if (!_formKey.currentState.validate()) {
      return;
    }
    _formKey.currentState.save();
    userNotifier.createOrUpdateUserData(_currentLoggedInUserData, false);
    Navigator.pop(context);
  }

  @override
  void initState() {
    super.initState();

    UserDataNotifier userDataNotifier =
        Provider.of<UserDataNotifier>(context, listen: false);

    if (userDataNotifier.currentLoggedInUserData != null) {
      _currentLoggedInUserData = userDataNotifier.currentLoggedInUserData;
      print(_currentLoggedInUserData.id);
    } else {
      _currentLoggedInUserData = UserData();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Edit Profile'),
      ),
      body: ModalProgressHUD(
        inAsyncCall: showSpinner,
        child: SingleChildScrollView(
          padding: EdgeInsets.only(top: 20.0),
          child: Form(
            autovalidate: true,
            key: _formKey,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                LabelTextPadding(text: 'Business Information'),

                //location
                RegularTextPadding(regText: 'Location'),
                //address 1
                _buildAddress(),
                //city
                _buildCityField(),
                //postcode
                _buildPostcode(),
                //state
                _buildStateField(),
                //country
                _buildCountry(),

                SizedBox(
                  height: 20.0,
                ),
                DividerClass(),
                SizedBox(
                  height: 20.0,
                ),

                //Personal information
                LabelTextPadding(
                  text: 'Personal Information',
                ),
                _buildFirstNameField(),
                _buildLastNameField(),
                _buildPhoneNumberField(),
                _buildButtons(),
              ],
            ),
          ),
        ),
      ),
    );
  }

  //business information
  _buildAddress() {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: TextFormField(
        initialValue: _currentLoggedInUserData.streetAddress,
        textAlign: TextAlign.left,
        onSaved: (value) {
          _currentLoggedInUserData.streetAddress = value;
        },
        validator: TextInputFieldValidator.validate,
        decoration:
            kTextFieldDecoration.copyWith(hintText: 'house and street address'),
      ),
    );
  }

  _buildCityField() {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: TextFormField(
        initialValue: _currentLoggedInUserData.city,
        textAlign: TextAlign.left,
        onSaved: (value) {
          _currentLoggedInUserData.city = value;
        },
        validator: TextInputFieldValidator.validate,
        decoration: kTextFieldDecoration.copyWith(hintText: 'enter city'),
      ),
    );
  }

  _buildPostcode() {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: TextFormField(
        initialValue: _currentLoggedInUserData.postcode,
        textAlign: TextAlign.left,
        onSaved: (value) {
          _currentLoggedInUserData.postcode = value;
        },
        validator: TextInputFieldValidator.validate,
        decoration: kTextFieldDecoration.copyWith(hintText: 'enter postcode'),
      ),
    );
  }

  _buildStateField() {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: TextFormField(
        initialValue: _currentLoggedInUserData.state,
        textAlign: TextAlign.left,
        onSaved: (value) {
          _currentLoggedInUserData.state = value;
        },
        validator: TextInputFieldValidator.validate,
        decoration: kTextFieldDecoration.copyWith(hintText: 'enter state'),
      ),
    );
  }

  _buildCountry() {
    return Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(0.5)),
        border: Border.all(
          color: kThemeStyleButtonFillColour,
          width: 1,
        ),
      ),
      padding: const EdgeInsets.fromLTRB(10.0, 10.0, 20.0, 0.0),
      margin: EdgeInsets.all(20.0),
      child: Center(
        child: DropdownButton(
          value: _currentLoggedInUserData.country == null
              ? selectedCountryDropDownValue
              : _currentLoggedInUserData.country,
          icon: Icon(
            FontAwesomeIcons.caretDown,
            color: kThemeStyleButtonFillColour,
          ),
          elevation: 15,
          underline: Container(
            height: 0,
            color: kThemeStyleButtonFillColour,
          ),
          items: country
              .map(
                (country) =>
                    DropdownMenuItem(value: country, child: Text(country)),
              )
              .toList(),
          onChanged: (newValue) {
            setState(() {
              selectedCountryDropDownValue = newValue;
              _currentLoggedInUserData.country = newValue;
            });
          },
        ),
      ),
    );
  }

  //user personal info build
  _buildFirstNameField() {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: TextFormField(
        initialValue: _currentLoggedInUserData.firstName,
        textAlign: TextAlign.left,
        onSaved: (value) {
          _currentLoggedInUserData.firstName = value;
        },
        validator: TextInputFieldValidator.validate,
        decoration: kTextFieldDecoration.copyWith(hintText: 'your first Name'),
      ),
    );
  }

  _buildLastNameField() {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: TextFormField(
        initialValue: _currentLoggedInUserData.lastName,
        textAlign: TextAlign.left,
        onSaved: (value) {
          _currentLoggedInUserData.lastName = value;
        },
        validator: TextInputFieldValidator.validate,
        decoration: kTextFieldDecoration.copyWith(hintText: 'your last name'),
      ),
    );
  }

  _buildPhoneNumberField() {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: TextFormField(
        initialValue: _currentLoggedInUserData.phoneNumber,
        textAlign: TextAlign.left,
        onSaved: (value) {
          _currentLoggedInUserData.phoneNumber = value;
        },
        validator: TextInputFieldValidator.validate,
        decoration:
            kTextFieldDecoration.copyWith(hintText: 'your phone number'),
      ),
    );
  }

  _buildButtons() {
    return Padding(
      padding: const EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Buttons(
              onPressedButton: () {
                Navigator.pop(context);
              },
              buttonLabel: 'Cancel',
              buttonColour: kThemeStyleButtonFillColour,
              buttonTextStyle: kThemeStyleButton),
          SizedBox(
            width: 15.0,
          ),
          Buttons(
              onPressedButton: () => _saveUserData(context),
              buttonLabel: 'Save',
              buttonColour: kThemeStyleButtonFillColour,
              buttonTextStyle: kThemeStyleButton),
        ],
      ),
    );
  }
}

class TextInputFieldValidator {
  static String validate(String value) {
    if (value.isEmpty) {
      return 'This field can\'t be empty, you must enter a text';
    }
    if (value.length < 3) {
      return 'You must enter at least a word';
    }
  }
}

这是我尝试过的:

  getUserData(UserDataNotifier userDataNotifier) async {
    String userId = (await FirebaseAuth.instance.currentUser()).uid;

    await Firestore.instance.collection('userData').document(userId).get();
    notifyListeners();
  }

【问题讨论】:

  • 当你尝试这样做时会发生什么?你到底想做什么,你现在不能?到目前为止,您探索了哪些链接?
  • 什么也没发生。我希望能够以表单格式显示保存到 Firestore 的 userData。因此,用户可以在最初不提供所有数据的情况下进行注册。但注册后,他们可以转到设置并更新/添加这些额外信息。我正在使用一个表单,以便它显示存在的数据,并且如果用户想要太快地编辑自己的信息,它是可编辑的格式。

标签: firebase flutter google-cloud-firestore firebase-authentication flutter-dependencies


【解决方案1】:

我认为您应该从分配一个变量开始,如下所示:

var myVar =  await Firestore.instance.collection('userData').document(userId).get();
var yourVariable = myVar.data()["YourFieldNameInFireStore"];

您可能想查看 Provider 或以其他方式使用 ChangeNotifier。

来自代码片段的示例,未针对您的问题量身定制并在此处输入,因此可能包含拼写错误/缺失信息:

class Muser extends ChangeNotifier {
  FirebaseUser firebaseUser;
  // this example uses FirebaseAuth, not anything from FireStore,
  // but you could easily add variables to your User class here such as
  // userAge for example, which you could update in your getUserData method or something
  fauth.User fAuthUser = fauth.FirebaseAuth.instance.currentUser;

  Muser({
    this.firebaseUser, this.userAge,
  });

  getUserData() {
  }
  int userAge;
  Map<dynamic, int> _specialCalculatedUserInfo;

  Map<dynamic, int> get specialCalculatedUserInfo{
    if (_specialCalculatedUserInfo== null) {
      _specialCalculatedUserInfo= _calculateUserInfo();
    }
    notifyListeners();
    return _specialCalculatedUserInfo;
  }

}


class ProfileViewState extends State<ProfileView> {

  @override
  void dispose() {
    // TODO: implement dispose for controllers
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: <Widget>[
          Column(
            children: <Widget>[
              Text("Your Profile"),
              Consumer<Muser>(
                builder: (context, user, child) {
                  return
                      Column(
                    children: [
                      Text("Age: ${user.userAge}"),
                      CircleAvatar(
                        radius: 50,
                        backgroundColor: Colors.brown.shade600,
                        backgroundImage: user.firebaseUser.photoUri.isEmpty
                            ? AssetImage("assets/images/icon.png")
                            : NetworkImage(user.getPhotoURL()),
                       ),
                     ],
                  );
                }
             )
           ]
         )
      );
    }
}

【讨论】:

  • 嗨斯科特!感谢您的回复。你的例子让我有点失望。在 specialCalculatedUserInfo 的 getter 函数中。我在某种程度上很熟悉,更喜欢使用 ChangeNotifier 类。我已经编辑了我的问题,以包括我的 userData 的完整 Notifier 类以及我尝试显示/更新/创建 userData 的个人资料表单屏幕,具体取决于它是否存在。
  • 第二行:var yourVariable = myVar.data()["YourFieldNameInFireStore"];你能更好地解释一下吗?谢谢。
  • 它可能是旧版本的 firestore 中的 .data["fieldname] ,所以您所做的可能是正确的。请参见此处:firebase.google.com/docs/firestore/query-data/get-data#go_6 特别是“获取文档”部分。如果更容易,在调试时设置一个 var = myVar.data() ,然后将鼠标悬停在它上面以查看为您存在的内容。它应该是文档中的所有字段。这可能是问题的根源,也可能不是. 我强烈建议下一步使用调试器单步执行代码。我不会过度考虑自定义 getter,它就在我的代码中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-03
  • 2021-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
相关资源
最近更新 更多