
前言
上一篇我們進(jìn)行了Flutter的環(huán)境搭建。整個(gè)過(guò)程相比大家應(yīng)該也很順利。在項(xiàng)目搭建完畢之后,創(chuàng)建項(xiàng)目的時(shí)候會(huì)有一個(gè)簡(jiǎn)單的Demo供開發(fā)者去體驗(yàn)。那么這一篇文章主要要做的就是,簡(jiǎn)單的介紹一下Flutter的一些簡(jiǎn)單的使用、常用的widget以及dart的語(yǔ)法。因?yàn)槲沂且幻鹖OS開發(fā)工程師,所以我還會(huì)類比一下iOS開發(fā)中控件,這樣方便我們的吸收和記憶。這里所有的代碼都是用Android Studio開發(fā)的,沒有用VSCode的原因就是AS的兼容性要更加的強(qiáng)大一些。
下面Demo的地址:https://github.com/Spr1ngHall/FlutterDemo
創(chuàng)建并運(yùn)行項(xiàng)目
首先我們?cè)趧?chuàng)建項(xiàng)目之前,為了確保環(huán)境是ok的,我們?cè)诿钚星?/p>
flutter doctor
用來(lái)檢察一下環(huán)境是否配置ok。
薛立恒@xuelihengdeMacBookPro ~/Desktop flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[?] Flutter (Channel stable, v1.5.4-hotfix.2, on Mac OS X 10.14.5 18F132, locale
zh-Hans-CN)
[?] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[?] iOS toolchain - develop for iOS devices (Xcode 10.2.1)
[?] Android Studio (version 3.4)
[?] VS Code (version 1.35.1)
[?] Connected device (1 available)
界面顯示這個(gè)打印信息才能說(shuō)明你的所有環(huán)境配置都是ok的,如果不ok,就參考我的前一篇文章。
這給打擊介紹兩種創(chuàng)建項(xiàng)目的方法,一種是命令行的形式,一種是手動(dòng)。
1、命令行形式創(chuàng)建項(xiàng)目
- 首先cd到你想要?jiǎng)?chuàng)建項(xiàng)目的路徑文件夾
cd /Users/xueliheng/Desktop/Flutter/FlutterDemo
接下來(lái)我們運(yùn)行一句命令
flutter create hello_flutter
那么我們就在相應(yīng)的文件夾里面能看到我們創(chuàng)建的hello_flutter。這里可能有一些人就要問了,為什么這里創(chuàng)建工程的名字不用大寫?這里就要說(shuō)明一下,F(xiàn)lutter在創(chuàng)建項(xiàng)目的時(shí)候,是跟iOS的命名規(guī)則不一樣的,他們所有的文件夾,包括項(xiàng)目中創(chuàng)建的文件名都不是不能含有大寫字母的,如果含有大寫字母,會(huì)報(bào)下面的錯(cuò)誤。

接著我們來(lái)認(rèn)識(shí)一下創(chuàng)建的項(xiàng)目:

