Flutter - 簡(jiǎn)單的登錄UI

我與Flutter的故事已于6個(gè)月前開(kāi)始诚啃,今天我終于決定開(kāi)始在Flutter社區(qū)分享我的小指紋浆竭。
我將解釋Login UI flutter代碼的不同部分,但首先吧秕,讓我告訴你它的樣子琉闪!我們走吧!


初步設(shè)計(jì)

首先寇甸,我開(kāi)始在Sketch上進(jìn)行UI設(shè)計(jì)塘偎,遵循我唯一且永遠(yuǎn)的設(shè)計(jì)理念“保持簡(jiǎn)單!”拿霉。

注意:為了能夠跟進(jìn)本文吟秩,我希望您對(duì)Flutter擁有最低限度的專(zhuān)業(yè)知識(shí)。


代碼

我們現(xiàn)在深入研究代碼绽淘!
當(dāng)然涵防,我們從main.dart開(kāi)始,聲明主要方法和一些主題規(guī)則沪铭。

請(qǐng)注意壮池,我們將DeviceOrientation設(shè)置為Portrait Up,因?yàn)槲覀儾幌胩幚碓O(shè)備方向改變時(shí)發(fā)生的混亂杀怠。

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

很簡(jiǎn)單椰憋。

現(xiàn)在讓我們來(lái)看看MyApp類(lèi),我在這里聲明了一些主題規(guī)則赔退。

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包含我們的大部分代碼证舟。首先,我們編寫(xiě)Home StatefulWidget類(lèi)并聲明所有必要的變量以便稍后存儲(chǔ)用戶輸入窗骑。

  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;

真正的工作從構(gòu)建方法開(kāi)始女责,但首先讓我分解我們的UI以了解我們將如何編碼它。

我們有3頁(yè):

  1. 包含Logo的主頁(yè)面创译,2個(gè)用于登錄的按鈕和用于注冊(cè)的按鈕抵知,以及一些繪制在底部的曲線。
  2. 登錄頁(yè)面软族,從主頁(yè)面點(diǎn)擊登錄按鈕后出現(xiàn)刷喜,它有4個(gè)組件:關(guān)閉圖標(biāo),2個(gè)輸入字段和一個(gè)提交按鈕互订。
  3. 注冊(cè)頁(yè)面吱肌,是具有1個(gè)附加字段的登錄頁(yè)面的副本痘拆。

現(xiàn)在我們可以開(kāi)始編寫(xiě)每個(gè)組件仰禽!

首先,我們有一個(gè)帶有keybackgroundColorScaffold纺蛆,我們將resizeToAvoidBottomPadding設(shè)置為false吐葵,因?yàn)楫?dāng)鍵盤(pán)越過(guò)它們時(shí),它會(huì)在輸入字段后面變得混亂桥氏。

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

Column小部件中温峭,我們將添加logo()小部件,以使用StackPositioned小部件繪制這個(gè)漂亮的簡(jiǎn)單徽標(biāo)字支。Logo()將在構(gòu)建方法之外凤藏,以使我們的代碼更清晰。

    //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個(gè)按鈕揖庄,一個(gè)是RaisedButton,另一個(gè)是OutlineButton欠雌。為什么我們?yōu)槊總€(gè)按鈕使用2種不同的小部件蹄梢?
僅僅為了造型 ??
RaisedButton可以很容易地給它填充顏色,這是我們想要的登錄按鈕(帶有白色填充)富俄。OutlineButton使得有一個(gè)帶邊框但沒(méi)有填充的按鈕變得更容易禁炒,這就是我們想要的注冊(cè)按鈕。
我真的很擅長(zhǎng)安排我的代碼...所以這是我想到的最好的主意霍比。我在代碼中多次使用凸起按鈕幕袱,但是大綱按鈕只有一個(gè),所以我認(rèn)為最好將一個(gè)凸起按鈕作為一個(gè)函數(shù)并給它以下參數(shù)以使其可重用:

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

這么長(zhǎng)的參數(shù)列表悠瞬!但比多次編寫(xiě)相同的按鈕代碼要好得多们豌!

這是我們的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();
    },
),

