这几天按照顺序先完成了登录和注册的页面,没有什么特别的东西,就是异步这个东西之前只有在用JS做前端的时候遇到了一次问题,还是同学帮忙解决的,这一次又遇到了所以多花了一点时间debug。
由于是刚刚开始学习flutter,于是就偷懒参考了现成的布局设计和代码,自己修改了一番。
效果如图所示,参考自用flutter写一个精美的登录页面,暂时没有做页面适配,以后学习到了会补上。
布局的代码参考上面引用的教程,这里主要说一下由于登录和注册都是在按钮onPressed之后才触发判断TextFormField中的值,所以使用了全局的GlobalKey来验证输入是否符合要求,但是这样有一个问题,那就是在判断输入符合要求之后在保存,那么在判断确认密码的时候还没有获取到第一次输入密码的值,于是就使用了TextEditingController
来获取输入的值。在onPressed
之后,调用_userRegister()
方法获取值并进行判断。
代码如下:
TextEditingController _controllerMail = new TextEditingController();
TextEditingController _controllerPwd = new TextEditingController();
TextEditingController _controllerRePwd = new TextEditingController();
void _userRegister() {
_email=_controllerMail.text;
_password=_controllerPwd.text;
_password_confirm=_controllerRePwd.text;
if (_formKey.currentState.validate()) {
save();//使用sharedPreferences存储数据
Navigator.pop(context);//返回到登陆页面
}
}
由于暂时只是应用前端部分,没有后台服务器和数据库,所以暂时用shared_preferences暂时存储注册的账户和密码,由于数据存储都是耗时进程,所以都要用到async/await
来执行。
注册界面的存储数据代码:
save() async{
final prefs = await SharedPreferences.getInstance();
prefs.setString(_email,_password);//存数据
}
登陆界面的取数据代码:
getPwd(String userEmail) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_passwordRe=prefs.get(userEmail);//取数据
await check(_password,_passwordRe);//判断密码是否正确
await login();//登陆操作
}
其他关于SharedPreferences
详细操作可以参看Flutter数据存储之shared_preferences,总结的挺不错的。
在写登录页面时遇到了一点小麻烦,第一次输入密码无论正确与否总时被判断为错误,后来发现又是由于异步操作的问题,因为从SharedPreferences
实例中取数据的操作耗时较长,导致先判断之后,变量才被赋值。于是将判断和登录的操作放进await里,问题得到了解决。
第三个问题就是涉及到页面的跳转和参数的传递了。首先我要实现的是,第一次启动App时进入的是登录页面,如果没有账号则点击注册,进入注册界面。原本我认为只是一个简单的Navigator.push()
的方法,但是事实上要稍微复杂一点。例如我从登录界面点击注册到达了注册界面,并且在注册界面完成了注册,点击注册按钮之后,如果直接进入主页,显然不太符合精神模型。一般会返回到登录页面并且已经保存好了用户名,等待输入密码后登录。这才是比较合适的设计。但是如果我点击注册按钮触发的是一个push()
操作,那么我们会进入一个新的登录页面,即语句new Login Page()
,这种情况下,我再点击返回按钮则会经历 “登录界面” => “注册界面” => “登陆界面” =>退出程序。这样似乎也不太合适,所以注册按钮触发的应该是Navigator.pop()
才对。
但是我查了很久才找到接收pop方法参数的例子,相比于接收push参数要相对复杂一点。具体的做法是将跳转语句放入await
中,通过一个Future
对象获得返回参数的值。
代码如下:
//构建注册按钮
Align buildRegisterText(BuildContext context) {
return Align(
alignment: Alignment.center,
child: Padding(
padding: EdgeInsets.only(top: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('没有账号?'),
GestureDetector(
child: Text(
'点击注册',
style: TextStyle(color: Colors.green),
),
onTap: () {
_toRegisterPage(context);//跳转注册页面方法。
},
),
],
),
),
);
}
_toRegisterPage(BuildContext context) async{
final preEmail = await Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new RegisterPage()));
_email=new Text("$preEmail").data;//接收传递的参数
_email = _email.substring(1,_email.length-1);//处理为已注册的用户邮箱
}
注册界面pop传递参数相对简单,代码如下:
Navigator.pop(context,[_email]);//传递用户邮箱
登陆界面对于邮箱输入框的构造也要加以改动,代码如下:
TextFormField buildEmailTextField() {
return TextFormField(
decoration: InputDecoration(
labelText: 'Email Address',
),
controller: TextEditingController.fromValue(
TextEditingValue(
text: _email//填入预先存储的邮箱
)
),
......
}
到这里,简单的登录注册界面就算是完成了,后期会对屏幕适配和布局进行优化和改动,并且有空会实现记住已登录用户并且选择账号登录的功能,加入网络请求之后也会进行改动和添加。
参考资料: