flutter login UI 登录界面实战

3,963 阅读8分钟

flutter login UI 登录界面实战

 终于开始进入flutter实战环节了,登录界面是很多软件和app的基本界面,我们就从登录界面开始学习编写。我是边学编写,欢迎各路大神指教。

寻找登录界面模板

 首先找一个好看的模板吧,毕竟自己不是学习设计的,就先从网络上找一个美观的login UI 开始照猫画虎吧。找了半天,最终决定用下边这个ui当做模板来实现,即简介又美观。

1.png

 UI来自“设计之家”

开始实现

白色底板

 模板背景是纯白色的,这个最好实现了,我们先创建一个新的login项目并运行起来。

2.png

 上图是默认模板,我们现在对他进行改造。首先将所有注释和代码去掉,只留一个框架:

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(
    );
  }
}

3.png

 空白背景就完成了,接下来我们在MyAppbody()下继续进行界面绘制。

左右拆分空白背景

 接下来实现左侧这个小人吧,想一想用什么方法实现呢?直接把这个小人从模板扣下来,然后当做背景图片贴上去得了。。。大佬请勿嘲笑。那就先抠图吧。

4.png

 图片扣好了,把他当做背景图片放上去吧。后来想了一想,这样好像有问题,换一种方法吧。将空白背景一分为二,左侧放单独的一个图片,右侧放登录组件。我们使用水平布局组件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/

5.png

 接下来实现左侧布局,新建 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"), //小人图片
        ),
      )),
    );
  }
}

 效果:

6.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(), //文本显示
      ]),
    );
  }
}

 效果图如下,看起来丑爆了,接下来由上至下依次进行美化:

7.png

文本框美化

 首先进行文本美化。模板的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, //字体加粗
          ),
        ));
    
  }
}

 效果:

8.png

 接着对小字部分进行处理,也就是副标题,模板副标题没什么特别需要设置的。

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, //字体大小
          ),
        ));
  }
}

 效果:

9.png

输入框美化

 利用 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)),
            ),
          ),
        ),
      ]),
    );
  }
}

 效果:

10.png

按钮美化

 利用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"), //显示的文字
          ),
        ));
  }
}

 效果:

11.png

圆形头像

 圆形图像使用的频率还是很高的,因为是两个图片,这里我们外层先使用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",
                ),
              ),
            ),
          ],
        )); 
  }
}

 效果:

12.png

总结

 模板:

1.png

 自己的赝品:

13.png

 到目前为止,基本上还原了模板的样子,所有代码加起来有267行。差距肯定是有的,自己的看起来就没那么顺眼,并且很多细节也没处理好,左边的小人图片和右边中间有明显的明暗变化。最重要的是,现在实现的只是一个花架子,并不能实现点击跳转等功能,因为我还没学会。。希望自己在后边的学习中能不断美化自己的作品。