主頁(yè)的最后一部分是底部彎曲的白色形狀,想知道如何做到這一點(diǎn)?我們將使用ClipPath小部件玛痊。
在我們的Scaffold體內(nèi)的Column內(nèi)部汰瘫,我們將添加此代碼以繪制彎曲的形狀:

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

讓我們轉(zhuǎn)到一個(gè)名為clipper.dart的新文件,它將包含將繪制彎曲路徑的類(lèi)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;
}

太神奇了混弥!我們完成了主頁(yè)面,讓我們繼續(xù)前進(jìn)对省。


看起來(lái)登錄頁(yè)面和注冊(cè)頁(yè)面是應(yīng)用程序中的新路由蝗拿,但它們不是,每個(gè)都只是一個(gè)PersistentBottomSheet蒿涎!
PersistentBottomSheet是在主要上下文中從底部繪制到屏幕的底部工作表哀托,我們可以在其中填充我們想要的任何內(nèi)容。

讓我們將以下函數(shù)添加到每個(gè)按鈕的onPressed屬性上(實(shí)際上我們將第一個(gè)函數(shù)傳遞給沒(méi)有括號(hào)的_button函數(shù)):

_loginSheet
_registerSheet()

你猜他們是什么劳秋?嗯仓手,確切地說(shuō)!它們的功能將返回登錄和注冊(cè)表玻淑。每個(gè)代碼都很長(zhǎng)嗽冒,我不會(huì)在這里全部包含它,但我們將看一下它的一些重要特性补履。

  1. 它的包裹里面DecoratedBox小部件添坊,然后ClipRRect,給它頂部邊角半徑箫锤。
  2. 為了滾動(dòng)贬蛙,當(dāng)鍵盤(pán)位于密碼字段和提交按鈕之上時(shí),元素用ListView而不是Column包裹谚攒,使用戶能夠滾動(dòng)來(lái)解決問(wèn)題阳准。
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())
  )
)

同樣適用于工作表,登錄和注冊(cè)五鲫。


現(xiàn)在是InputField溺职,它將是一個(gè)名為_(kāi)input的函數(shù),它將返回所需的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),
              )),
        ),
      );
    }

因此位喂,每次我們想要使用輸入字段時(shí)浪耘,我們只需調(diào)用此函數(shù)并為其提供適合我們使用的參數(shù)。我們有5個(gè)輸入字段塑崖,將從這個(gè)功能中提取七冲,非常整潔和優(yōu)雅。


1_dMlmvO2ENIRt0Nic8jHB6A.gif
1_SGCH-xPXb_8rfL4_eGBOCQ.gif

完成规婆!

你可以在這里找到GitHub上的源代碼澜躺。??
轉(zhuǎn):https://medium.com/swlh/simple-clean-login-ui-using-flutter-43314d0dbdee

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蝉稳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子掘鄙,更是在濱河造成了極大的恐慌耘戚,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件操漠,死亡現(xiàn)場(chǎng)離奇詭異收津,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)浊伙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)撞秋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人嚣鄙,你說(shuō)我怎么就攤上這事吻贿。” “怎么了哑子?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵舅列,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我赵抢,道長(zhǎng)剧蹂,這世上最難降的妖魔是什么声功? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任烦却,我火速辦了婚禮,結(jié)果婚禮上先巴,老公的妹妹穿的比我還像新娘其爵。我一直安慰自己,他們只是感情好伸蚯,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布摩渺。 她就那樣靜靜地躺著,像睡著了一般剂邮。 火紅的嫁衣襯著肌膚如雪摇幻。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天挥萌,我揣著相機(jī)與錄音绰姻,去河邊找鬼。 笑死引瀑,一個(gè)胖子當(dāng)著我的面吹牛狂芋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播憨栽,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼帜矾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼翼虫!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起屡萤,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤珍剑,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后死陆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體次慢,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年翔曲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迫像。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瞳遍,死狀恐怖闻妓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情掠械,我是刑警寧澤由缆,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站猾蒂,受9級(jí)特大地震影響均唉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肚菠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一舔箭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚊逢,春花似錦层扶、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至终抽,卻和暖如春戳表,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昼伴。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工匾旭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亩码。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓季率,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親描沟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子飒泻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容