-
android和ios就分別是不同的兩個(gè)工程的工程文件,這個(gè)一般不會(huì)動(dòng),需要?jiǎng)拥臅r(shí)候,大概也是的到項(xiàng)目混合開發(fā)的階段,到時(shí)候再說(shuō)吧。 - 然后
lib文件就是裝.dart為結(jié)尾的代碼文件的。這里面也就是我們需要寫的flutter工程源碼。 - 然后
test是自動(dòng)化測(cè)試用的。 - 這里的
pubspec.lock和pubspec.yaml這兩個(gè)文件,大家可以直接聯(lián)想成為iOS里面的podspec和podfile.lock文件,功能很類似。
接下來(lái)我們進(jìn)入到hello_flutter這個(gè)Demo里面去:
cd hello_flutter
繼續(xù)敲:
flutter run
那么項(xiàng)目就會(huì)自動(dòng)打開。這里還是要說(shuō)明一下,如果你同時(shí)開啟了兩個(gè)模擬器,那么這個(gè)時(shí)候敲擊上面這個(gè)命令的時(shí)候,flutter會(huì)報(bào)一個(gè)錯(cuò)誤,會(huì)讓你選擇一個(gè)具體的模擬器去運(yùn)行項(xiàng)目。同時(shí)也把模擬器的信息都打印出來(lái)了,這個(gè)時(shí)候你只需要選擇一個(gè)執(zhí)行就行了:
flutter run -d 'iPhone X?'
或者是運(yùn)行到所有的模擬器上面:
flutter run -d all
我們?cè)龠\(yùn)行項(xiàng)目之后,模擬器的打印如下:
薛立恒@xuelihengdeMacBookPro ~/Desktop/Flutter/FlutterDemo/hello_flutter flutter run
Launching lib/main.dart on iPhone X? in debug mode...
Running Xcode build...
├─Assembling Flutter resources... 1.3s
└─Compiling, linking and signing... 3.7s
Xcode build done. 6.4s
Syncing files to device iPhone X?... 1,650ms
?? To hot reload changes while running, press "r". To hot restart (and rebuild
state), press "R".
An Observatory debugger and profiler on iPhone X? is available at:
http://127.0.0.1:62574/dpTTmbSopM8=/
For a more detailed help message, press "h". To detach, press "d"; to quit,
press "q".
這里有一個(gè)提示,讓你敲r或者R,其實(shí)意思就是如果你需要重新build一下項(xiàng)目,就輸入R,如果你敲r的意思就是,啟動(dòng)熱重載。熱重載是flutter的特色功能,能在不build項(xiàng)目的同時(shí)也能看到模擬器上面的東西在變動(dòng),所見即所得,這個(gè)真是iOS開發(fā)的一個(gè)福音啊!按q的話就是退出。
2、手動(dòng)形式創(chuàng)建項(xiàng)目
-
打開Android Studio會(huì)發(fā)現(xiàn)這里多了一個(gè)選項(xiàng)
屏幕快照 2019-06-30 13.58.09.png
這里會(huì)有下面幾個(gè)選項(xiàng)

