flutter login UI 登录界面实战
终于开始进入flutter实战环节了,登录界面是很多软件和app的基本界面,我们就从登录界面开始学习编写。我是边学编写,欢迎各路大神指教。
寻找登录界面模板
首先找一个好看的模板吧,毕竟自己不是学习设计的,就先从网络上找一个美观的login UI 开始照猫画虎吧。找了半天,最终决定用下边这个ui当做模板来实现,即简介又美观。
UI来自“设计之家”
开始实现
白色底板
模板背景是纯白色的,这个最好实现了,我们先创建一个新的login项目并运行起来。
上图是默认模板,我们现在对他进行改造。首先将所有注释和代码去掉,只留一个框架:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,//去掉右上角debug标识
theme: ThemeData(//主题设置
primarySwatch: Colors.blue,
),
home: const SelectionArea(//子组件支持文字选定 3.3新特性
child: Scaffold(//子组件
body: MyAppbody(),
),
),
);
}
}
class MyAppbody extends StatelessWidget {
const MyAppbody({super.key});
@override
Widget build(BuildContext context) {
return Center(
);
}
}
空白背景就完成了,接下来我们在MyAppbody()下继续进行界面绘制。
左右拆分空白背景
接下来实现左侧这个小人吧,想一想用什么方法实现呢?直接把这个小人从模板扣下来,然后当做背景图片贴上去得了。。。大佬请勿嘲笑。那就先抠图吧。
图片扣好了,把他当做背景图片放上去吧。后来想了一想,这样好像有问题,换一种方法吧。将空白背景一分为二,左侧放单独的一个图片,右侧放登录组件。我们使用水平布局组件Row()
在Row()组件下,我们结合Expanded组件实现空间填充,Expanded组件动态调整child组件沿主轴的尺寸,进行填充剩余空间,设置尺寸比例。也可以和Column组合起来使用。
class MyAppbody extends StatelessWidget {
const MyAppbody({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Row(//水平布局
children: [//子组件
Expanded(//空间占比组件
flex: 1,//左侧空间占比
child: Leftlist(),//左侧布局
),
Expanded(
flex: 4,//右侧空间占比
child: Rightbox(),//右侧布局
),
],
),
);
}
}
左侧组件实现
要想添加图片到代码中,需要对pubspec.yaml文件进行设置,添加下列代码,同时新建images文件夹,将你的图片放进去,这样就可以在代码中添加图片了:
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- images/
接下来实现左侧布局,新建 Leftlist(),外边用Padding组件包裹,实现四周填充,接下来使用Container包裹BoxDecoration、DecorationImage实现组件圆角和图片显示。
Container的decoration可以设置设置边框、背景色、背景图片、圆角等属性,Decoration共有以下几种:
1.BoxDecoration:实现边框、圆角、阴影、形状、渐变、背景图像;
2.ShapeDecoration: 颜色,阴影,图片;
3.FlutterLogoDecoration: Logo图片;
4.UnderlineTabindicator:下划线;
DecorationImage组件可以加载本地图片。实现图片展示,fit参数可以设置图片显示模式:
1.fill:设置源填充目标框。它可能会扭曲源的纵横比。
2.contain:在目标框内将源设置为尽可能大。
3.cover:将源设置为尽可能小,同时仍覆盖整个目标框。
4.fitWidth: 设置源的宽度以匹配目标框的宽度。它可能会导致源垂直溢出目标框。
5.fitHeight: 设置源的高度以匹配目标框的宽度。它可能会导致源水平溢出目标框。
6.none: 对齐目标框内的源并丢弃框外的任何部分。
7.scaleDown:在目标框内对齐源并在必要时缩小源以适合目标框。
class Leftlist extends StatelessWidget {
const Leftlist({super.key});
@override
Widget build(BuildContext context) {
return Padding(
//外围填充
padding: const EdgeInsets.fromLTRB(20, 20, 0,20), //四周填充10
child: Container(
decoration: const BoxDecoration(//BoxDecoration:实现边框、圆角、阴影、形状、渐变、背景图像
borderRadius: BorderRadius.all(Radius.circular(8.0)),//四个角的圆角度数
image: DecorationImage(//加载本地图片
fit: BoxFit.cover,
image: AssetImage("images/1.png"), //小人图片
),
)),
);
}
}
效果:
右侧组件实现
接下来实现右侧布局,新建空白的右侧组件Rightbox():
class Rightbox extends StatelessWidget {
const Rightbox({super.key});
@override
Widget build(BuildContext context) {
return Padding(
//外围填充
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), //四周填充10
child: Container(
),
);
}
}
思路大概是这样,在右侧最外层放置一个竖着的布局,利用Column组件实现,然后从上到下依次布局文字、文本框、按钮等。我们依旧结合Expanded组件实现填充,使这些组件能填充满整个右侧空间。
class Rightbox extends StatelessWidget {
const Rightbox({super.key});
@override
Widget build(BuildContext context) {
return Padding(
//外围填充
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), //四周填充10
child: Column(//垂直布局
children: const <Widget>[
Textbody(), //文本显示
Textbody2(), //文本显示
Textinput(), //输入框
Loginbutton(), //按钮
ClipOvalimage(), //圆形图片
Textbody3(), //文本显示
]),
);
}
}
效果图如下,看起来丑爆了,接下来由上至下依次进行美化:
文本框美化
首先进行文本美化。模板的Log in 是加大加粗黑体的,咱们也模仿这个样子实现。
class Textbody extends StatelessWidget {
const Textbody({super.key});
@override
Widget build(BuildContext context) {
return const Padding(//外围填充
padding: EdgeInsets.fromLTRB(0, 100, 0, 0), //上边部分填充
child: Text(
'Log in',
style: TextStyle(
fontSize: 25,//字体大小
fontWeight: FontWeight.bold, //字体加粗
),
));
}
}
效果:
接着对小字部分进行处理,也就是副标题,模板副标题没什么特别需要设置的。
class Textbody2 extends StatelessWidget {
const Textbody2({super.key});
@override
Widget build(BuildContext context) {
return const Padding(
//外围填充
padding: EdgeInsets.fromLTRB(0, 10, 0, 0), //上边部分填充
child: Text(
'Hello,friend! Can you speak English?',
style: TextStyle(
fontSize: 12, //字体大小
),
));
}
}
效果:
输入框美化
利用 BorderRadius.circular设置输入框圆角,利用padding填充上下左右空间,用padding包裹两个输入框组件,可以同时设置两个输入框的长短。除此之外,还涉及到边框隐藏,背景颜色,密码输入时隐藏等诸多特性,大家可以结合代码和注释查看。
class Textinput extends StatelessWidget {
const Textinput({super.key});
@override
Widget build(BuildContext context) {
return Padding(
//外围填充
padding: const EdgeInsets.fromLTRB(200, 0, 200, 0), //前后填充,缩短框的长度
child: Column(//垂直布局
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(0, 20, 0, 0), //上边部分填充
child: TextField(
style:
const TextStyle(fontSize: 12, color: Colors.black87), //文字大小、颜色
//输入组件
decoration: InputDecoration(
fillColor: Colors.grey[80], //背景颜色,
filled: true, //必须设置为true,fillColor才有效
isCollapsed: true, //相当于高度包裹的意思,必须设置为true
contentPadding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 15), //内容内边距
hintText: "Email",
enabledBorder: OutlineInputBorder(
//设置输入框可编辑时的边框样式
borderRadius: BorderRadius.circular(20.0), //输入框圆角
borderSide:
const BorderSide(color: Colors.white, width: 0) //输入框白色隐藏
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20.0), //输入框圆角
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(0, 20, 0, 0), //上边部分填充
child: TextField(
obscureText: true, //密码隐藏
style:
const TextStyle(fontSize: 12, color: Colors.black87), //文字大小、颜色
//输入组件
decoration: InputDecoration(
fillColor: Colors.grey[80], //背景颜色,必须结合filled: true,才有效
filled: true, //必须设置为true,fillColor才有效
isCollapsed: true, //相当于高度包裹的意思,必须设置为true
contentPadding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 15), //内容内边距,影响高度
hintText: "Password",
enabledBorder: OutlineInputBorder(
//设置输入框可编辑时的边框样式
borderRadius: BorderRadius.circular(20.0), //输入框圆角
borderSide:
const BorderSide(color: Colors.white, width: 0) //输入框白色隐藏
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20.0), //输入框圆角
borderSide: const BorderSide(color: Colors.white, width: 0)),
),
),
),
]),
);
}
}
效果:
按钮美化
利用minWidth设置按钮的最小宽度,height设置按钮高度,使他与输入框一致,看起来会协调一些。使用 BorderRadius.circular设置按钮圆角。点击事件放在 onPressed中。通过 textColor设置“Let’s start”的文本颜色。
class Loginbutton extends StatelessWidget {
const Loginbutton({super.key});
@override
Widget build(BuildContext context) {
return Padding(
//外围填充
padding: const EdgeInsets.fromLTRB(0, 20, 0, 0), //四周填充10
child: SelectionContainer.disabled(
//文字不可选定,会影响按钮点击
child: MaterialButton(
color: const Color.fromARGB(255, 35, 102, 37), //按钮颜色
textColor: Colors.white, //文本的颜色
minWidth: 188, //最小的宽度 默认是 88
height: 40, // 高度, 默认是 36
onPressed: () {},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
child: const Text("Let’s start"), //显示的文字
),
));
}
}
效果:
圆形头像
圆形图像使用的频率还是很高的,因为是两个图片,这里我们外层先使用Row横向布局组件,接着用两个Padding组件包裹CircleAvatar实现圆形图片显示。通过radius设置图片大小,设置backgroundColor背景颜色为白色。
class ClipOvalimage extends StatelessWidget {
const ClipOvalimage({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(100, 10, 100, 0),
child: Row(//横向布局
children: const [
Padding(
padding: EdgeInsets.fromLTRB(178, 10, 0, 0),//填充
child: CircleAvatar(//图片圆形剪裁
radius: 10,//圆形直径,(半径)?
backgroundColor: Colors.white,//背景颜色设置为白色
backgroundImage: AssetImage(
"images/2.png",
),
),
),
Padding(
padding: EdgeInsets.fromLTRB(20, 10, 0, 0),
child: CircleAvatar(
radius: 13,
backgroundColor: Colors.white,//背景颜色设置为白色
backgroundImage: AssetImage(
"images/3.png",
),
),
),
],
));
}
}
效果:
总结
模板:
自己的赝品:
到目前为止,基本上还原了模板的样子,所有代码加起来有267行。差距肯定是有的,自己的看起来就没那么顺眼,并且很多细节也没处理好,左边的小人图片和右边中间有明显的明暗变化。最重要的是,现在实现的只是一个花架子,并不能实现点击跳转等功能,因为我还没学会。。希望自己在后边的学习中能不断美化自己的作品。