我與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è):
- 包含Logo的主頁(yè)面创译,2個(gè)用于登錄的按鈕和用于注冊(cè)的按鈕抵知,以及一些繪制在底部的曲線。
- 登錄頁(yè)面软族,從主頁(yè)面點(diǎn)擊登錄按鈕后出現(xiàn)刷喜,它有4個(gè)組件:關(guān)閉圖標(biāo),2個(gè)輸入字段和一個(gè)提交按鈕互订。
- 注冊(cè)頁(yè)面吱肌,是具有1個(gè)附加字段的登錄頁(yè)面的副本痘拆。
現(xiàn)在我們可以開(kāi)始編寫(xiě)每個(gè)組件仰禽!
首先,我們有一個(gè)帶有key
和backgroundColor
的Scaffold
纺蛆,我們將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()小部件,以使用Stack和Positioned小部件繪制這個(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ì)在這里全部包含它,但我們將看一下它的一些重要特性补履。
- 它的包裹里面DecoratedBox小部件添坊,然后ClipRRect,給它頂部邊角半徑箫锤。
- 為了滾動(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)雅。
完成规婆!
你可以在這里找到GitHub上的源代碼澜躺。??
轉(zhuǎn):https://medium.com/swlh/simple-clean-login-ui-using-flutter-43314d0dbdee