当用户试图编辑他们的信息时,我试图将我的 UserData 读入表单字段。我可以写入 Firebase,但我无法读取用户数据并将其显示到内置字段中。每个 userData 都以登录用户的 mid 作为 userData 的 id 来存储,以便区分。
我错过了什么?请参阅下文了解我的尝试。
这是我的 UserData 模型:
class UserData {
String id;
String firstName;
String lastName;
String phoneNumber;
String role;
String businessName;
String businessType;
String streetAddress;
String city;
String state;
String postcode;
String country;
String businessTradingCurrency;
Timestamp createdAt;
Timestamp updatedAt;
UserData(
this.id,
this.firstName,
this.businessTradingCurrency,
this.businessType,
this.businessName,
this.city,
this.country,
this.createdAt,
this.lastName,
this.phoneNumber,
this.postcode,
this.role,
this.state,
this.streetAddress,
this.updatedAt,
);
UserData.fromMap(Map<String, dynamic> data) {
id = data['id'];
firstName = data['first_name'];
lastName = data['last_name'];
phoneNumber = data['phone_number'];
businessTradingCurrency = data['trading_currency'];
role = data['role'];
businessName = data['business_name'];
businessType = data['business_type'];
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,
'role': role,
'trading_currency': businessTradingCurrency,
'business_name': businessName,
'business_type': businessType,
'street_address': streetAddress,
'city': city,
'postcode': postcode,
'state': state,
'country': country,
'created_at': createdAt,
'updated_at': updatedAt,
};
}
}
这是我的 UserData 通知程序类:
class UserDataNotifier with ChangeNotifier {
UserData _currentLoggedInUserData;
CollectionReference userDataRef = Firestore.instance.collection('userData');
UserData get currentLoggedInUserData => _currentLoggedInUserData;
set currentLoggedInUserData(UserData userData) {
_currentLoggedInUserData = userData;
notifyListeners();
}
Future<void> getUserData() async {
String userId = (await FirebaseAuth.instance.currentUser()).uid.toString();
DocumentSnapshot result =
await Firestore.instance.collection('userData').document(userId).get();
_currentLoggedInUserData = result.data as UserData;
print('Phone Number: ${_currentLoggedInUserData.phoneNumber}');
notifyListeners();
}
Future createOrUpdateUserData(UserData userData, bool isUpdating) async {
String userId = (await FirebaseAuth.instance.currentUser()).uid;
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();
}
}
这是我的编辑用户配置文件表单:
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;
bool showSpinner = false;
//global declarations
String selectedBusinessTypeDropDownValue = 'Fashion';
String selectedCurrencyDropDownValue = 'AUD: Australian Dollar';
String selectedCountryDropDownValue = 'Australia';
String email;
@override
void initState() {
super.initState();
UserDataNotifier userDataNotifier =
Provider.of<UserDataNotifier>(context, listen: false);
if (userDataNotifier.currentLoggedInUserData != null) {
_currentLoggedInUserData = userDataNotifier.currentLoggedInUserData;
} else {
_currentLoggedInUserData = UserData(
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
);
}
}
@override
Widget build(BuildContext context) {
UserDataNotifier userDataNotifier =
Provider.of<UserDataNotifier>(context, listen: false);
print('Logged in user id: ${_currentLoggedInUserData.id}');
_saveUserData() async {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
userDataNotifier.createOrUpdateUserData(_currentLoggedInUserData, true);
Navigator.pop(context);
}
return Scaffold(
appBar: AppBar(
title: Text('Edit Profile'),
actions: <Widget>[
FlatButton(
onPressed: () => _saveUserData(),
child: Icon(
FontAwesomeIcons.save,
color: kThemeStyleButtonFillColour,
)),
],
),
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: SingleChildScrollView(
// padding: EdgeInsets.only(top: 20.0),
child: Form(
autovalidateMode: AutovalidateMode.always,
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
LabelTextPadding(text: 'Business Information'),
//business name
RegularTextPadding(regText: 'Business Name'),
_buildBusinessName(),
SizedBox(height: 20.0),
//business type
RegularTextPadding(regText: 'Business Type'),
Platform.isIOS
? _buildCupertinoStyleBusinessType(context)
: _buildMaterialStyleBusinessType(context),
//Trading currency
RegularTextPadding(regText: 'Trading Currency'),
Platform.isIOS
? _buildCupertinoStyleTradingCurrency(context)
: _buildMaterialStyleTradingCurrency(context),
//business location
RegularTextPadding(regText: 'Location'),
//address 1
_buildAddress(),
//city
_buildCityField(),
//postcode
_buildPostcode(),
//state
_buildStateField(),
//country
Platform.isIOS
? _buildCupertinoStyleCountry(context)
: _buildMaterialStyleCountry(context),
SizedBox(
height: 20.0,
),
DividerClass(),
SizedBox(
height: 20.0,
),
//Personal information
LabelTextPadding(
text: 'Personal Information',
),
_buildFirstNameField(),
_buildLastNameField(),
_buildPhoneNumberField(),
// _buildEmailField(),
//cancel and save buttons
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(),
buttonLabel: 'Save',
buttonColour: kThemeStyleButtonFillColour,
buttonTextStyle: kThemeStyleButton),
],
),
),
],
),
),
),
),
);
}
//business information
Widget _buildBusinessName() {
return Container(
padding: EdgeInsets.all(20.0),
child: TextFormField(
initialValue: _currentLoggedInUserData.businessName,
textAlign: TextAlign.left,
onSaved: (value) {
_currentLoggedInUserData.businessName = value;
},
validator: TextInputFieldValidator.validate,
decoration:
kTextFieldDecoration.copyWith(hintText: 'update business Name'),
),
);
}
//business type - cupertino and material styles
_buildCupertinoStyleBusinessType(BuildContext context) {
return GestureDetector(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
border: Border.all(
color: kThemeStyleButtonFillColour,
width: 1,
)),
padding: const EdgeInsets.fromLTRB(10.0, 12.0, 10.0, 12.0),
margin: EdgeInsets.all(20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(selectedBusinessTypeDropDownValue),
Icon(
FontAwesomeIcons.caretDown,
color: kThemeStyleButtonFillColour,
),
],
),
),
onTap: () => showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return Container(
color: Colors.white,
height: MediaQuery.of(context).copyWith().size.height / 4,
child: CupertinoPicker(
magnification: 1.5,
children: List<Widget>.generate(businessType.length, (int index) {
return Center(
child: Text(
businessType[index].toString(),
softWrap: true,
style: TextStyle(fontSize: 15.0),
),
);
}),
itemExtent: 25,
onSelectedItemChanged: (index) {
setState(() {
selectedBusinessTypeDropDownValue = businessType[index];
});
},
),
);
},
),
);
}
_buildMaterialStyleBusinessType(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
color: kThemeStyleButtonFillColour,
width: 1,
)),
margin: EdgeInsets.all(20.0),
width: MediaQuery.of(context).size.width,
child: Center(
child: DropdownButton(
value: _currentLoggedInUserData.businessType == null
? selectedBusinessTypeDropDownValue
: _currentLoggedInUserData.businessType,
elevation: 15,
iconDisabledColor: kThemeStyleButtonFillColour,
iconEnabledColor: kThemeStyleButtonFillColour,
underline: Container(),
items: businessType
.map(
(businessType) => DropdownMenuItem(
value: businessType, child: Text(businessType)),
)
.toList(),
onChanged: (newValue) {
setState(() {
selectedBusinessTypeDropDownValue = newValue;
_currentLoggedInUserData.businessType = newValue;
});
},
),
),
);
}
//trading currency - cupertino and material styles
_buildCupertinoStyleTradingCurrency(BuildContext context) {
return GestureDetector(
onTap: () => showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return Container(
color: Colors.white,
height: MediaQuery.of(context).copyWith().size.height / 4,
child: CupertinoPicker(
magnification: 1.5,
children:
List<Widget>.generate(tradingCurrency.length, (int index) {
return Center(
child: Text(
tradingCurrency[index].toString(),
softWrap: true,
style: TextStyle(fontSize: 15.0),
),
);
}),
itemExtent: 25,
onSelectedItemChanged: (index) {
setState(() {
selectedCurrencyDropDownValue = tradingCurrency[index];
});
},
),
);
},
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(0.5)),
border: Border.all(
color: kThemeStyleButtonFillColour,
width: 1,
)),
padding: const EdgeInsets.fromLTRB(10.0, 12.0, 10.0, 12.0),
margin: EdgeInsets.all(20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(selectedCurrencyDropDownValue),
Icon(
FontAwesomeIcons.caretDown,
color: kThemeStyleButtonFillColour,
),
],
),
),
);
}
_buildMaterialStyleTradingCurrency(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
color: kThemeStyleButtonFillColour,
width: 1,
)),
margin: EdgeInsets.all(20.0),
width: MediaQuery.of(context).size.width,
child: Center(
child: DropdownButton(
value: _currentLoggedInUserData.businessType == null
? selectedCurrencyDropDownValue
: _currentLoggedInUserData.businessTradingCurrency,
icon: Icon(
FontAwesomeIcons.caretDown,
color: kThemeStyleButtonFillColour,
),
elevation: 15,
underline: Container(
color: kThemeStyleButtonFillColour,
),
items: tradingCurrency
.map(
(tradingCurrency) => DropdownMenuItem(
value: tradingCurrency, child: Text(tradingCurrency)),
)
.toList(),
onChanged: (newValue) {
setState(() {
selectedCurrencyDropDownValue = newValue;
_currentLoggedInUserData.businessTradingCurrency = newValue;
});
},
),
),
);
}
//address field
_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'),
),
);
}
//city field
_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'),
),
);
}
//postcode field
_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'),
),
);
}
//state field
_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'),
),
);
}
//country field - cupertino and material styles
_buildCupertinoStyleCountry(BuildContext context) {
return GestureDetector(
onTap: () => showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return Container(
color: Colors.white,
height: MediaQuery.of(context).copyWith().size.height / 4,
child: CupertinoPicker(
magnification: 1.5,
children: List<Widget>.generate(country.length, (int index) {
return Center(
child: Text(
country[index].toString(),
softWrap: true,
style: TextStyle(fontSize: 15.0),
),
);
}),
itemExtent: 25,
onSelectedItemChanged: (index) {
setState(() {
selectedCountryDropDownValue = country[index];
});
},
),
);
},
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(0.5)),
border: Border.all(
color: kThemeStyleButtonFillColour,
width: 1,
),
),
padding: const EdgeInsets.fromLTRB(10.0, 12.0, 20.0, 12.0),
margin: EdgeInsets.all(20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(selectedCountryDropDownValue),
Icon(
FontAwesomeIcons.caretDown,
color: kThemeStyleButtonFillColour,
),
],
),
),
);
}
_buildMaterialStyleCountry(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
color: kThemeStyleButtonFillColour,
width: 1,
),
),
margin: EdgeInsets.all(20.0),
width: MediaQuery.of(context).size.width,
child: Center(
child: DropdownButton(
value: _currentLoggedInUserData.country == null
? selectedCountryDropDownValue
: _currentLoggedInUserData.country,
elevation: 15,
iconDisabledColor: kThemeStyleButtonFillColour,
iconEnabledColor: kThemeStyleButtonFillColour,
underline: Container(),
items: country
.map(
(country) =>
DropdownMenuItem(value: country, child: Text(country)),
)
.toList(),
onChanged: (newValue) {
setState(() {
selectedCountryDropDownValue = newValue;
_currentLoggedInUserData.country = newValue;
});
},
),
),
);
}
//logged in user personal info build
//first name
_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'),
),
);
}
//last 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'),
),
);
}
//phone number
_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'),
),
);
}
}
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';
}
return value;
}
}