用 Flutter 写一个精美的登录页面(最新版)

  • 主体结构
  • 标题
  • 输入框
  • 登录按钮
  • 其他登录方式
  • 注册按钮
  • 完整源码

参考了博客:用flutter写一个精美的登录页面。但是那篇文章是 18 年的,较多 API 已经更新,本篇博文在其基础上使用最新版本 Dart 和 Flutter 开发。

完整源码在文章最后,有需要可以直接看源码。

效果图:

主体结构

该页面的主体布局如下:

  • 整体基于一个 Scaffold,没有 AppBar
  • 由于要对输入框进行表单校验,使用了 Form
  • 使用 ListView 作为外层控件
return Scaffold(body: Form(key: _formKey,autovalidateMode: AutovalidateMode.onUserInteraction,child: ListView(padding: const EdgeInsets.symmetric(horizontal: 20),children: [const SizedBox(height: kToolbarHeight), // 距离顶部一个工具栏的高度buildTitle(), // LoginbuildTitleLine(), // 标题下面的下滑线const SizedBox(height: 50),buildEmailTextField(), // 输入邮箱const SizedBox(height: 30),buildPasswordTextField(context), // 输入密码buildForgetPasswordText(context), // 忘记密码const SizedBox(height: 50),buildLoginButton(context), // 登录按钮const SizedBox(height: 30),buildOtherLoginText(), // 其他账号登录buildOtherMethod(context), // 其他登录方式buildRegisterText(context), // 注册],),),
);

标题

大标题 Titile 比较简单,就是设置了 边距 和 文字大小:

  • Padding 组件:设置边距
  • Text 组件:显示文本及控制样式
Widget buildTitle() {return const Padding( // 设置边距padding: EdgeInsets.all(8),child: Text('Login',style: TextStyle(fontSize: 42),));
}

Title 下面的下划线其实就是个设置了宽高的 Container

  • Align 组件:用于控制子组件的对齐方式
  • Container 组件:一个包含绘画、定位、大小的组件
Widget buildTitleLine() {return Padding(padding: const EdgeInsets.only(left: 12.0, top: 4.0),child: Align(alignment: Alignment.bottomLeft,child: Container(color: Colors.black,width: 40,height: 2,),));
}

输入框

邮箱输入框:使用正则对输入进行校验

  • TextFormField 组件:用于 Form 中的文本输入框
  • validator 属性用于对输入框进行表单校验
  • onSave 属性当表单校验通过后执行一些操作

何时进行表单校验是 From 组件中的一个属性

Widget buildEmailTextField() {return TextFormField(decoration: const InputDecoration(labelText: 'Email Address'),validator: (v) {var emailReg = RegExp(r"[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?");if (!emailReg.hasMatch(v!)) {return '请输入正确的邮箱地址';}},onSaved: (v) => _email = v!,);
}

密码输入框:

  • obscureText 属性控制表单文字的显示与隐藏,我们使用自定义变量来实现点击按钮隐藏或显示密码功能
  • suffixIcon 属性是在输入框后面加一个图标,给它一个点击方法是改变是否显示密码,并更改图标的颜色
  • setState 用于通知 Flutter 框架重绘界面,此处我们利用变量 _isObscure 控制输入框密码的可见性,涉及到界面显示的更新,因此要更新 _isObscure 的代码放在 setState 中。
Widget buildPasswordTextField(BuildContext context) {return TextFormField(obscureText: _isObscure, // 是否显示文字onSaved: (v) => _password = v!,validator: (v) {if (v!.isEmpty) {return '请输入密码';}},decoration: InputDecoration(labelText: "Password",suffixIcon: IconButton(icon: Icon(Icons.remove_red_eye,color: _eyeColor,),onPressed: () {// 修改 state 内部变量, 且需要界面内容更新, 需要使用 setState()setState(() {_isObscure = !_isObscure;_eyeColor = (_isObscure? Colors.grey: Theme.of(context).iconTheme.color)!;});},)));
}

密码输入框下面还有一行 “忘记密码?” 的文本,利用 Align 组件将其靠右对齐

Widget buildForgetPasswordText(BuildContext context) {return Padding(padding: const EdgeInsets.only(top: 8),child: Align(alignment: Alignment.centerRight,child: TextButton(onPressed: () {// Navigator.pop(context);print("忘记密码");},child: const Text("忘记密码?",style: TextStyle(fontSize: 14, color: Colors.grey)),),),);
}

登录按钮

登录按钮:

  • ElevatedButton 组件是一个常见的,点击后有波纹效果的按钮
  • 点击登录按钮后,进行表单校验
Widget buildLoginButton(BuildContext context) {return Align(child: SizedBox(height: 45,width: 270,child: ElevatedButton(style: ButtonStyle(// 设置圆角shape: MaterialStateProperty.all(const StadiumBorder(side: BorderSide(style: BorderStyle.none)))),child: Text('Login',style: Theme.of(context).primaryTextTheme.headline5),onPressed: () {// 表单校验通过才会继续执行if ((_formKey.currentState as FormState).validate()) {(_formKey.currentState as FormState).save();//TODO 执行登录方法print('email: $_email, password: $_password');}},),),);
}

其他登录方式

其他登录方式:

  • ButtonBar 组件用于构建多个按钮的排列(方向可控)
  • map 是个高阶函数,可以对 数组的每个元素进行某种操作,最后再归约成数组
  • SnackBar 是一种从底部出现的轻量级弹窗
final List _loginMethod = [{"title": "facebook","icon": Icons.facebook,},{"title": "google","icon": Icons.fiber_dvr,},{"title": "twitter","icon": Icons.account_balance,},
];Widget buildOtherMethod(context) {return ButtonBar(alignment: MainAxisAlignment.center,children: _loginMethod.map((item) => Builder(builder: (context) {return IconButton(icon: Icon(item['icon'],color: Theme.of(context).iconTheme.color),onPressed: () {//TODO: 第三方登录方法ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('${item['title']}登录'),action: SnackBarAction(label: '取消',onPressed: () {},)),);});})).toList(),);
}

注册按钮

注册按钮:

  • GestureDetector 是手势检测器,用它包裹组件后可以实现对该组件的各种手势的监听,例如:“单击”、“双击”、“长按” 等。
Widget buildRegisterText(context) {return Center(child: Padding(padding: const EdgeInsets.only(top: 10),child: Row(mainAxisAlignment: MainAxisAlignment.center,children: [const Text('没有账号?'),GestureDetector(child: const Text('点击注册', style: TextStyle(color: Colors.green)),onTap: () {print("点击注册");},)],),),);
}

完整源码

新建一个 Flutter 项目,替换其中 main.dart 的内容,即可运行起来。

import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(debugShowCheckedModeBanner: false, // 不显示右上角的 debugtitle: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),// 注册路由表routes: {"/": (context) => const HomePage(title: "登录"), // 首页路由});}
}class HomePage extends StatefulWidget {const HomePage({Key? key, required this.title}) : super(key: key);final String title;@override_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {final GlobalKey _formKey = GlobalKey<FormState>();late String _email, _password;bool _isObscure = true;Color _eyeColor = Colors.grey;final List _loginMethod = [{"title": "facebook","icon": Icons.facebook,},{"title": "google","icon": Icons.fiber_dvr,},{"title": "twitter","icon": Icons.account_balance,},];@overrideWidget build(BuildContext context) {return Scaffold(body: Form(key: _formKey, // 设置globalKey,用于后面获取FormStatautovalidateMode: AutovalidateMode.onUserInteraction,child: ListView(padding: const EdgeInsets.symmetric(horizontal: 20),children: [const SizedBox(height: kToolbarHeight), // 距离顶部一个工具栏的高度buildTitle(), // LoginbuildTitleLine(), // Login下面的下划线const SizedBox(height: 60),buildEmailTextField(), // 输入邮箱const SizedBox(height: 30),buildPasswordTextField(context), // 输入密码buildForgetPasswordText(context), // 忘记密码const SizedBox(height: 60),buildLoginButton(context), // 登录按钮const SizedBox(height: 40),buildOtherLoginText(), // 其他账号登录buildOtherMethod(context), // 其他登录方式buildRegisterText(context), // 注册],),),);}Widget buildRegisterText(context) {return Center(child: Padding(padding: const EdgeInsets.only(top: 10),child: Row(mainAxisAlignment: MainAxisAlignment.center,children: [const Text('没有账号?'),GestureDetector(child: const Text('点击注册', style: TextStyle(color: Colors.green)),onTap: () {print("点击注册");},)],),),);}Widget buildOtherMethod(context) {return ButtonBar(alignment: MainAxisAlignment.center,children: _loginMethod.map((item) => Builder(builder: (context) {return IconButton(icon: Icon(item['icon'],color: Theme.of(context).iconTheme.color),onPressed: () {//TODO: 第三方登录方法ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('${item['title']}登录'),action: SnackBarAction(label: '取消',onPressed: () {},)),);});})).toList(),);}Widget buildOtherLoginText() {return const Center(child: Text('其他账号登录',style: TextStyle(color: Colors.grey, fontSize: 14),),);}Widget buildLoginButton(BuildContext context) {return Align(child: SizedBox(height: 45,width: 270,child: ElevatedButton(style: ButtonStyle(// 设置圆角shape: MaterialStateProperty.all(const StadiumBorder(side: BorderSide(style: BorderStyle.none)))),child: Text('Login',style: Theme.of(context).primaryTextTheme.headline5),onPressed: () {// 表单校验通过才会继续执行if ((_formKey.currentState as FormState).validate()) {(_formKey.currentState as FormState).save();//TODO 执行登录方法print('email: $_email, password: $_password');}},),),);}Widget buildForgetPasswordText(BuildContext context) {return Padding(padding: const EdgeInsets.only(top: 8),child: Align(alignment: Alignment.centerRight,child: TextButton(onPressed: () {// Navigator.pop(context);print("忘记密码");},child: const Text("忘记密码?",style: TextStyle(fontSize: 14, color: Colors.grey)),),),);}Widget buildPasswordTextField(BuildContext context) {return TextFormField(obscureText: _isObscure, // 是否显示文字onSaved: (v) => _password = v!,validator: (v) {if (v!.isEmpty) {return '请输入密码';}},decoration: InputDecoration(labelText: "Password",suffixIcon: IconButton(icon: Icon(Icons.remove_red_eye,color: _eyeColor,),onPressed: () {// 修改 state 内部变量, 且需要界面内容更新, 需要使用 setState()setState(() {_isObscure = !_isObscure;_eyeColor = (_isObscure? Colors.grey: Theme.of(context).iconTheme.color)!;});},)));}Widget buildEmailTextField() {return TextFormField(decoration: const InputDecoration(labelText: 'Email Address'),validator: (v) {var emailReg = RegExp(r"[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?");if (!emailReg.hasMatch(v!)) {return '请输入正确的邮箱地址';}},onSaved: (v) => _email = v!,);}Widget buildTitleLine() {return Padding(padding: const EdgeInsets.only(left: 12.0, top: 4.0),child: Align(alignment: Alignment.bottomLeft,child: Container(color: Colors.black,width: 40,height: 2,),));}Widget buildTitle() {return const Padding(padding: EdgeInsets.all(8),child: Text('Login',style: TextStyle(fontSize: 42),));}
}

用 Flutter 写一个精美的登录页面(最新版)相关推荐

  1. 写一个简单的登录页面!!!(html)

    写一个简单的登录页面!!! 大家好,我是伍柒. 今天我又双叒叕发现了一个好东西. 那就是用html做一个登录页面 那,该如何做呢? 首先 现在html里的body里写 一个头部header标签(双标签 ...

  2. html 写一个注册和登录页面

    先写 注册页面 <!DOCTYPE html> <html lang="en"> <head><meta charset="UT ...

  3. 用html写一个QQ空间登录页面

    这是一个基本的 QQ 空间登录页面的 HTML 代码: <!DOCTYPE html> <html> <head><meta charset="ut ...

  4. 用python写个华丽的登录页面

    好的,我会用 Python 写一个华丽的登录页面. 首先,你需要准备一个能够用来输入用户名和密码的表单.你可以使用 Python 的 GUI 库来创建这个表单.推荐使用 Tkinter 库,它是 Py ...

  5. 小白教程——Windows下用PHP写一个简单的登录注册页面(二)

    哈喽,看到这里希望小伙伴们都把wampserver环境安装好了,如果还没有安装或创建数据表就移步去看我上一篇文章吧.OK~接下我们将进入代码实现部分,首先我们需要一个文本编辑器,可以是电脑自带的not ...

  6. 利用Flutter写一个跨平台的果核APP(4)——数据存储

    前言 目前我们已经实现了几个界面,今天这篇文章开始着手进行登录页的制作,主要流程就是获取输入框中的内容,发送给后台进行验证,如果成功将返回信息保存在本地并跳转至首页,如果失败就提示用户重新输入. 在这 ...

  7. 实现一个简单的登录页面

    实现一个简单的登录页面 设计了一个登录页面,感觉还挺不错的 实现效果 设计的还是挺好看的吧 分析需要的功能 一个登录页面一个注册页面 翻转效果 输入后的正则判断,给用户提示信息 翻转要清空页面的全部信 ...

  8. [html] 写一个布局,当页面滚动一定高时,导航始终固定在顶部,反之恢复原位

    [html] 写一个布局,当页面滚动一定高时,导航始终固定在顶部,反之恢复原位 使用粘性定位,position:sticky 记得使用的时候父元素不能使用overflow:hidden和overflo ...

  9. [html] 写一个布局,当页面滚动一定高时,导航始终固定在顶部,反之恢复原位

    [html] 写一个布局,当页面滚动一定高时,导航始终固定在顶部,反之恢复原位 使用粘性定位,position:sticky 记得使用的时候父元素不能使用overflow:hidden和overflo ...

最新文章

  1. java 矩阵计算 加减乘除 反转 分解
  2. Bootstrap4+MySQL前后端综合实训-Day01-PM【position定位的四种方式、Flex布局语法教程及案例(概念、容器属性、项目属性)、双飞翼布局复习、Bootstrap4 教程】
  3. echarts中x轴文字过长换行处理和倾斜处理。
  4. docker安装gamit_科学网—Ubuntu系统GAMIT/GLOBK程序安装 - 陈超的博文
  5. ceph 代码分析 读_五大常见存储系统PK | Ceph、GlusterFS、MooseFS、HDFS、DRBD
  6. window7 64位 myeclipse9.0破解步骤
  7. 十三、this关键字
  8. 【java毕业设计】基于javaEE+Mybatis的WEB仓库管理系统设计与实现(毕业论文+程序源码)——仓库管理系统
  9. sqlserver用sql语句备份数据库
  10. std::adjacent_find 用法
  11. 6sp电池测试软件,小白必看!iPhone 6sp电池更换记录,续航半天变成2天,给力!...
  12. SQL Prompt5 破解版+使用说明
  13. 堆栈内存的分析以及作用域的详解
  14. python读取配置文件-python 配置文件读写
  15. git pull origin master与git pull --rebase origin master的区别
  16. Centos查看端口占用
  17. Java实现矩阵的乘法
  18. 专访羽顺热能董事长陈群:荣誉就是不懈的努力
  19. unionid openid微信php,openid与unionid
  20. 解决M1芯片版本安装Sketch问题 M1芯片安装那个Sketch版本?Sketch已完美支持M1芯片安装 支持big sur系统

热门文章

  1. 2020年全球亿万富豪的财富增加了1.9万亿美元
  2. 最近为A公司提炼的经营理念之合作理念
  3. 中国程序员最应该感谢的几家公司
  4. 马云还曾有过这么一段求职经历
  5. Spring如何优雅地发送异步发送通知?
  6. Java的几种路径总结
  7. ssrs 生成pdf_在SSRS报告中生成热图的可用选项
  8. aws rds监控慢sql_探索AWS RDS SQL Server上SQL Server集成服务(SSIS)
  9. 了解SQL Server中NOLOCK和WITH NOLOCK表提示的影响
  10. Chemical table CFR500 div2D(并查集)