分別來(lái)介紹一下選項(xiàng)吧
- Flutter Application:顧名思義就是創(chuàng)建一個(gè)Flutter的項(xiàng)目,這里不用說(shuō)肯定選他;
- Flutter Plugin:如果說(shuō)你開發(fā)出來(lái)的項(xiàng)目既要用到iOS原生也要用到Android原生,那么這個(gè)時(shí)候你就要選擇給他們開發(fā)一個(gè)插件;
- Flutter Package:如果說(shuō)你開發(fā)出來(lái)的項(xiàng)目是只給Dart語(yǔ)言使用的,那么這個(gè)項(xiàng)目就可以創(chuàng)建一個(gè)package,其實(shí)plugin和package都差不太多,只是創(chuàng)建不同模式而已。
-
Flutter Module:這個(gè)是混合開發(fā)的時(shí)候會(huì)用到的,這里先不講,后面研究研究在來(lái)說(shuō)。
點(diǎn)擊第一個(gè)選項(xiàng),然后一步步的填寫項(xiàng)目名稱就ok了,這里太簡(jiǎn)單就不贅述了。
但是這里是有一個(gè)坑的,如果你在創(chuàng)建項(xiàng)目的時(shí)候,如果選擇了一個(gè)中文路徑的話,AS會(huì)報(bào)錯(cuò),這里AS是不支持在中文路徑下面創(chuàng)建項(xiàng)目的,如果你非要在中文路徑下面創(chuàng)建項(xiàng)目,就只能用第一種命令行的形式去創(chuàng)建項(xiàng)目了。
編寫項(xiàng)目
介紹了這么多前戲,終于來(lái)到正題了。刪掉main.dart里面所有的代碼。我們重頭開始。
首先引入基礎(chǔ)組件庫(kù):
import 'package:flutter/material.dart';
我們可以看作就是UIKit。
創(chuàng)建main函數(shù):
void main() {
runApp(Center(
child: Text(
'Hello',
textDirection: TextDirection.ltr,
),
));
}
這個(gè)main函數(shù)跟iOS中的main函數(shù)其實(shí)是一個(gè)道理,runApp就相當(dāng)于UIApplication。Center里面的意思就是,其中的child組件按照居中對(duì)齊的方式排列。child當(dāng)然就好理解了,就是iOS中的subView的意思。所以就可以得出,runApp后面的這一段代碼,其實(shí)就是在設(shè)置一個(gè)根控制器。然后設(shè)置一下Text組件的一些屬性。
什么是Widget
講到這里,我們都知道了又一個(gè)child是指的subView的意思,那么UIView是什么呢?
那么這里,我們就要說(shuō)到Widget。Widget翻譯過(guò)來(lái)就是小部件的意思。我們可以理解為一個(gè)小控件。就像一個(gè)UIView一樣。
然后Widget分為兩種。一種是Stateful(有狀態(tài)的),一種是Stateless(無(wú)狀態(tài)的)。他們分別有什么用呢?無(wú)狀態(tài)的就表示這個(gè)Widget創(chuàng)建出來(lái)是什么樣子就是什么樣子,狀態(tài)是不可改變的。相反,有狀態(tài)的其實(shí)也是一個(gè)特殊的無(wú)狀態(tài)的Widget,但是這個(gè)Widget帶有一個(gè)狀態(tài)類,去標(biāo)識(shí)這個(gè)widget的一些狀態(tài)。有狀態(tài)的Widget在渲染的時(shí)候,也是渲染成了一個(gè)無(wú)狀態(tài)的Widget。
創(chuàng)建一個(gè)Widget類
我們現(xiàn)在創(chuàng)建一個(gè)MyWidget類,也就是一個(gè)widget控件,:
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
}
這里重寫了一個(gè)build方法,這個(gè)方法是干嘛的呢?
實(shí)際上,這個(gè)方法就是將你現(xiàn)在自定義的這個(gè)小控件放到控件的渲染的樹中去。這個(gè)return返回的是什么,那么這個(gè)控件就是什么。他會(huì)從你的main函數(shù)中的runApp中的第一個(gè)控件去渲染,然后逐步的去渲染里面內(nèi)部的控件。
(tips:這里創(chuàng)建的時(shí)候跟前面創(chuàng)建文件名是不一樣的,這里創(chuàng)建類名是需要運(yùn)用駝峰命名法的,并且首字母是大寫。這里注意區(qū)分一下。)
那么我們現(xiàn)在可以把runApp中的Text控件換成我們的自己自定義的控件了,但是前提是我們要重寫一下build方法。
void main() {
runApp(Center(
child: MyWidget(),
));
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Text(
'Hello Flutter',
textDirection: TextDirection.ltr,
),
);
}
}
當(dāng)然,其實(shí)我們也可以在main方法中去自定義一個(gè)function,然后function返回的是一個(gè)組件,這種方式也是可以的,代碼如下:
Widget func () {
return Text('Hello');
}
但是我個(gè)人覺得,如果說(shuō)是比較復(fù)雜的控件的話,還是定義一個(gè)類去封裝控件比較好,因?yàn)榭梢园芽丶盅b到不同的文件中,供別人使用。
tips:這里我們發(fā)現(xiàn)MyWidget方法返回的也是一個(gè)Center (),那么我們其實(shí)是可以把runApp中的Center方法省略掉。并且如果一個(gè)方法里面,只有一句代碼,dart語(yǔ)言是可以簡(jiǎn)寫成如下的:
void main() => runApp(MyWidget());
這個(gè)是不是很熟悉,這就是我們剛開始創(chuàng)建項(xiàng)目時(shí),默認(rèn)的工程里面,main函數(shù)的代碼就是這樣寫的。這個(gè)在JS ES6里面好像也有。前端同學(xué)估計(jì)會(huì)熟悉一些。
我們點(diǎn)到Text里面去看源碼的時(shí)候,能看到如下簡(jiǎn)化代碼:
class Text extends StatelessWidget {
const Text(
this.data, {
Key key,
this.style,
this.strutStyle,
this.textAlign,
this.textDirection,
this.locale,
this.softWrap,
this.overflow,
this.textScaleFactor,
this.maxLines,
this.semanticsLabel,
}) : assert(
data != null,
'A non-null String must be provided to a Text widget.',
),
textSpan = null,
super(key: key);
const Text.rich(
this.textSpan, {
Key key,
this.style,
this.strutStyle,
this.textAlign,
this.textDirection,
this.locale,
this.softWrap,
this.overflow,
this.textScaleFactor,
this.maxLines,
this.semanticsLabel,
}) : assert(
textSpan != null,
'A non-null TextSpan must be provided to a Text.rich widget.',
),
data = null,
super(key: key);
final String data;
final TextSpan textSpan;
final TextStyle style;
final StrutStyle strutStyle;
final TextAlign textAlign;
final TextDirection textDirection;
final Locale locale;
final bool softWrap;
...
this后面的很好理解,就是這個(gè)類的可選參數(shù),那么下面的final定義的是什么呢?也好理解,就是屬性唄。
為什么用final定義呢?
原因是Text是一個(gè)Stateless的Widget,那么創(chuàng)建出來(lái)之后就是固定了的,屬性也是同樣的道理。那么這里就肯定是final修飾,而不是var修飾。這個(gè)final其實(shí)可以類比Swift或者JS里的let。
接下來(lái),我們創(chuàng)建一個(gè)_textStyle對(duì)象,去設(shè)置一些我們需要設(shè)置的Style:
final _textStyle = TextStyle(
color: Colors.red,
fontSize: 40.0,
);
然后把這個(gè)_textStyle賦值給Text里面的style。
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final _textStyle = TextStyle(
color: Colors.red,
fontSize: 40.0,
);
return Center(
child: Text(
'Hello Flutter',
textDirection: TextDirection.ltr,
style: _textStyle,
),
);
}
}
這種方式同樣只是一種技巧,可以把Style里面的東西提取出來(lái)。這就跟CSS有一些類似了。
認(rèn)識(shí)MaterialApp
這一次直接上代碼吧
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: MyWidget(),
),
theme: ThemeData(
primaryColor: Colors.yellow,
),
);
}
}
...
運(yùn)行結(jié)果是這樣的:

