【发布时间】:2018-03-03 21:59:11
【问题描述】:
我的应用程序中有一个奇怪的问题,我有一个注册页面,其中 ListView 中有几个 textformfields,而 ListView 是表单的子项。
当我到达 ListView 中的最后一个表单域时,我需要向上滚动屏幕才能真正看到我在该域中输入的内容。当我向上滚动时,失去焦点的文本表单字段会丢失输入的文本并初始化为空。我不确定我错过了什么,或者我是否必须使用其他方式来确保我保留 TextFormField 值。我尝试在每个 TextFormField 中使用控制器,然后即使我向上滚动它也会保留该值。但是,对象的属性,例如 hostelData.hostelName 仅在第一次更新,并且从第二次开始,更改不会反映在对象属性中。我不太确定我错过了什么?请有人可以帮我解决这个问题。 请原谅我发帖时的错误,因为我是编程新手,任何帮助将不胜感激。
我已经发布了下面的代码,以便您可以重现相同的问题。
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:myhostel/theme.dart' as Theme;
import 'package:myhostel/globals.dart' as gl;
class SignUp extends StatefulWidget {
const SignUp({ Key key }) : super(key: key);
@override
_SignUpState createState() => new _SignUpState();
}
class HostelData{
String hostelName = '';
String ownersName = '';
String mobileNumber = '';
String emailId = '';
String password = '';
String city = '';
String hostelType= 'mens';
String confirmPassword ='';
}
class _SignUpState extends State<SignUp> {
HostelData hostelData = new HostelData();
void showInSnackBar(String value) {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(value),
duration: const Duration(milliseconds: 3000),
));
}
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
final GlobalKey<FormFieldState<String>> _passwordFieldKey = new
GlobalKey<FormFieldState<String>>();
bool _autovalidate = false;
bool _formWasEdited = false;
void _handleSubmitted() {
final FormState form = _formKey.currentState;
if (!form.validate()) {
_autovalidate = true; // Start validating on every change.
showInSnackBar('Please fix the errors in red before submitting.');
} else {
form.save();
showInSnackBar('${hostelData.ownersName}\'s hostel name is
${hostelData.hostelName}');
_createuserwithemailandpassword();
}
}
String _validateName(String value) {
_formWasEdited = true;
if (value.isEmpty)
return 'Name is required.';
final RegExp nameExp = new RegExp(r'^[A-Za-z ]+$');
if (!nameExp.hasMatch(value))
return 'Please enter only alphabetical characters and spaces.';
return null;
}
String _validatePassword(String value) {
_formWasEdited = true;
final FormFieldState<String> passwordField =
_passwordFieldKey.currentState;
if (passwordField.value == null || passwordField.value.isEmpty)
return 'Please choose a password.';
if (passwordField.value != value)
return 'Passwords don\'t match';
return null;
}
@override
Widget build(BuildContext context){
return new MaterialApp(
theme: Theme.MyHostelThemeData,
home: new Scaffold(
key: _scaffoldKey,
body: new Container(
child: new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('assets/bg.png'),
fit: BoxFit.fill,
),
),
child: new Form(
key: _formKey,
autovalidate: _autovalidate,
child: new ListView(
children: <Widget>[
new Container(
margin: const EdgeInsets.only(top:32.0),
child: new Center(
child: new Text('SIGN UP',
style: new TextStyle(
color: const Color(0xFFF5FEFD),fontSize:24.0,fontWeight:
FontWeight.bold),
),
),
),
new Row(
children:[
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(decoration: new InputDecoration(
/*hintStyle: new TextStyle(
fontSize: 20.0,color: const Color(0xFFF5FEFD),),*/
labelText: 'Hostel Name',
),
/*style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),*/
onSaved:(String value){ hostelData.hostelName = value; },
validator: _validateName,
),
),
),
],
),
new Row(
children:[
/*new Container(
margin: const EdgeInsets.all(8.0),
width: 24.0,
child: new Image.asset(
'assets/person_avatar.png',
fit: BoxFit.contain,
//alignment: FractionalOffset.center,
),
),*/
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(
//controller: _ownersNameController,
decoration: new InputDecoration(//hintText: 'Mobile
Number',
/* hintStyle: new TextStyle(fontSize: 20.0,color: const
Color(0xFFF5FEFD),),*/
labelText: 'Owners Name',
),
/*style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),*/
onSaved: (String value) { hostelData.ownersName = value;
},
validator: _validateName,
),
),
),
],
),
new Row(
children:[
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(
//controller: _mobileNumberController,
keyboardType: TextInputType.phone,
decoration: new InputDecoration(//hintText: 'Mobile
Number',
/* hintStyle: new TextStyle(fontSize: 20.0,color:
const Color(0xFFF5FEFD),),*/
labelText: 'Mobile Number',
//prefixText: '+91',
),
/*style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),*/
onSaved: (String value) { hostelData.mobileNumber = value; },
//validator: _validatePhoneNumber,
),
),
),
],
),
//implements EmailID Row
new Row(
children:[
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(
//controller: _emailIdController,
decoration: new InputDecoration(
//hintText: 'Mobile Number',
/*hintStyle: new TextStyle(fontSize: 20.0,color: const
Color(0xFFF5FEFD),),*/
labelText: 'EMail Id',
helperText: 'Required',
),
/*style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),*/
onSaved: (String value){hostelData.emailId = value;},
),
),
),
],
),
//implements the password row
new Container(
padding: const EdgeInsets.only(left: 8.0,),
child: new Row(
children: <Widget>[
new Container(
margin: const EdgeInsets.only(top: 16.0),
//padding: const EdgeInsets.all(8.0),
child: new Icon(Icons.lock_outline,
color: const Color(0xFFF5FEFD)
),
),
new Expanded(
child: new Container(
margin: const EdgeInsets.only(
left: 28.0, right: 12.0,),
child: new TextFormField(
key: _passwordFieldKey,
//controller: _passwordController,
decoration: const InputDecoration(
hintText: 'min 8 characters',
labelText: 'Password',
helperText: 'Required',
),
autocorrect: false,
//obscureText: true,
//validator: 'Required',
/*style: DefaultTextStyle.of(context).style.merge(new
TextStyle(
fontSize: 16.0,
color: CustomColors.fontColor,
),
),*/
onSaved: (String value){hostelData.password = value;},
),
),
),
],
),
),
new Container(
padding: const EdgeInsets.only(left: 8.0,),
child: new Row(
children: <Widget>[
new Container(
margin: const EdgeInsets.only(top: 16.0),
//padding: const EdgeInsets.all(8.0),
child: new Icon(Icons.lock_outline,
color: const Color(0xFFF5FEFD)
),
),
new Expanded(
child: new Container(
margin: const EdgeInsets.only(
left: 28.0, right: 12.0,),
child: new TextFormField(
//controller: _confirmpasswordcontroller,
decoration: const InputDecoration(
labelText: 'Confirm Password',
helperText: 'Required'
),
autocorrect: false,
// obscureText: true,
//validator: 'Required',
/*style: DefaultTextStyle.of(context).style.merge(new
TextStyle(
fontSize: 16.0,
color: CustomColors.fontColor,
),
),*/
//onSaved: (String value){hostelData.confirmPassword =
value;},
validator: _validatePassword,
),
),
),
],
),
),
new Row(
children:[
new Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 24.0,right: 12.0,),
child:new TextFormField(
controller: _cityController,
decoration: new InputDecoration(labelText: 'City*'),
onSaved: (String value){hostelData.city = value;},
validator: _validateName,
),
),
),
],
),
new Container(
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.only(left:24.0,right: 24.0),
decoration: new BoxDecoration(
border: new Border.all(
color: Colors.white,
width: 2.0,
),
borderRadius: new BorderRadius.all(const
Radius.circular(32.0),)
),
child: new FlatButton(
onPressed: ((){
print('SignUp Button Clicked');
_handleSubmitted();
}),
child: new Text (
'SIGN UP',
style: new TextStyle(fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
new Row(
children:[
new Container(
margin: const EdgeInsets.only(left: 24.0,bottom: 12.0),
child:new Text(
'Already have an account?',
style: new TextStyle(
fontSize: 20.0,
color: Colors.white,
),
),
),
new Container(
padding: const EdgeInsets.only(right: 8.0,top:
4.0,bottom:16.0),
margin: const EdgeInsets.only(right: 12.0,left: 4.0),
child:
new MaterialButton(
onPressed: ((){
//Sign In Button pressed declaration here
print('sign in button clicked');
}),
child: new Text('SignIn',
style: new TextStyle(
fontSize: 16.0,
color: Colors.teal[200],
),
),
minWidth: 16.0,
),
),
],
),
new Container(
padding: const EdgeInsets.only(left: 16.0),
child: new Text('* indicates required field',
style: new TextStyle(
fontSize: 14.0,
color: Colors.white,
),),
),
]
),
),
),
),
),
);
} // widget ends here
globals.dart
library my_hostel.globals;
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
final FirebaseAuth auth = FirebaseAuth.instance;
final DatabaseReference messagesRef =FirebaseDatabase.instance.reference();
【问题讨论】:
-
您缺少文本字段的控制器。以下是关于 TextEditingControllers 的文档:docs.flutter.io/flutter/widgets/… ListView 会移除因性能原因而无法查看的子项,并在它们再次出现时再次添加它们。如果没有控制器,文本字段将使用空字符串进行初始化。我觉得我以前回答过这个问题。找到链接后我会发布。
-
感谢您的回复,我想到了这一点并意识到我正在丢失字符串值,因为我没有使用控制器,然后尝试使用控制器。 '瞧'它起作用了,但是快乐只是短暂的,因为当我再次编辑字段时,新值不会像示例 hostelData.hostelName、hostelData.emailId 等那样写入对象字段,只分配了第一个值不是稍后完成的更改。很抱歉没有看到您之前的回答,但非常感谢您的帮助。