让我们回想下android做一个登录界面需要什么。一个登录界面顶部logo、logo下一个登录标题、一个用户名输入框、一个密码输入框、一个登录按钮。如下图:

一、我们新创建一个登录的页面,flutter的界面创建和andorid的有所不同,android的是一个activity+layout.xml,而flutter的是一个Page+一个PageState :
Page:
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
一般都继承StatefulWidget。
PageState:
class _LoginPageState extends State<LoginPage> {
@override
Widget build(BuildContext context) {
........ 这里是创建界面的代码
}
}
PageState里面必须有build方法,还可以继承其它法如:
1、@override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); //添加观察者 }
2、// 生命周期变化时回调 // resumed:应用可见并可响应用户操作,app进入前台 // inactive:用户可见,但不可响应用户操作,比如来了个电话,前后台切换的过渡状态 // paused:已经暂停了,用户不可见、不可操作,app进入后台 // suspending:应用被挂起,此状态IOS永远不会回调 @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); print("didChangeAppLifecycleState: $state"); }
3、//当前系统改变了一些访问性活动的回调 @override void didChangeAccessibilityFeatures() { super.didChangeAccessibilityFeatures(); print("didChangeAccessibilityFeatures"); }
4、//低内存回调 @override void didHaveMemoryPressure() { super.didHaveMemoryPressure(); print("didHaveMemoryPressure"); }
5、//应用尺寸改变时回调,例如旋转 @override void didChangeMetrics() { super.didChangeMetrics(); Size size = WidgetsBinding.instance.window.physicalSize; print("didChangeMetrics :宽:{size.width} 高:{size.height}"); }
6、@override void dispose() { super.dispose(); WidgetsBinding.instance.removeObserver(this); //销毁观察者 }
二、当我们的登录界面的主类建好后,下面我们要做的是怎么绘制一个完整的登录界面:
如果有学习过js框架里面的extjs,会对flutter创建界面的方式有很熟悉的感觉。
这里我们用:
Container + SingleChildScrollView 做整体布局,Container做整个的组合容器,同时加渐变颜色,当然也可以加背景图,SingleChildScrollView 里面加Column的子控件,column下加多个字控件,子控件居中。完整的创建界面代码如下:
Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(// 使用线性渐变
begin: Alignment.topLeft,// 渐变开始位置
end: Alignment.bottomRight, // 渐变结束位置
colors: [ // 定义渐变颜色列表
Colors.blueAccent,// 起始颜色
Colors.purpleAccent// 结束颜色
],
),
),
child: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(//用于垂直堆叠子控件
mainAxisAlignment: MainAxisAlignment.center,//所有子控件居中
children: <Widget>[
Image.asset(
"assets/images/go_logo.png",
width: 100,
height: 100,
),
SizedBox(height: 20.0),//加一个空白行
// 提示文本
Text(
'欢迎回来',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(height: 20.0),
// 账号输入框
TextField(
controller: _usernameController,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
hintText: '账号',
prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide.none,
),
),
),
SizedBox(height: 10.0),
// 密码输入框
TextField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
hintText: '密码',
prefixIcon: Icon(Icons.lock),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide.none,
),
),
),
SizedBox(height: 20.0),
// 登录按钮
ElevatedButton(
onPressed: _login,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.blueAccent,
backgroundColor: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 50,
vertical: 15,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
child: Text(
'立即登录',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(height: 10.0),
// 忘记密码按钮
TextButton(
onPressed: () {
Navigator.of(context).push(MyRoute(FindPasswordPage()));
// Navigator.push(context, MaterialPageRoute(builder: (context) {
// return FindPasswordPage();
// }));
},
child: Text(
'忘记密码?',
style: TextStyle(color: Colors.white, fontSize: 16.0),
),
),
SizedBox(height: 20.0),
// 注册提示
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'还没有账号? ',
style: TextStyle(color: Colors.white, fontSize: 16.0),
),
TextButton(
onPressed: () {
_register();
},
child: Text(
'立即注册',
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
),
),
),
);
三、very good 现在我们已经创建好了登录界面,那么已经完成工作了吗,当然不是,整个界面还不能点击,无法响应登录事件:
我们需要做三件事:获取点击事件、获取用户名、获取密码。
1、获取点击事件:
ElevatedButton( onPressed: _login 从上面代码可以看到ElevatedButton 有一个onPressed接收点击事件方法,点击后直接调用_login调用登录方法:
// 登录逻辑
void _login() {
_usernameController.text = "lb";
_passwordController.text = "123";
String username = _usernameController.text.trim();
String password = _passwordController.text.trim();
Result loginResult = loginService.login(username, password);
// 验证账号和密码是否为空
if (loginResult.result == false) {
String? errorMessage = loginResult.resultMessage;
Fluttertoast.showToast(
msg: errorMessage??"",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.black,
textColor: Colors.white,
fontSize: 16.0
);
}else {
_goToMainPage();
}
}
2、获取用户名和密码
这里我们还注意到要获取登录名和密码,需要有两个和text绑定的controller:
// 用于保存用户输入的账号和密码
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
这一点和我们写android和ios有所区别的哦。
TextEditingController是一个核心类,用于控制文本输入框(如TextField或TextFormField)的文本内容、焦点和事件监听。它允许开发者动态设置初始文本、获取用户输入、监听变化并管理资源。
四、下面我把整个代码片段奉上:
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:aieshop/findpassword.dart';
import 'package:aieshop/route.dart';
import 'package:aieshop/register.dart';
import 'package:aieshop/homepage.dart';
import 'package:aieshop/service/login/LoginService.dart';
import 'package:aieshop/service/base/baseservice.dart';
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
LoginService loginService = LoginService();
// 用于保存用户输入的账号和密码
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
// 登录逻辑
void _login() {
_usernameController.text = "lb";
_passwordController.text = "123";
String username = _usernameController.text.trim();
String password = _passwordController.text.trim();
Result loginResult = loginService.login(username, password);
// 验证账号和密码是否为空
if (loginResult.result == false) {
String? errorMessage = loginResult.resultMessage;
Fluttertoast.showToast(
msg: errorMessage??"",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.black,
textColor: Colors.white,
fontSize: 16.0
);
}else {
_goToMainPage();
}
}
void _register(){
Navigator.of(context).push(MyRoute(RegisterPage()));
}
void _goToMainPage(){
Navigator.of(context).push(MyRoute(WillPopScopeHome()));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(// 使用线性渐变
begin: Alignment.topLeft,// 渐变开始位置
end: Alignment.bottomRight, // 渐变结束位置
colors: [ // 定义渐变颜色列表
Colors.blueAccent,// 起始颜色
Colors.purpleAccent// 结束颜色
],
),
),
child: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(//用于垂直堆叠子控件
mainAxisAlignment: MainAxisAlignment.center,//所有子控件居中
children: <Widget>[
Image.asset(
"assets/images/go_logo.png",
width: 100,
height: 100,
),
SizedBox(height: 20.0),//加一个空白行
// 提示文本
Text(
'欢迎回来',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(height: 20.0),
// 账号输入框
TextField(
controller: _usernameController,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
hintText: '账号',
prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide.none,
),
),
),
SizedBox(height: 10.0),
// 密码输入框
TextField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
hintText: '密码',
prefixIcon: Icon(Icons.lock),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide.none,
),
),
),
SizedBox(height: 20.0),
// 登录按钮
ElevatedButton(
onPressed: _login,
style: ElevatedButton.styleFrom(
foregroundColor: Colors.blueAccent,
backgroundColor: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 50,
vertical: 15,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
child: Text(
'立即登录',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(height: 10.0),
// 忘记密码按钮
TextButton(
onPressed: () {
Navigator.of(context).push(MyRoute(FindPasswordPage()));
// Navigator.push(context, MaterialPageRoute(builder: (context) {
// return FindPasswordPage();
// }));
},
child: Text(
'忘记密码?',
style: TextStyle(color: Colors.white, fontSize: 16.0),
),
),
SizedBox(height: 20.0),
// 注册提示
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'还没有账号? ',
style: TextStyle(color: Colors.white, fontSize: 16.0),
),
TextButton(
onPressed: () {
_register();
},
child: Text(
'立即注册',
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
),
),
),
);
}
}