Flutter 开发一个登录页面

业务逻辑

为了演示登录跳转,在分类浏览先做了一个简单的按钮,点击跳转到登录页面。实际的 App 中,通常会是触发某些需要登录才能查看的操作后再跳转到登录界面。

布局分析

界面如上图所示,从界面上看,整体内容区域是居中的,内容的布局是一个简单的列式布局,包括了顶部的一个 Logo(通常是 App图标),再往下是两个文本输入框,最后是登录按钮。整体布局比较简单,使用 Center 下嵌一个Column 进行列布局即可。

图片圆形裁剪

在 Flutter 中实行图片圆形裁剪有两个方式,一是使用外层的容器,通过将正方形的按圆形裁剪即可;二是使用内置的 CircleAvatar。不过从名字上看 CircleAvatar 用于头像的,因此这里使用容器的来实现圆形裁剪。封装一个获取圆形图片的方法_getRoundImage,传入图片资源名称和正方形边长,代码如下所示:

Widget _getRoundImage(String imageName, double size) {
    return Container(
      width: size,
      height: size,
      clipBehavior: Clip.antiAlias,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(size / 2)),
      ),
      child: Image.asset(
        imageName,
        fit: BoxFit.fitWidth,
      ),
    );
  }

这里使用了 BoxDecoration 将边框设置为圆形的边框,半径为边长的一半,这样就达到边框是圆形的效果了。但是,需要额外设置一个属性就是 clipBehavior,这是边缘裁剪类型,默认是不裁剪的。这里使用了 Clip.antiAlias(抗锯齿)的方式进行裁剪,这种方式的裁剪效果最好,但是更耗资源,其他的裁剪方式如下:

  • Clip.hardEdge:从名字就知道,这种方式很粗糙,但是裁剪的效率最快;
  • Clip.antiAliasSaveLayer:最为精细的裁剪,但是非常慢,不建议使用;
  • Clip.none:默认值,如果内容区没有超出容器边界的话,不会做任何裁剪。内容超出边界的话需要使用别的裁剪方式防止内容溢出。

圆形扁平按钮

这里需要提一下, Flutter 2.0以前的扁平按钮是FlatButton,使用起来很简单,但是很多场合不太满足,因此2.0以后引入了 TextButton 替代。TextButton 多了一个 style来装饰按钮样式。具体可以看官方的文档。这里我们的按钮需要设置背景色为主题色,然后按钮文字颜色为白色,同时需要切成圆角,因此还是使用 Container 的边界圆弧来实现。需要注意的是,默认按钮的宽度是根据内容来的,因此为了让按钮撑满屏幕,我们设置了 Container 的宽度为 double.infinity。代码如下所示:

Widget _getLoginButton() {
    return Container(
      height: 50,
      width: double.infinity,
      margin: EdgeInsets.all(10),
      decoration: BoxDecoration(
        color: Theme.of(context).primaryColor,
        borderRadius: BorderRadius.circular(4.0),
      ),
      child: TextButton(
        style: ButtonStyle(
          foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
          backgroundColor:
              MaterialStateProperty.all<Color>(Theme.of(context).primaryColor),
        ),
        child: Text(
          '登录',
        ),
        onPressed: () {
          print(
              'Login: username=${_username.trim()}, password=${_password.trim()}');
        },
      ),
    );
  }

按钮点击回调事件为 onPressed,这里只是简单地打印了表单的内容。

TextField 文本框

TextField 是 Flutter 提供的文本输入框,TextField 的属性非常多,常用的属性如下:

  • keyboardType:键盘类型,可以指定是数字、字母、电话号码、邮箱、日期等多种方式,通过与表单内容匹配的键盘类型可以提供输入效率,进而改善用户体验。
  • controller:TextEditingController 对象,TextEditingController 主要用于控制文本框的初始值,清除内容的操作。
  • obscureText:是否需要隐藏输入内容,如果为 true,则输入内容会使用圆点显示,通常用与密码。
  • decoration:文本框的装饰,属性也很多,可以指定前置图标,边框类型、后置组件等多种属性,因此可以通过 decoration 获得想要的文本框样式。
  • focusNode:聚焦点,可以通过这个来控制文本框是否获取焦点,从而实现类似上一个下一个的输入控制。
  • onChanged:输入值改变事件回调,通常用这个方法实现双向绑定。

