前言一:接下來一段時間我會陸續更新一些列Flutter文字教程前端
更新進度: 每週至少兩篇;vue
更新地點: 首發於公衆號,次日更新於掘金、思否、開發者頭條等地方;算法
更多交流: 能夠添加個人微信 372623326,關注個人微博:coderwhyapi
但願你們能夠 幫忙轉發,點擊在看,給我更多的創做動力。服務器
前言二:這個章節原本打算講解Flutter的渲染原理,可是學習初期過多的講解原理性的內容,並不利於你們快速入門和上手,作出一些帶效果的內容;微信
因此,我打算換一種思路,先講解一些組件的用法,讓你們習慣Flutter的開發過程和模式,再回頭去鞏固原理性的知識;網絡
另外,在講解這些Widget的時候,我並不打算將全部的屬性一一列出,由於沒有意義,也記不住;數據結構
我後面打算有一個專題是關於Flutter佈局的,會選出一些好看的佈局界面帶着你們一塊兒來完成:美團頁面、京東頁面、B站頁面等等,某些我目前沒有講到的屬性,後面應用的會再進行講解;app
在Android中,咱們使用TextView,iOS中咱們使用UILabel來顯示文本;less
Flutter中,咱們使用Text組件控制文本如何展現;
在Flutter中,咱們能夠將文本的控制顯示分紅兩類:
下面咱們來看一下其中一些屬性的使用:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
"《定風波》 蘇軾 \n莫聽穿林打葉聲,何妨吟嘯且徐行。\n竹杖芒鞋輕勝馬,誰怕?一蓑煙雨任生平。",
style: TextStyle(
fontSize: 20,
color: Colors.purple
),
);
}
}
複製代碼
展現效果以下:
咱們能夠經過一些屬性來改變Text的佈局:
代碼以下:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
"《定風波》 蘇軾 \n莫聽穿林打葉聲,何妨吟嘯且徐行。\n竹杖芒鞋輕勝馬,誰怕?一蓑煙雨任生平。",
textAlign: TextAlign.center, // 全部內容都居中對齊
maxLines: 3, // 顯然 "生。" 被刪除了
overflow: TextOverflow.ellipsis, // 超出部分顯示...
// textScaleFactor: 1.25,
style: TextStyle(
fontSize: 20,
color: Colors.purple
),
);
}
}
複製代碼
前面展現的文本,咱們都應用了相同的樣式,若是咱們但願給他們不一樣的樣式呢?
若是但願展現這種混合樣式,那麼咱們能夠利用分片來進行操做(在Android中,咱們可使用SpannableString,在iOS中,咱們可使用NSAttributedString完成,瞭解便可)
代碼以下:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text.rich(
TextSpan(
children: [
TextSpan(text: "《定風波》", style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold, color: Colors.black)),
TextSpan(text: "蘇軾", style: TextStyle(fontSize: 18, color: Colors.redAccent)),
TextSpan(text: "\n莫聽穿林打葉聲,何妨吟嘯且徐行。\n竹杖芒鞋輕勝馬,誰怕?一蓑煙雨任生平。")
],
),
style: TextStyle(fontSize: 20, color: Colors.purple),
textAlign: TextAlign.center,
);
}
}
複製代碼
Material widget庫中提供了多種按鈕Widget如FloatingActionButton、RaisedButton、FlatButton、OutlineButton等
咱們直接來對他們進行一個展現:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
FloatingActionButton(
child: Text("FloatingActionButton"),
onPressed: () {
print("FloatingActionButton Click");
},
),
RaisedButton(
child: Text("RaisedButton"),
onPressed: () {
print("RaisedButton Click");
},
),
FlatButton(
child: Text("FlatButton"),
onPressed: () {
print("FlatButton Click");
},
),
OutlineButton(
child: Text("OutlineButton"),
onPressed: () {
print("OutlineButton Click");
},
)
],
);
}
}
複製代碼
前面的按鈕咱們使用的都是默認樣式,咱們能夠經過一些屬性來改變按鈕的樣式
RaisedButton(
child: Text("贊成協議", style: TextStyle(color: Colors.white)),
color: Colors.orange, // 按鈕的顏色
highlightColor: Colors.orange[700], // 按下去高亮顏色
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), // 圓角的實現
onPressed: () {
print("贊成協議");
},
)
複製代碼
事實上這裏還有一個比較常見的屬性:elevation,用於控制陰影的大小,不少地方都會有這個屬性,你們能夠自行演示一下
圖片可讓咱們的應用更加豐富多彩,Flutter中使用Image組件
Image組件有不少的構造函數,咱們這裏主要學習兩個:
相對來說,Flutter中加載網絡圖片會更加簡單,直接傳入URL並不須要什麼配置,因此咱們先來看一下Flutter中如何加載網絡圖片。
咱們先來看看Image有哪些屬性能夠設置:
const Image({
...
this.width, //圖片的寬
this.height, //圖片高度
this.color, //圖片的混合色值
this.colorBlendMode, //混合模式
this.fit,//縮放模式
this.alignment = Alignment.center, //對齊方式
this.repeat = ImageRepeat.noRepeat, //重複方式
...
})
複製代碼
width
、height
:用於設置圖片的寬、高,當不指定寬高時,圖片會根據當前父容器的限制,儘量的顯示其原始大小,若是隻設置width
、height
的其中一個,那麼另外一個屬性默認會按比例縮放,但能夠經過下面介紹的fit
屬性來指定適應規則。fit
:該屬性用於在圖片的顯示空間和圖片自己大小不一樣時指定圖片的適應模式。適應模式是在BoxFit
中定義,它是一個枚舉類型,有以下值:
fill
:會拉伸填充滿顯示空間,圖片自己長寬比會發生變化,圖片會變形。cover
:會按圖片的長寬比放大後居中填滿顯示空間,圖片不會變形,超出顯示空間部分會被剪裁。contain
:這是圖片的默認適應規則,圖片會在保證圖片自己長寬比不變的狀況下縮放以適應當前顯示空間,圖片不會變形。fitWidth
:圖片的寬度會縮放到顯示空間的寬度,高度會按比例縮放,而後居中顯示,圖片不會變形,超出顯示空間部分會被剪裁。fitHeight
:圖片的高度會縮放到顯示空間的高度,寬度會按比例縮放,而後居中顯示,圖片不會變形,超出顯示空間部分會被剪裁。none
:圖片沒有適應策略,會在顯示空間內顯示圖片,若是圖片比顯示空間大,則顯示空間只會顯示圖片中間部分。color
和 colorBlendMode
:在圖片繪製時能夠對每個像素進行顏色混合處理,color
指定混合色,而colorBlendMode
指定混合模式;repeat
:當圖片自己大小小於顯示空間時,指定圖片的重複規則。咱們對其中某些屬性作一個演練:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Image.network(
"http://img0.dili360.com/ga/M01/48/3C/wKgBy1kj49qAMVd7ADKmuZ9jug8377.tub.jpg",
alignment: Alignment.topCenter,
repeat: ImageRepeat.repeatY,
color: Colors.red,
colorBlendMode: BlendMode.colorDodge,
),
width: 300,
height: 300,
color: Colors.yellow,
),
);
}
}
複製代碼
加載本地圖片稍微麻煩一點,須要將圖片引入,而且進行配置
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 300,
height: 300,
color: Colors.yellow,
child: Image.asset("images/test.jpeg"),
),
);
}
}
複製代碼
在Flutter中實現圓角效果也是使用一些Widget來實現的。
方式一:CircleAvatar
CircleAvatar能夠實現圓角頭像,也能夠添加一個子Widget:
const CircleAvatar({
Key key,
this.child, // 子Widget
this.backgroundColor, // 背景顏色
this.backgroundImage, // 背景圖像
this.foregroundColor, // 前景顏色
this.radius, // 半徑
this.minRadius, // 最小半徑
this.maxRadius, // 最大半徑
})
複製代碼
咱們來實現一個圓形頭像:
注意一:這裏咱們使用的是NetworkImage,由於backgroundImage要求咱們傳入一個ImageProvider;
image
屬性,該屬性就是一個ImageProvider注意二:這裏我還在裏面添加了一個文字,可是我在文字外層包裹了一個Container;
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: CircleAvatar(
radius: 100,
backgroundImage: NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg"),
child: Container(
alignment: Alignment(0, .5),
width: 200,
height: 200,
child: Text("兵長利威爾")
),
),
);
}
}
複製代碼
方式二:ClipOval
ClipOval也能夠實現圓角頭像,並且一般是在只有頭像時使用
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ClipOval(
child: Image.network(
"https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",
width: 200,
height: 200,
),
),
);
}
}
複製代碼
實現方式三:Container+BoxDecoration
這種方式咱們放在講解Container時來說這種方式
方式一:ClipRRect
ClipRRect用於實現圓角效果,能夠設置圓角的大小。
實現代碼以下,很是簡單:
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
"https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",
width: 200,
height: 200,
),
),
);
}
}
複製代碼
方式二:Container+BoxDecoration
這個也放到後面講解Container時講解
和用戶交互的其中一種就是輸入框,好比註冊、登陸、搜索,咱們收集用戶輸入的內容將其提交到服務器。
TextField用於接收用戶的文本輸入,它提供了很是多的屬性,咱們來看一下源碼:
const TextField({
Key key,
this.controller,
this.focusNode,
this.decoration = const InputDecoration(),
TextInputType keyboardType,
this.textInputAction,
this.textCapitalization = TextCapitalization.none,
this.style,
this.strutStyle,
this.textAlign = TextAlign.start,
this.textAlignVertical,
this.textDirection,
this.readOnly = false,
ToolbarOptions toolbarOptions,
this.showCursor,
this.autofocus = false,
this.obscureText = false,
this.autocorrect = true,
this.maxLines = 1,
this.minLines,
this.expands = false,
this.maxLength,
this.maxLengthEnforced = true,
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.inputFormatters,
this.enabled,
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true,
this.onTap,
this.buildCounter,
this.scrollController,
this.scrollPhysics,
})
複製代碼
咱們來學習幾個比較常見的屬性:
keyboardType
鍵盤的類型,style
設置樣式,textAlign
文本對齊方式,maxLength
最大顯示行數等等;decoration
:用於設置輸入框相關的樣式
controller
:onChanged
:監聽輸入框內容的改變,傳入一個回調函數onSubmitted
:點擊鍵盤中右下角的down時,會回調的一個函數咱們來演示一下TextField的decoration屬性以及監聽:
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFieldDemo()
],
),
);
}
}
class TextFieldDemo extends StatefulWidget {
@override
_TextFieldDemoState createState() => _TextFieldDemoState();
}
class _TextFieldDemoState extends State<TextFieldDemo> {
@override
Widget build(BuildContext context) {
return TextField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "username",
hintText: "請輸入用戶名",
border: InputBorder.none,
filled: true,
fillColor: Colors.lightGreen
),
onChanged: (value) {
print("onChanged:$value");
},
onSubmitted: (value) {
print("onSubmitted:$value");
},
);
}
}
複製代碼
咱們能夠給TextField添加一個控制器(Controller),可使用它設置文本的初始值,也可使用它來監聽文本的改變;
事實上,若是咱們沒有爲TextField提供一個Controller,那麼會Flutter會默認建立一個TextEditingController的,這個結論能夠經過閱讀源碼獲得:
@override
void initState() {
super.initState();
// ...其餘代碼
if (widget.controller == null)
_controller = TextEditingController();
}
複製代碼
咱們也能夠本身來建立一個Controller控制一些內容:
class _TextFieldDemoState extends State<TextFieldDemo> {
final textEditingController = TextEditingController();
@override
void initState() {
super.initState();
// 1.設置默認值
textEditingController.text = "Hello World";
// 2.監聽文本框
textEditingController.addListener(() {
print("textEditingController:${textEditingController.text}");
});
}
// ...省略build方法
}
複製代碼
在咱們開發註冊、登陸頁面時,一般會有多個表單須要同時獲取內容或者進行一些驗證,若是對每個TextField都分別進行驗證,是一件比較麻煩的事情。
作過前端的開發知道,咱們能夠將多個input標籤放在一個form裏面,Flutter也借鑑了這樣的思想:咱們能夠經過Form對輸入框進行分組,統一進行一些操做。
Form表單也是一個Widget,能夠在裏面放入咱們的輸入框。
可是Form表單中輸入框必須是FormField類型的
咱們經過Form的包裹,來實現一個註冊的頁面:
class FormDemo extends StatefulWidget {
@override
_FormDemoState createState() => _FormDemoState();
}
class _FormDemoState extends State<FormDemo> {
@override
Widget build(BuildContext context) {
return Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "用戶名或手機號"
),
),
TextFormField(
obscureText: true,
decoration: InputDecoration(
icon: Icon(Icons.lock),
labelText: "密碼"
),
),
SizedBox(height: 16,),
Container(
width: double.infinity,
height: 44,
child: RaisedButton(
color: Colors.lightGreen,
child: Text("注 冊", style: TextStyle(fontSize: 20, color: Colors.white),),
onPressed: () {
print("點擊了註冊按鈕");
},
),
)
],
),
);
}
}
複製代碼
有了表單後,咱們須要在點擊註冊時,能夠同時獲取和保存表單中的數據,怎麼能夠作到呢?
用戶名
和密碼
的表單信息。如何同時獲取用戶名
和密碼
的表單信息?
Form的State對象
的save方法,就會調用Form中放入的TextFormField的onSave回調:TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "用戶名或手機號"
),
onSaved: (value) {
print("用戶名:$value");
},
),
複製代碼
Form對象
來調用它的save方法呢?知識點:在Flutter如何能夠獲取一個經過一個引用獲取一個StatefulWidget的State對象呢?
答案:經過綁定一個GlobalKey便可。
案例代碼演練:
class FormDemo extends StatefulWidget {
@override
_FormDemoState createState() => _FormDemoState();
}
class _FormDemoState extends State<FormDemo> {
final registerFormKey = GlobalKey<FormState>();
String username, password;
void registerForm() {
registerFormKey.currentState.save();
print("username:$username password:$password");
}
@override
Widget build(BuildContext context) {
return Form(
key: registerFormKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "用戶名或手機號"
),
onSaved: (value) {
this.username = value;
},
),
TextFormField(
obscureText: true,
decoration: InputDecoration(
icon: Icon(Icons.lock),
labelText: "密碼"
),
onSaved: (value) {
this.password = value;
},
),
SizedBox(height: 16,),
Container(
width: double.infinity,
height: 44,
child: RaisedButton(
color: Colors.lightGreen,
child: Text("注 冊", style: TextStyle(fontSize: 20, color: Colors.white),),
onPressed: registerForm,
),
)
],
),
);
}
}
複製代碼
在表單中,咱們能夠添加驗證器
,若是不符合某些特定的規則,那麼給用戶必定的提示信息
好比咱們須要帳號和密碼有這樣的規則:帳號和密碼都不能爲空。
按照以下步驟就能夠完成整個驗證過程:
也能夠爲TextFormField添加一個屬性:autovalidate
備註:全部內容首發於公衆號,以後除了Flutter也會更新其餘技術文章,TypeScript、React、Node、uniapp、mpvue、數據結構與算法等等,也會更新一些本身的學習心得等,歡迎你們關注