今日目标:
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)跳转(需要提前给首页注册这个名字);
  • 小白理解:相当于你考完试(登录成功),从考场(登录页)直接去教室(首页),并且把考场的门锁了(不能返回)。

四、代码完善(小白也能看懂的优化点)

  1. 加载状态逻辑小漏洞:如果登录成功跳转到首页,页面已经销毁了,还会执行 setState(() {_isLoading = false;}),可能报错;
    ✅ 优化:把跳转后的 setState 包在 else 里,或者用 if (mounted) 判断页面是否还在:
    if (用户名密码正确) {
      Navigator.of(context).pushReplacementNamed('/home');
    } else {
      setState(() {_isLoading = false;}); // 只有失败才关闭加载
    }
    
  2. 无错误提示:密码输错时,用户看不到任何提示,不知道哪里错了;
    ✅ 优化:输错时弹个提示框:
    else {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('用户名或密码错啦!')),
      );
      setState(() {_isLoading = false;});
    }
    

总结

  1. 核心逻辑:表单校验 → 加载状态 → 密码加密 → 账号密码校验 → 跳转/提示 → 关闭加载;
  2. 核心知识点:异步编程、表单校验、状态管理、文本控制器、MD5加密、路由导航;
  3. 小白重点:先记住 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,
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