在这个案例中,我们使用了一个前置图标用来表示输入内容的类型,比如使用手机图标代表输入手机号,使用锁代表代表密码。同时使用了一个 Offstage作为后置的组件,用于在输入内容后可以点击清除内容。Offstage 组件是通过一个属性offstage来控制组件是否显示,这样我们可以在没有内容的时候隐藏它,有输入内容的时候再显示。

为了提高代码复用性,使用了一个方法获取通用的文本框,这里主要是使用了 Container包裹以控制边距和文本框下的分隔线:

Widget _getInputTextField(
    TextInputType keyboardType, {
    FocusNode focusNode,
    controller: TextEditingController,
    onChanged: Function,
    InputDecoration decoration,
    bool obscureText = false,
    height = 50.0,
  }) {
    return Container(
      height: height,
      margin: EdgeInsets.all(10.0),
      child: Column(
        children: [
          TextField(
            keyboardType: keyboardType,
            focusNode: focusNode,
            obscureText: obscureText,
            controller: controller,
            decoration: decoration,
            onChanged: onChanged,
          ),
          Divider(
            height: 1.0,
            color: Colors.grey[400],
          ),
        ],
      ),
    );
  }

完整代码

class _LoginPageState extends State<LoginPage> {
  //TextEditingController可以使用 text 属性指定初始值
  TextEditingController _usernameController = TextEditingController();
  TextEditingController _passwordController = TextEditingController();
  String _username = '', _password = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('登录'),
        brightness: Brightness.dark,
      ),
      body: Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _getRoundImage('images/logo.png', 100.0),
            SizedBox(
              height: 60,
            ),
            _getUsernameInput(),
            _getPasswordInput(),
            SizedBox(
              height: 10,
            ),
            _getLoginButton(),
          ],
        ),
      ),
    );
  }

  Widget _getUsernameInput() {
    return _getInputTextField(
      TextInputType.number,
      controller: _usernameController,
      decoration: InputDecoration(
        hintText: "输入手机号",
        icon: Icon(
          Icons.mobile_friendly_rounded,
          size: 20.0,
        ),
        border: InputBorder.none,
        //使用 GestureDetector 实现手势识别
        suffixIcon: GestureDetector(
          child: Offstage(
            child: Icon(Icons.clear),
            offstage: _username == '',
          ),
          //点击清除文本框内容
          onTap: () {
            this.setState(() {
              _username = '';
              _usernameController.clear();
            });
          },
        ),
      ),
      //使用 onChanged 完成双向绑定
      onChanged: (value) {
        this.setState(() {
          _username = value;
        });
      },
    );
  }

  Widget _getPasswordInput() {
    return _getInputTextField(
      TextInputType.text,
      obscureText: true,
      controller: _passwordController,
      decoration: InputDecoration(
        hintText: "输入密码",
        icon: Icon(
          Icons.lock_open,
          size: 20.0,
        ),
        suffixIcon: GestureDetector(
          child: Offstage(
            child: Icon(Icons.clear),
            offstage: _password == '',
          ),
          onTap: () {
            this.setState(() {
              _password = '';
              _passwordController.clear();
            });
          },
        ),
        border: InputBorder.none,
      ),
      onChanged: (value) {
        this.setState(() {
          _password = value;
        });
      },
    );
  }

  //省略了上述列举的代码

}

页面跳转

在上层面的登录按钮上,我们增加了一个点击事件,点击后再跳到登录页,按钮的响应代码如下所示。这是页面跳转的最简单的方式,使用 Navigator 导航器的 push方法实现页面跳转,后续会介绍如何通过路由实现页面跳转,那种方式更为优雅。

//...
onPressed: () {
  Navigator.of(context).push(
    MaterialPageRoute(builder: (context) => LoginPage()),
  );
},
//...

总结

