Flutter - 简单的登录UI

我与Flutter的故事已于6个月前开始,今天我终于决定开始在Flutter社区分享我的小指纹。
我将解释Login UI flutter代码的不同部分,但首先,让我告诉你它的样子!我们走吧!


初步设计

首先,我开始在Sketch上进行UI设计,遵循我唯一且永远的设计理念“保持简单!”。

注意:为了能够跟进本文,我希望您对Flutter拥有最低限度的专业知识。


代码

我们现在深入研究代码!
当然,我们从main.dart开始,声明主要方法和一些主题规则。

请注意,我们将DeviceOrientation设置为Portrait Up,因为我们不想处理设备方向改变时发生的混乱。

void main() {
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
      .then((_) {
    runApp(new MyApp());
  });
}

很简单。

现在让我们来看看MyApp类,我在这里声明了一些主题规则。

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
          hintColor: Color(0xFFC0F0E8),
          primaryColor: Color(0xFF80E1D1),
          canvasColor: Colors.transparent),
          fontFamily: "Montserrat",
      home: Home(),
    );
  }
}

下一步和主要步骤,home.dart包含我们的大部分代码。首先,我们编写Home StatefulWidget类并声明所有必要的变量以便稍后存储用户输入。

  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  TextEditingController _emailController = new TextEditingController();
  TextEditingController _passwordController = new TextEditingController();
  TextEditingController _nameController = new TextEditingController();
  String _email;
  String _password;
  String _displayName;
  bool _obsecure = false;

真正的工作从构建方法开始,但首先让我分解我们的UI以了解我们将如何编码它。

我们有3页:

  1. 包含Logo的主页面,2个用于登录的按钮和用于注册的按钮,以及一些绘制在底部的曲线。
  2. 登录页面,从主页面点击登录按钮后出现,它有4个组件:关闭图标,2个输入字段和一个提交按钮。
  3. 注册页面,是具有1个附加字段的登录页面的副本。

现在我们可以开始编写每个组件!

首先,我们有一个带有keybackgroundColorScaffold,我们将resizeToAvoidBottomPadding设置为false,因为当键盘越过它们时,它会在输入字段后面变得混乱。

return Scaffold(
        resizeToAvoidBottomPadding: false,
        key: _scaffoldKey,
        backgroundColor: Theme.of(context).primaryColor,
        body: Column());
  }

Column小部件中,我们将添加logo()小部件,以使用StackPositioned小部件绘制这个漂亮的简单徽标。Logo()将在构建方法之外,以使我们的代码更清晰。

    //GO logo widget
    Widget logo() {
      return Center(
          child: Padding(
        padding: EdgeInsets.only(top: 120),
        child: Container(
          width: MediaQuery.of(context).size.width,
          height: 240,
          child: Stack(
            children: <Widget>[
              Positioned(
                  child: Container(
                child: Align(
                  child: Container(
                    decoration: BoxDecoration(
                        shape: BoxShape.circle, color: Colors.white),
                    width: 150,
                    height: 150,
                  ),
                  alignment: Alignment.center,
                ),
                height: 154,
              )),
              Positioned(
                child: Container(
                    height: 154,
                    width: MediaQuery.of(context).size.width,
                    child: Align(
                      alignment: Alignment.center,
                      child: Text(
                        "GO",
                        style: TextStyle(
                          fontSize: 120,
                          fontWeight: FontWeight.bold,
                          color: Theme.of(context).primaryColor,
                        ),
                      ),
                    )),
              ),
              Positioned(
                width: 60,
                height: 60,
                top: 140,
                left: 260,
                child: Container(
                  decoration: BoxDecoration(
                      shape: BoxShape.circle, color: Colors.white),
                ),
              ),
              Positioned(
                width: 30,
                height: 30,
                top: 200,
                left: 230,
                child: Container(
                  decoration: BoxDecoration(
                      shape: BoxShape.circle, color: Colors.white),
                ),
              ),
            ],
          ),
        ),
      ));
    }

轮到按钮。我们有2个按钮,一个是RaisedButton,另一个是OutlineButton。为什么我们为每个按钮使用2种不同的小部件?
仅仅为了造型 😁
RaisedButton可以很容易地给它填充颜色,这是我们想要的登录按钮(带有白色填充)。OutlineButton使得有一个带边框但没有填充的按钮变得更容易,这就是我们想要的注册按钮。
我真的很擅长安排我的代码...所以这是我想到的最好的主意。我在代码中多次使用凸起按钮,但是大纲按钮只有一个,所以我认为最好将一个凸起按钮作为一个函数并给它以下参数以使其可重用:

Widget _button (String text, Color splashColor, Color highlightColor, Color fillColor, Color textColor, void function()){}

这么长的参数列表!但比多次编写相同的按钮代码要好得多!

这是我们的RaisedButton小部件的完整代码:


Widget filledButton(String text, Color splashColor, Color highlightColor,
        Color fillColor, Color textColor, void function()) {
      return RaisedButton(
        highlightElevation: 0.0,
        splashColor: splashColor,
        highlightColor: highlightColor,
        elevation: 0.0,
        color: fillColor,
        shape: RoundedRectangleBorder(
            borderRadius: new BorderRadius.circular(30.0)),
        child: Text(
          text,
          style: TextStyle(
              fontWeight: FontWeight.bold, color: textColor, fontSize: 20),
        ),
        onPressed: () {
          function();
        },
      );
    }

这是OutlineButton:

OutlineButton(
  highlightedBorderColor: Colors.white,
  borderSide: BorderSide(color: Colors.white, width: 2.0),
  highlightElevation: 0.0,
  splashColor: Colors.white,
  highlightColor: Theme.of(context).primaryColor,
  color: Theme.of(context).primaryColor,
    shape: RoundedRectangleBorder(
      borderRadius: new BorderRadius.circular(30.0),
     ),
   child: Text("REGISTER",style: TextStyle(
      fontWeight: FontWeight.bold,
      color: Colors.white,
      fontSize: 20),),
    onPressed: () { _registerSheet();
    },
),

主页的最后一部分是底部弯曲的白色形状,想知道如何做到这一点?我们将使用ClipPath小部件。
在我们的Scaffold体内的Column内部,我们将添加此代码以绘制弯曲的形状:

Expanded(
  child: Align(
    child: ClipPath(
      child: Container(
        color: Colors.white,
        height: 300,
        ),
      clipper: BottomWaveClipper(),
      ),
  alignment: Alignment.bottomCenter,
  ),
)

让我们转到一个名为clipper.dart的新文件,它将包含将绘制弯曲路径的类BottomWaveClipper

import 'package:flutter/material.dart';

class BottomWaveClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();
    path.moveTo(size.width, 0.0);
    path.lineTo(size.width, size.height);
    path.lineTo(0.0, size.height);
    path.lineTo(0.0, size.height + 5);
    var secondControlPoint = Offset(size.width - (size.width / 6), size.height);
    var secondEndPoint = Offset(size.width, 0.0);
    path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy,
        secondEndPoint.dx, secondEndPoint.dy);

    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

太神奇了!我们完成了主页面,让我们继续前进。


看起来登录页面和注册页面是应用程序中的新路由,但它们不是,每个都只是一个PersistentBottomSheet
PersistentBottomSheet是在主要上下文中从底部绘制到屏幕的底部工作表,我们可以在其中填充我们想要的任何内容。

让我们将以下函数添加到每个按钮的onPressed属性上(实际上我们将第一个函数传递给没有括号的_button函数):

_loginSheet
_registerSheet()

你猜他们是什么?嗯,确切地说!它们的功能将返回登录和注册表。每个代码都很长,我不会在这里全部包含它,但我们将看一下它的一些重要特性。

  1. 它的包裹里面DecoratedBox小部件,然后ClipRRect,给它顶部边角半径。
  2. 为了滚动,当键盘位于密码字段和提交按钮之上时,元素用ListView而不是Column包裹,使用户能够滚动来解决问题。
DecoratedBox(
  decoration: BoxDecoration(color: Theme.of(context).canvasColor),   
  child: 
ClipRRect(borderRadius: BorderRadius.only( 
     topLeft: Radius.circular(40.0), 
     topRight: Radius.circular(40.0)), 
     child: Container( child: 
ListView())
  )
)

同样适用于工作表,登录和注册。


现在是InputField,它将是一个名为_input的函数,它将返回所需的InputField小部件:

    //input widget
    Widget _input(Icon icon, String hint, TextEditingController controller, bool obsecure) {
      return Container(
        padding: EdgeInsets.only(left: 20, right: 20),
        child: TextField(
          controller: controller,
          obscureText: obsecure,
          style: TextStyle(fontSize: 20, ),
          decoration: InputDecoration(
              hintStyle: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
              hintText: hint,
              enabledBorder: OutlineInputBorder(
                borderRadius: BorderRadius.circular(30),
                borderSide: BorderSide(
                  color: Theme.of(context).primaryColor,
                  width: 2,
                ),
              ),
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(30),
                borderSide: BorderSide(
                  color: Theme.of(context).primaryColor,
                  width: 3,
                ),
              ),
              prefixIcon: Padding(
                child: IconTheme(
                  data: IconThemeData(color: Theme.of(context).primaryColor),
                  child: icon,
                ),
                padding: EdgeInsets.only(left: 30, right: 10),
              )),
        ),
      );
    }

因此,每次我们想要使用输入字段时,我们只需调用此函数并为其提供适合我们使用的参数。我们有5个输入字段,将从这个功能中提取,非常整洁和优雅。


1_dMlmvO2ENIRt0Nic8jHB6A.gif
1_SGCH-xPXb_8rfL4_eGBOCQ.gif

完成!

你可以在这里找到GitHub上的源代码。💙
转:https://medium.com/swlh/simple-clean-login-ui-using-flutter-43314d0dbdee

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容