第4天:登录界面完善
本文介绍了Flutter登录功能的实现方法,主要包括三个关键步骤:1)为登录界面添加背景图;2)对密码进行MD5加密处理;3)实现基本的账号密码验证功能。核心代码展示了完整的登录流程:先进行表单验证,然后显示加载状态,接着将用户输入的密码进行MD5加密,验证用户名和加密密码是否正确,验证通过后跳转到首页,最后关闭加载状态。文章详细解析了代码中涉及的异步编程、表单验证、状态管理、文本控制器、MD5加
·
文章目录
今日目标:
1.给登录界面添加一个背景图;
2.给密码添加一个 md5 加密,
3.做一个账号密码的简单的判断,正确点击登录即可跳转到首页。
最后效果为:
一、先给整体结论
这段代码是一个Flutter登录功能的核心方法:先校验表单是否填写合法 → 显示加载状态 → 把用户输入的密码做MD5加密 → 校验用户名和加密后的密码是否正确 → 正确就跳转到首页 → 最后关闭加载状态。
二、逐行拆解代码(小白友好版)
// 1. 方法定义:异步的登录方法,无返回值
Future<void> _login() async {
// 2. 校验表单:检查输入框(比如用户名/密码)是否填对(比如非空、格式正确)
if (_formKey.currentState!.validate()) {
// 3. 开启加载状态:比如让登录按钮变成“加载中”,防止用户重复点击
setState(() {
_isLoading = true;
});
// 4. MD5加密密码:把明文密码转成不可逆的加密字符串
// ① utf8.encode:把密码字符串转成UTF8编码的字节(MD5需要字节数据)
// ② md5.convert:对字节做MD5加密
// ③ .toString:把加密结果转成字符串(方便对比)
final md5Password = md5.convert(utf8.encode(_passwordController.text)).toString();
// 5. 打印加密后的密码:方便开发时调试,看加密是否正确
print("$md5Password");
// 6. 校验账号密码:
// 用户名必须是"username",加密后的密码必须是"e10adc3949ba59abbe56e057f20f883e"(这是12345的MD5值)
if (_usernameController.text == 'username' &&
md5Password == 'e10adc3949ba59abbe56e057f20f883e') {
// 7. 验证成功:跳转到首页(/home是页面的“名字”,替换当前登录页,无法返回)
Navigator.of(context).pushReplacementNamed('/home');
}
// 8. 关闭加载状态:不管登录成功/失败,都把“加载中”状态关掉
setState(() {
_isLoading = false;
});
}
}
三、涉及的核心知识点(小白必懂)
我把知识点按“基础→进阶”排序,每个知识点都用大白话解释:
1. 异步编程:Future<void> + async
- 是什么:
Future<void>表示这个方法是“异步执行”的,async是异步方法的标记; - 为什么用:登录通常要调后台接口(网络请求),需要等服务器返回结果,异步能避免APP卡屏;
- 小白理解:就像你点外卖,不用一直等,先做别的事,外卖到了再处理(这段代码里暂时没真正的网络请求,但预留了异步的写法)。
2. 表单校验:_formKey.currentState!.validate()
- 核心角色:
_formKey:是绑定到表单(Form)的“钥匙”,用来控制/校验表单;currentState!.validate():触发表单里所有输入框的校验规则(比如用户名不能为空、密码长度≥6位);
- 小白理解:相当于考试前老师检查你有没有填准考证(输入框有没有填对),只有检查通过,才继续下一步登录。
3. 状态管理:setState(() {})
- 是什么:Flutter中更新UI的核心方法;
- 作用:修改
_isLoading(加载状态)后,调用setState会让页面重新渲染,比如登录按钮从“登录”变成“加载中…”; - 小白理解:就像你改了微信昵称,点“保存”后,界面才会显示新昵称(
setState就是“保存”这个动作)。
4. 文本控制器:_passwordController.text
- 是什么:
TextEditingController是控制输入框的“工具”; - 作用:
_passwordController.text能获取用户在密码输入框里输入的内容; - 小白理解:相当于你在输入框里写了密码,这个控制器就是“抄下来”密码的小本本。
5. 加密:MD5
- 是什么:一种不可逆的加密算法(只能加密,不能解密);
- 这段代码里的逻辑:
- 用户输入明文密码“12345” → 转成UTF8字节 → MD5加密 → 得到字符串“e10adc3949ba59abbe56e057f20f883e”;
- 代码里直接对比加密后的字符串,而不是明文(安全);
- 小白理解:就像你把纸条(明文密码)放进密码箱(MD5),只能看加密后的样子,打不开箱子看原纸条。
- ✨ 注意:用MD5前要先导入依赖,在
pubspec.yaml里加:
然后代码顶部导入:dependencies: crypto: ^3.0.3 # 提供MD5加密功能import 'package:crypto/crypto.dart'; import 'dart:convert'; // 提供utf8.encode
6. 路由导航:pushReplacementNamed('/home')
- 核心作用:
push:跳转到新页面(首页);Replacement:替换当前页面(登录页),登录成功后不能返回登录页;Named('/home'):通过“页面名字”(/home)跳转(需要提前给首页注册这个名字);
- 小白理解:相当于你考完试(登录成功),从考场(登录页)直接去教室(首页),并且把考场的门锁了(不能返回)。
四、代码完善(小白也能看懂的优化点)
- 加载状态逻辑小漏洞:如果登录成功跳转到首页,页面已经销毁了,还会执行
setState(() {_isLoading = false;}),可能报错;
✅ 优化:把跳转后的setState包在else里,或者用if (mounted)判断页面是否还在:if (用户名密码正确) { Navigator.of(context).pushReplacementNamed('/home'); } else { setState(() {_isLoading = false;}); // 只有失败才关闭加载 } - 无错误提示:密码输错时,用户看不到任何提示,不知道哪里错了;
✅ 优化:输错时弹个提示框:else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('用户名或密码错啦!')), ); setState(() {_isLoading = false;}); }
总结
- 核心逻辑:表单校验 → 加载状态 → 密码加密 → 账号密码校验 → 跳转/提示 → 关闭加载;
- 核心知识点:异步编程、表单校验、状态管理、文本控制器、MD5加密、路由导航;
- 小白重点:先记住
setState是更新UI、Controller拿输入内容、pushReplacementNamed是跳转且不返回,这三个是最常用的。
第四天最终代码:
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
import 'package:envhelper/src/register_page.dart';
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
bool _obscureText = true;
bool _isLoading = false;
void dispose() {
// TODO: implement dispose
_usernameController.dispose();
_passwordController.dispose();
super.dispose();
}
void _togglePasswordVisibility() {
setState(() {
_obscureText = !_obscureText;
});
}
Future<void> _login() async {
if (_formKey.currentState!.validate()) {
setState(() {
_isLoading = true;
});
// 对密码进行md5 加密
final md5Password = md5
.convert(utf8.encode(_passwordController.text))
.toString();
print("$md5Password");
// 如果用户名为 username,密码为 12345 则验证通过
if (_usernameController.text == 'username' &&
md5Password == 'e10adc3949ba59abbe56e057f20f883e') {
// 验证成功,跳转到主页
Navigator.of(context).pushReplacementNamed('/home');
} else {
// 验证失败,显示错误信息
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('用户名或密码错误!'), backgroundColor: Colors.red),
);
setState(() {
_isLoading = false;
});
}
}
}
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/bg.png"),
fit: BoxFit.fill,
),
),
child: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'登录',
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 48),
// 用户名输入框
TextFormField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: '用户名',
prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return "please enter your username";
}
return null;
},
),
const SizedBox(height: 16),
// 密码输入框
TextFormField(
controller: _passwordController,
obscureText: _obscureText, // 是否隐藏密码
decoration: InputDecoration(
labelText: '密码',
prefixIcon: Icon(Icons.lock),
suffixIcon: IconButton(
icon: Icon(
_obscureText
? Icons.visibility_off
: Icons.visibility,
),
onPressed: _togglePasswordVisibility,
),
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return "please enter your password";
}
if (value.length < 6) {
return "password must be at least 6 characters";
}
return null;
},
),
const SizedBox(height: 24),
// 登录按钮
ElevatedButton(
onPressed: _isLoading ? null : _login,
child: _isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text('登录', style: TextStyle(fontSize: 16)),
),
const SizedBox(height: 16),
// 注册按钮
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RegisterPage(),
),
);
},
child: Text("注册"),
),
Container(
padding: EdgeInsets.all(16.0),
child: Text(
'忘记密码?',
style: TextStyle(color: Colors.blue, fontSize: 16.0),
textAlign: TextAlign.center,
),
),
],
),
),
),
),
),
),
);
}
}
更多推荐



所有评论(0)