由于当前项目的账号是直接使用Google账户,iOS系统问题不大,但是Android系统如果没有Google框架,则无法使用我们的应用程序。因此,我们需要创建自己的账户数据。在这篇文章中,我们会创建一个登陆屏幕和注册屏幕,两个屏幕的UI如下图所示:
关于UI布局的内容不是这篇文章的重点,所以不会具体描述,只会展示代码并陈述布局思路。有关UI布局的内容可以查看《Flutter进阶—构建布局实例》、《Flutter进阶—布局方法演示》、《Flutter进阶—布局一个控件》、《Flutter进阶—垂直和水平布局》等文章。
实现登陆屏幕
首先添加三个图像资源,分别为登陆屏幕的背景、注册屏幕的背景和应用程序的Logo,具体的添加方法可以查看《Flutter基础—常用控件之图片》。
我们在项目的lib
目录下创建一个sign_in.dart
文件,并添加下面的代码。
import 'package:flutter/material.dart';
class SignIn extends StatefulWidget {
@override
State createState() => new SignInState();
}
class SignInState extends State<SignIn> {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
new Opacity(
opacity: 0.3,
child: new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new ExactAssetImage('images/sign_in_background.jpg'),
fit: BoxFit.cover,
),
),
)),
new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
],
)
]));
}
}
为了实现透明背景,我们使用Stack
控件,它可以一层一层地放置控件。Stack
控件的第一层是Opacity
控件,它可以控件其子控件的透明度,我们用Container
控件包装指定的背景图片作为其子控件,并把透明度设置为0.3
。第二层是Column
控件,它能让多个子控件排成一列。mainAxisAlignment
属性设置成MainAxisAlignment.spaceEvenly
以使子控件们均匀的占用空间,crossAxisAlignment
属性设置成CrossAxisAlignment.start
以使子控件们靠左排列。现在我们开始往Column
控件中添加子控件。
class SignInState extends State<SignIn> {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
//...
new Column(
//...
children: <Widget>[
new Center(
child: new Image.asset(
'images/talk_casually.png',
width: MediaQuery.of(context).size.width * 0.4,
)),
],
)
]));
}
}
第一个子控件是Image
控件,由于其父控件设置成靠左排列,因此将其包装到Center
控件中,以使Image
控件居中显示。同时将Image
控件的width
属性设置为当前屏幕宽度的40%,使图像以合适的大小显示。
class SignInState extends State<SignIn> {
final TextEditingController _usernameController = new TextEditingController();
final TextEditingController _passwordController = new TextEditingController();
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
//...
new Column(
//...
children: <Widget>[
//...
new Container(
width: MediaQuery.of(context).size.width * 0.96,
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new TextField(
controller: _usernameController,
decoration: new InputDecoration(
hintText: 'Username',
icon: new Icon(
Icons.account_circle,
),
),
),
new TextField(
controller: _passwordController,
obscureText: true,
keyboardType: TextInputType.number,
decoration: new InputDecoration(
hintText: 'Password',
icon: new Icon(
Icons.lock_outline,
),
),
),
])),
],
)
]));
}
}
第二个控件是Column
控件,其包括两个TextField
子控件,用于输入用户名与登陆密码。把Column
控件包装在Container
控件中,并且将其width
属性设置成当前屏幕宽度的96%,是因为如果不这样做,效果如下所示:
上图中,输入框的输入区域一直到屏幕的边缘,反之,如果设置成96%的宽度,效果如下所示:
为了使屏幕整体效果更和谐,这里使输入框的输入区域与屏幕边缘保持一定距离。当然,具体使用怎样的效果还是看个人的审美吧!
class SignInState extends State<SignIn> {
//...
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
//...
new Column(
//...
children: <Widget>[
//...
new FlatButton(
child: new Container(
decoration: new BoxDecoration(
color: Theme.of(context).accentColor,
),
child: new Center(
child: new Text("Sign In",
style: new TextStyle(
color: const Color(0xff000000),
))),
),
onPressed: () {
print('Sign In');
},
),
],
)
]));
}
}
第三个子控件是FlatButton
控件,它是一个平面按钮,后面我们会在这里添加登陆的代码。
class SignInState extends State<SignIn> {
//...
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
//...
new Column(
//...
children: <Widget>[
//...
new Center(
child: new FlatButton(
child: new Text("Don't have an account ? Sign Up",
style: new TextStyle(
color: const Color(0xff000000),
)),
onPressed: null,
))
],
)
]));
}
}
第四个子控件也是FlatButton
控件,不过为了居中显示,将其包装在Center
控件中。这个按钮用于导航到后面的注册屏幕。
显示登陆屏幕
我们在项目的lib
目录下创建一个chat_screen.dart
文件,然后我们将之前聊天屏幕的代码转移到新建的chat_screen.dart
文件中,使main.dart
文件显示以下代码:
import 'package:flutter/material.dart';
import 'sign_in.dart';
void main() {
runApp(new TalkcasuallyApp());
}
class TalkcasuallyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: '谈天说地',
home: new SignIn()
);
}
}
重新运行应用程序,就能显示我们的登陆屏幕。
实现注册屏幕
我们在项目的lib
目录下创建一个sign_up.dart
文件,并添加下面的代码。
import 'package:flutter/material.dart';
class SignUp extends StatefulWidget {
@override
State createState() => new SignUpState();
}
class SignUpState extends State<SignUp> {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
new Opacity(
opacity: 0.3,
child: new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new ExactAssetImage('images/sign_up_background.jpg'),
fit: BoxFit.cover,
),
),
)),
new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
])
]));
}
}
上面的代码与登陆屏幕相似,所以我们直接往Column
控件中添加子控件。
class SignUpState extends State<SignUp> {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
//...
new Column(
//...
children: <Widget>[
new BackButton(),
new Text(" Sign Up",
textScaleFactor: 2.0,
style: new TextStyle(
color: const Color(0xff000000),
)),
])
]));
}
}
第一个子控件是BackButton
控件,它会显示当前平台的返回按钮,点击返回到上一个屏幕。第二个子控件是Text
控件,将textScaleFactor
属性设置成2.0
,使字体放大一倍显示。
class SignUpState extends State<SignUp> {
final TextEditingController _usernameController = new TextEditingController();
final TextEditingController _passwordController = new TextEditingController();
final TextEditingController _emailController = new TextEditingController();
final TextEditingController _phoneController = new TextEditingController();
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
//...
new Column(
//...
children: <Widget>[
//...
new Container(
width: MediaQuery.of(context).size.width * 0.96,
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new TextField(
controller: _usernameController,
decoration: new InputDecoration(
hintText: 'Username',
icon: new Icon(
Icons.account_circle,
),
),
),
new TextField(
controller: _passwordController,
obscureText: true,
keyboardType: TextInputType.number,
decoration: new InputDecoration(
hintText: 'Password',
icon: new Icon(
Icons.lock_outline,
),
),
),
new TextField(
controller: _emailController,
decoration: new InputDecoration(
hintText: 'E-mail',
icon: new Icon(
Icons.email,
),
),
),
new TextField(
controller: _phoneController,
decoration: new InputDecoration(
hintText: 'Phone',
icon: new Icon(
Icons.phone,
),
),
),
])),
]));
}
}
第三个控件是Column
控件,与登陆屏幕相似。
class SignUpState extends State<SignUp> {
//...
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
//...
new Column(
//...
children: <Widget>[
//...
new FlatButton(
child: new Container(
width: MediaQuery.of(context).size.width,
decoration: new BoxDecoration(
color: Theme.of(context).accentColor,
),
child: new Center(
child: new Text("Join",
style: new TextStyle(
color: const Color(0xff000000),
))),
),
onPressed: () {
print('Sign In');
},
),
])
]));
}
}
第四个子控件是FlatButton
控件,与登陆屏幕相似,后面会实现用户注册的代码。
class SignUpState extends State<SignUp> {
//...
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
//...
new Column(
//...
children: <Widget>[
//...
new Center(
child: new FlatButton(
child: new Text("Already have an account ? Sign In",
style: new TextStyle(
color: const Color(0xff000000),
)),
onPressed: () {
Navigator.of(context).pop();
},
))
])
]));
}
}
第五个子控件是包装在Center
控件里的FlatButton
控件,点击这个按钮可以返回上一个屏幕。
从登陆屏幕导航到注册屏幕
我们在sign_in.dart
文件中添加一个_openSignUp
方法,代码如下所示:
class SignInState extends State<SignIn> {
//...
void _openSignUp() {
setState(() {
Navigator.of(context).push(new MaterialPageRoute<Null>(
builder: (BuildContext context) {
return new SignUp();
},
));
});
}
//...
}
然后我们将_openSignUp
方法添加到Column
控件的第四个子控件中,代码如下所示:
import 'package:flutter/material.dart';
import 'sign_up.dart';
//...
class SignInState extends State<SignIn> {
//...
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(children: <Widget>[
//...
new Column(
//...
children: <Widget>[
//...
new Center(
child: new FlatButton(
child: new Text("Don't have an account ? Sign Up",
style: new TextStyle(
color: const Color(0xff000000),
)),
onPressed: _openSignUp,
))
],
)
]));
}
}