从代码上看,功能虽然实现了,但是构建用户名和密码的代码十分相似,有没有办法进一步提高代码复用率,构建一个更为通用的表单组件呢?下篇我们将介绍如何来封装。

以上就是Flutter 开发一个登录页面的详细内容,更多关于Flutter 开发登录页面的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flutter实现页面切换后保持原页面状态的3种方法

    前言: 在Flutter应用中,导航栏切换页面后默认情况下会丢失原页面状态,即每次进入页面时都会重新初始化状态,如果在initState中打印日志,会发现每次进入时都会输出,显然这样增加了额外的开销,并且带来了不好的用户体验. 在正文之前,先看一些常见的App导航,以喜马拉雅FM为例: 它拥有一个固定的底部导航以及首页的顶部导航,可以看到不管是点击底部导航切换页面还是在首页左右侧滑切换页面,之前的页面状态都是始终维持的,下面就具体介绍下如何在flutter中实现类似喜马拉雅的导航效果 第一步:实

  • flutter编写精美的登录页面

    本文实例为大家分享了flutter编写精美的登录页面的具体代码,供大家参考,具体内容如下 先看效果图; 源代码已上传到github 我们先看一下页面 , 首先这个页面,我们并没有用到AppBar,当然也就没有自带返回功能. 然后下面有个Login的文字以及一条横线. 屏幕中上方是填写帐号以及密码的2个输入框,密码输入框有隐藏和显示密码的按钮. 下方是登录按钮 以及其他登录方式. 看一下主体布局: return Scaffold( body: Form( key: _formKey, child:

  • Flutter 使用Navigator进行局部跳转页面的方法

    Navigator组件使用的频率不是很高,但在一些场景下非常适用,比如局部表单多页填写.底部导航一直存在,每个tab各自导航场景. Navigator 是管理路由的控件,通常情况下直接使用Navigator.of(context)的方法来跳转页面,之所以可以直接使用Navigator.of(context)是因为在WidgetsApp中使用了此控件,应用程序的根控件通常是MaterialApp,MaterialApp包含WidgetsApp,所以可以直接使用Navigator的相关属性. Nav

  • Flutter实现用视频背景的登录页的示例代码

    最终效果 项目地址 https://github.com/Tecode/flutter_widget 实现方法 安装插件 安装video_player,我安装的是最新的版本,请根据你自己的flutter版本去安装对应的版本,安卓可以直接使用虚拟机,IOS需要真机才可以播放. dev_dependencies: flutter_test: sdk: flutter video_player: ^0.10.1+6 我的Flutter版本 Flutter 1.7.8+hotfix.4 • channe

  • Flutter页面传值的几种方式

    今天来聊聊Flutter页面传值的几种方式: InheritWidget Notification Eventbus (当前Flutter版本:2.0.4) InheritWidget 如果看过Provider的源码的同学都知道,Provider跨组件传值的原理就是根据系统提供的InheritWidget实现的,让我们来看一下这个组件. InheritWidget是一个抽象类,我们写一个保存用户信息的类UserInfoInheritWidget继承于InheritWidget: class Us

  • Flutter 构建一个常用的页面框架

    最终实现的结果如上图所示,顶部共用一个导航栏,底部有四个图标导航,点击对应的图标跳转到对应的页面. 图标准备 本次例程需要4个图标,2种颜色,可以从 iconfont 中找到自己需要的图标下载不同的颜色使用.然后在 pubspec.yaml 中的 assets 指定素材所在目录.需要注意的是如果是 png 文件直接指定整个目录即可,但如果是 jpg 格式,则需要同时指定文件名及后缀. BottomNavigationBar 简介 BottomNavigationBar的构造函数如下: Botto

  • Flutter 开发一个登录页面

    业务逻辑 为了演示登录跳转,在分类浏览先做了一个简单的按钮,点击跳转到登录页面.实际的 App 中,通常会是触发某些需要登录才能查看的操作后再跳转到登录界面. 布局分析 界面如上图所示,从界面上看,整体内容区域是居中的,内容的布局是一个简单的列式布局,包括了顶部的一个 Logo(通常是 App图标),再往下是两个文本输入框,最后是登录按钮.整体布局比较简单,使用 Center 下嵌一个Column 进行列布局即可. 图片圆形裁剪 在 Flutter 中实行图片圆形裁剪有两个方式,一是使用外层的容

  • IOS开发用户登录注册模块所遇到的问题

    最近和另外一位同事负责公司登录和用户中心模块的开发工作,开发周期计划两周,减去和产品和接口的协调时间,再减去由于原型图和接口的问题,导致强迫症纠结症状高发,情绪不稳定耗费的时间,能在两周基本完成也算是个不小的奇迹了.本文就总结一下如何满足产品需要的情况下,高效开发一个登录注册模块. 1.利用继承解决界面重复性功能.通常登录注册会有一个独立的设计,而模块内部会有有相似的背景,相似的导航栏样式,相似返回和退出行为,相似的输入框,按钮样式等. 比如上面的的注册和登录模块,就有相同的返回按钮,相同的背景

  • 使用Flutter开发的抖音国际版实例代码详解

    简介 最近花了两天时间研究使用Flutter开发一个抖音国际版. 个人感觉使用Flutter开发app快得不要不要的额. 两天就基本可以开发个大概出来. 最主要是热重载,太方便实时调整UI布局了. 相应速度极快. 如下图: 主要项目架构 详细说明一下,开发主要在lib文件夹 pubspec.yaml是配置插件的位置,如http: ^0.12.0+4,类似依赖组件. common文件夹存放的是重写的网络组件,以及图标组件icons.dart config文件夹存放的api.dart,wei调用的a

  • 微信小程序实现登录页面

    本文实例为大家分享了微信小程序实现登录页面的具体代码,供大家参考,具体内容如下 实现在进入微信小程序首页前的登录验证页面,这里有两种方法,但其实原理都是一样的. 1. 在首页中加入一个弹窗作为登录窗口,效果如下图: (1)index.wxml 登录窗口代码如下: <view class="modal-mask" bindtap="hideModal" catchtouchmove="preventTouchMove" wx:if="

  • 使用Vue.js和Element-UI做一个简单登录页面的实例

    最近了解到Vue.js挺火的,有同学已经学习了,那我心里痒痒的也学习了一点,然后也学了一点Element组件,就做了简单的登录页面. 效果很简单: 代码如下: 前端页面 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="stylesheet" hr

  • Vue.js实现一个SPA登录页面的过程【推荐】

    技术栈 vue.js 主框架 vuex 状态管理 vue-router 路由管理 一般过程 在一般的登录过程中,一种前端方案是: 检查状态:进入页面时或者路由变化时检查是否有登录状态(保存在cookie或者本地存储的值): 如果有登录态则查询登录信息(uid,头像等...)并保存起来:如果没有则跳转到登录页: 在登录页面(或者登录框),校检用户输入信息是否合法: 校检通过后发送登录请求:校检不成功则反馈给用户: 登录成功则从后端数据中取出session信息保存登录状态(可能需要跳转);登录不成功

  • flutter开发技巧自定页面指示器PageIndicator详解

    目录 一.来源 二.效果 三.源码实现 1.flutter_swiper_null_safety 使用示例: 2.PageIndicatorWidget 指示器源码: 三.总结 一.来源 项目中遇到多个需要自定义轮播图指示器的需求,封装成基础组件方便复用: 原理是通过 ValueListenableBuilder 实时监听轮播图的当前索引,然后更新指示器组件,达到最终效果: 二.效果 三.源码实现 1.flutter_swiper_null_safety 使用示例: import 'packag

  • CI框架开发新浪微博登录接口源码完整版

    首先来看下流程:流程原理:     1.通过code获得access_token通过授权,并获取用户的信息(包括用户u_id)(这个u_id在后面的第三方登录表里面叫sina_id,那个表是需要自己建的)     2.查询第三方登录表,如果不存在用户sina_id,分2种情况,一:用户在平台已经有帐号,这时需要把平台(比如:平台的用户表是:user_reg)用户id绑定到第三方登录表(比如是:third_login表),然后就让客户登录;                             

随机推荐