這里MaterialApp其實(shí)上就是Flutter封裝的一些便于我們?nèi)ゴ罱ˋPP的一系列組件。Scaffold實(shí)際上我們可以理解為UINavigationControllre?。其中也包含了AppBar,也就是導(dǎo)航條,body就是實(shí)際顯示在手機(jī)中的內(nèi)容。theme就是一些主題,可以讓我們自己去設(shè)置導(dǎo)航欄的顏色啊等等東西。這一點(diǎn)上來(lái)說(shuō)比iOS確實(shí)是方便了很多。
創(chuàng)建一個(gè)Model
我們?cè)賱?chuàng)建一個(gè)名叫animal.dart的文件,然后敲入如下代碼:
class Animal {
// 構(gòu)造函數(shù)
const Animal({
this.name,
this.imageUrl,
});
final String name;
final String imageUrl;
}
這里定義一個(gè)Animal的類,const Animal()就是構(gòu)造函數(shù),下面的final定義的都是屬性,在構(gòu)造函數(shù)里面賦值name和imageUrl。這就構(gòu)成了一個(gè)Animal的模型。
創(chuàng)建數(shù)據(jù)源
我們創(chuàng)建完模型之后,才應(yīng)該創(chuàng)建一下數(shù)據(jù)源。我們?cè)?code>animal.dart文件中定義一下模型數(shù)組:
//定義一個(gè)模型數(shù)組
final List<Animal> datas = [
Animal(
name: '兔子',
imageUrl:
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561905982563&di=c69bd273942564d09f5eb8ca4eaa1943&imgtype=0&src=http%3A%2F%2Fs15.sinaimg.cn%2Fmw690%2F00328H1Nzy74f5vBmKG8e%26690',
),
Animal(
name: '鴨子',
imageUrl:
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561906404967&di=80e4b6c937176ff9a17bcd8bc377de28&imgtype=0&src=http%3A%2F%2Fphotocdn.sohu.com%2F20120305%2FImg336680797.jpg',
),
Animal(
name: '金錢豹',
imageUrl:
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561906421196&di=ba764154104591d2f9da67c89d6fd36b&imgtype=0&src=http%3A%2F%2Fphotocdn.sohu.com%2F20130611%2FImg378599972.jpg',
),
Animal(
name: '獅子',
imageUrl:
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561906438918&di=e2202a99c9931aa0d76a3e2de25e435b&imgtype=0&src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F615f13c5ff460d568c7b632846a2b04f00cf6509b47e-NhJ9FI_fw658',
),
Animal(
name: '老虎',
imageUrl:
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561906452597&di=766795e10f0d9afc7d11c173f08aaf9c&imgtype=0&src=http%3A%2F%2Fimg18.3lian.com%2Fd%2Ffile%2F201710%2F09%2F02b420dddc4db52a75f7cbbaed83644b.jpg',
),
Animal(
name: '袋鼠',
imageUrl:
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561906467233&di=c31bd84ae874f2ad767ca0a76287a8eb&imgtype=0&src=http%3A%2F%2Fimages.china.cn%2Fattachement%2Fjpg%2Fsite1000%2F20130319%2F001aa0ba5c7712b1f5005e.jpg',
),
Animal(
name: '大象',
imageUrl:
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561906481939&di=16bf1c9ea3c78bc46dff56e30919da53&imgtype=0&src=http%3A%2F%2Fphotocdn.sohu.com%2F20130702%2FImg380495405.jpg',
),
Animal(
name: '公雞',
imageUrl:
'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3634424173,2840985996&fm=26&gp=0.jpg',
),
];
按快捷鍵Option+return快速導(dǎo)入Animal的頭文件。
創(chuàng)建ListView
我們創(chuàng)建一個(gè)新的類名叫Home,然后在App中將home中的Scaffold替換成新建的類。
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
theme: ThemeData(
primaryColor: Colors.yellow,
),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
...
接著我們重寫Home的build方法,并且返回的是一個(gè)Scaffold,然后設(shè)置一下標(biāo)題:
class Home extends StatelessWidget {
Widget _cellForRow (BuildContext context, int index) {
return Text('123');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
);
}
}
這里都不用多說(shuō)。然后我們就需要設(shè)置我們的body了,代碼如下:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: ListView.builder(
itemCount: datas.length,
itemBuilder: _cellForRow,
),
);
}
這里就是創(chuàng)建一個(gè)ListView,這里的itemCount很明顯,就跟iOS中的numberOfRowsInSection方法是一個(gè)道理,意思就是這個(gè)ListView有多少行。itemBuilder很顯然也就是cellForRowAtIndexPath,既然iOS里面我們用的是代理去實(shí)現(xiàn)的,這里我們?yōu)榱烁拥馁N心iOS,我們把這里的實(shí)現(xiàn)抽離出來(lái):
Widget _cellForRow (BuildContext context, int index) {
return Text('123');
}
我們定義了一個(gè)_cellForRow的Widget,這個(gè)Widget返回的就是一個(gè)row所對(duì)應(yīng)顯示的內(nèi)容。
tips:這里說(shuō)明一下:
- 我們?cè)诙x一個(gè)屬性的時(shí)候,如果加了前綴“_”,就標(biāo)識(shí)這是一個(gè)私有的。外面死不能使用的,如果沒有加,那么說(shuō)明外面是可以使用的。
- ListView中是沒有section這個(gè)概念的,我們?cè)谛枰枰纸M的時(shí)候,必須得自己去一行行的實(shí)現(xiàn)了(這一點(diǎn)我覺得iOS做的要好很多,當(dāng)然目前還不好說(shuō),后面慢慢來(lái)看)。
我們運(yùn)行一下項(xiàng)目,就能看到如下的顯示

在Row中添加視圖
我們上面只在row里面添加了一個(gè)Text,這在實(shí)際開發(fā)過(guò)程中是遠(yuǎn)遠(yuǎn)不夠的,那么我們要怎么去添加別的視圖在row中呢?
這里就要用到Container了。話不多說(shuō),線上代碼:
Widget _cellForRow(BuildContext context, int index) {
return Container(
color: Colors.grey[100],
margin: EdgeInsets.all(10),
child: Image.network(datas[index].imageUrl),
);
}
這里的Container就是指的容器,這一點(diǎn)上來(lái)說(shuō),我們可以類比前端的div,也可以類比iOS中的UIView。包括其中的布局方式也跟FlexBox很類似,這一點(diǎn)我們?cè)傧乱黄恼轮袝?huì)來(lái)針對(duì)性的講一下。Container里面的實(shí)現(xiàn)就不用多說(shuō)了,設(shè)置顏色為100度灰、設(shè)置外邊距統(tǒng)一為10、設(shè)置子視圖為一張Image并且是網(wǎng)絡(luò)請(qǐng)求的,請(qǐng)求的url是從datas的數(shù)組中取得。
這里如果要多加一個(gè)Text到row怎么辦呢?這里也可以的:
Widget _cellForRow(BuildContext context, int index) {
return Container(
color: Colors.grey[100],
margin: EdgeInsets.all(10),
child: Column(//這里還有Row可以Stack布局
children: <Widget>[
Image.network(datas[index].imageUrl),
Text(datas[index].name),
],
),
);
}
我們把Image替換成一個(gè)children就行了,這個(gè)children里面是一個(gè)Widget的數(shù)組,那么理論上我們就可以無(wú)限制的往里面添加Widget了。并且誰(shuí)最先執(zhí)行,哪個(gè)控件就在最上面。
執(zhí)行的結(jié)果是這樣的

除了Column布局之外,還有Row布局和Stack布局,我們分別看看效果

上面是Row布局的,其實(shí)就是橫向的從左至右的布局方式,這里圖片太長(zhǎng)了,已經(jīng)把文字都擠出去了。

上面是Stack布局的,意思就是說(shuō)把各個(gè)控件層疊起來(lái)擺放。
如果你現(xiàn)在要在文字和圖片之間弄一個(gè)間距,我們可以直接加一個(gè)SizeBox
SizedBox(
height: 20,
),
把SizeBox也加入到children中去,并且加到文字個(gè)圖片之間,這樣文字跟圖片之間就會(huì)有間距了。
child: Column(
//這里還有Row可以Stack布局
children: <Widget>[
Image.network(datas[index].imageUrl),
SizedBox(
height: 20,
),
Text(
datas[index].name,
style: TextStyle(
fontWeight: FontWeight.w800,
fontSize: 18.0,
fontStyle: FontStyle.values[1],
color: Colors.blue,
),
),
SizedBox(
height: 20,
),
],
),
那么到這里,我們的簡(jiǎn)單的項(xiàng)目就算是完成了。接下來(lái)我們來(lái)簡(jiǎn)單認(rèn)識(shí)幾個(gè)常用的Widget。
常用Widget
Text
我們前面介紹了這個(gè)控件,那么如果我們想要拼接字符串怎么弄呢?上代碼:
class TextDemo extends StatelessWidget {
final TextStyle _textStyle = TextStyle(
fontSize: 16.0,
);
final String _title = '這是一個(gè)標(biāo)題';
final String _detail = '這是一個(gè)內(nèi)容';
@override
Widget build(BuildContext context) {
return Text(
'《${_title}》-- $_detail。最近Flutter已經(jīng)瘋狂的刷屏了各個(gè)技術(shù)博客、技術(shù)網(wǎng)站,完全有一統(tǒng)天下的氣勢(shì)。所以最近也決定開始嘗嘗鮮,從零開始一步步的來(lái)探索Flutter的世界。就從環(huán)境搭建開始,記錄一下自己探索Flutter的過(guò)程。',
textAlign: TextAlign.center,
style: _textStyle,
);
}
}
這里我們看到這個(gè)標(biāo)題是我們拼接到這個(gè)字符串上面的,所以說(shuō)拼接的語(yǔ)法就是:
$_title
//或者
${_title}
在Text中,我們除了可以設(shè)置textAlign以外,我們還可以設(shè)置maxLines,就是限制最大行數(shù)。設(shè)置了最大行數(shù)之后,如果字?jǐn)?shù)超過(guò)了行數(shù),接下來(lái)的是不顯示的,如圖所示

Text(
'《${_title}》-- $_detail。最近Flutter已經(jīng)瘋狂的刷屏了各個(gè)技術(shù)博客、技術(shù)網(wǎng)站,完全有一統(tǒng)天下的氣勢(shì)。所以最近也決定開始嘗嘗鮮,從零開始一步步的來(lái)探索Flutter的世界。就從環(huán)境搭建開始,記錄一下自己探索Flutter的過(guò)程。',
textAlign: TextAlign.center,
style: _textStyle,
maxLines: 3,
overflow: TextOverflow.ellipsis,
);

后面就多了...的符號(hào)。
富文本
直接線上代碼吧:
RichText(
text: TextSpan(
text: '<這是一個(gè)標(biāo)題>',
style: TextStyle(
fontSize: 30,
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text: 'xueliheng500@vip.qq.com',
style: TextStyle(
fontSize: 16,
color: Colors.red,
)
),
TextSpan(
text: '?',
style: TextStyle(
fontSize: 16,
color: Colors.red,
)
),
TextSpan(
text: 'xueliheng500@vip.qq.com',
style: TextStyle(
fontSize: 16,
color: Colors.red,
)
),
],
),
);
}
最后呈現(xiàn)的效果:

這里可以總結(jié)一下:
- 富文本用的Widget就是
RichText; - 我們需要添加富文本只需要添加
children就可以了; -
children是一個(gè)TextSpan的數(shù)組; - 我們可以添加很多的
TextSpan,并且自定義相應(yīng)的TextSpan,來(lái)完成富文本的要求。
結(jié)語(yǔ)
今天我們從Flutter的基礎(chǔ)的main函數(shù)一直講到通過(guò)ListView去展示一些常用的界面,并且還介紹了一些常用的Widget,這當(dāng)然不是全部,看完這個(gè)就以為自己已經(jīng)完全搞懂Flutter的只能說(shuō)太年輕了。后面我還會(huì)持續(xù)更新。預(yù)告一下,不出意外,下一篇應(yīng)該會(huì)給大家介紹一些Flutter的布局問題。上文中有提到,這里先賣個(gè)關(guān)子。如果覺得文章對(duì)你有用,可以幫我點(diǎn)個(gè)贊!需要技術(shù)交流的,可以發(fā)郵件到我的郵箱:coderspr1nghall@gmail.com。
