上一篇咱們進行了Flutter的環境搭建。整個過程相比你們應該也很順利。在項目搭建完畢以後,建立項目的時候會有一個簡單的Demo供開發者去體驗。那麼這一篇文章主要要作的就是,簡單的介紹一下Flutter的一些簡單的使用、經常使用的widget以及dart的語法。由於我是一名iOS開發工程師,因此我還會類比一下iOS開發中控件,這樣方便咱們的吸取和記憶。這裏全部的代碼都是用Android Studio開發的,沒有用VSCode的緣由就是AS的兼容性要更加的強大一些。前端
下面Demo的地址:github.com/Spr1ngHall/…android
首先咱們在建立項目以前,爲了確保環境是ok的,咱們在命令行敲ios
flutter doctor
複製代碼
用來檢察一下環境是否配置ok。git
薛立恆@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)
複製代碼
界面顯示這個打印信息才能說明你的全部環境配置都是ok的,若是不ok,就參考個人前一篇文章。github
這給打擊介紹兩種建立項目的方法,一種是命令行的形式,一種是手動。數組
cd /Users/xueliheng/Desktop/Flutter/FlutterDemo
複製代碼
接下來咱們運行一句命令bash
flutter create hello_flutter
複製代碼
那麼咱們就在相應的文件夾裏面能看到咱們建立的hello_flutter
。這裏可能有一些人就要問了,爲何這裏建立工程的名字不用大寫?這裏就要說明一下,Flutter在建立項目的時候,是跟iOS的命名規則不同的,他們全部的文件夾,包括項目中建立的文件名都不是不能含有大寫字母的,若是含有大寫字母,會報下面的錯誤。網絡
接着咱們來認識一下建立的項目: app
android
和ios
就分別是不一樣的兩個工程的工程文件,這個通常不會動,須要動的時候,大概也是的到項目混合開發的階段,到時候再說吧。lib
文件就是裝.dart
爲結尾的代碼文件的。這裏面也就是咱們須要寫的flutter工程源碼。test
是自動化測試用的。pubspec.lock
和pubspec.yaml
這兩個文件,你們能夠直接聯想成爲iOS裏面的podspec
和podfile.lock
文件,功能很相似。接下來咱們進入到hello_flutter
這個Demo裏面去:less
cd hello_flutter
複製代碼
繼續敲:
flutter run
複製代碼
那麼項目就會自動打開。這裏仍是要說明一下,若是你同時開啓了兩個模擬器,那麼這個時候敲擊上面這個命令的時候,flutter會報一個錯誤,會讓你選擇一個具體的模擬器去運行項目。同時也把模擬器的信息都打印出來了,這個時候你只須要選擇一個執行就好了:
flutter run -d 'iPhone Xʀ'
複製代碼
或者是運行到全部的模擬器上面:
flutter run -d all
複製代碼
咱們再運行項目以後,模擬器的打印以下:
薛立恆@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".
複製代碼
這裏有一個提示,讓你敲r
或者R
,其實意思就是若是你須要從新build一下項目,就輸入R
,若是你敲r
的意思就是,啓動熱重載。熱重載是flutter的特點功能,能在不build項目的同時也能看到模擬器上面的東西在變更,所見即所得,這個真是iOS開發的一個福音啊!按q
的話就是退出。
這裏會有下面幾個選項
分別來介紹一下選項吧
可是這裏是有一個坑的,若是你在建立項目的時候,若是選擇了一箇中文路徑的話,AS會報錯,這裏AS是不支持在中文路徑下面建立項目的,若是你非要在中文路徑下面建立項目,就只能用第一種命令行的形式去建立項目了。
介紹了這麼多前戲,終於來到正題了。刪掉main.dart
裏面全部的代碼。咱們重頭開始。
import 'package:flutter/material.dart';
複製代碼
咱們能夠看做就是UIKit
。
void main() {
runApp(Center(
child: Text(
'Hello',
textDirection: TextDirection.ltr,
),
));
}
複製代碼
這個main
函數跟iOS中的main
函數實際上是一個道理,runApp
就至關於UIApplication
。Center
裏面的意思就是,其中的child
組件按照居中對齊的方式排列。child
固然就好理解了,就是iOS中的subView
的意思。因此就能夠得出,runApp
後面的這一段代碼,其實就是在設置一個根控制器。而後設置一下Text
組件的一些屬性。
講到這裏,咱們都知道了又一個child
是指的subView
的意思,那麼UIView
是什麼呢?
那麼這裏,咱們就要說到Widget
。Widget
翻譯過來就是小部件的意思。咱們能夠理解爲一個小控件。就像一個UIView
同樣。 而後Widget分爲兩種。一種是Stateful
(有狀態的),一種是Stateless
(無狀態的)。他們分別有什麼用呢?無狀態的就表示這個Widget建立出來是什麼樣子就是什麼樣子,狀態是不可改變的。相反,有狀態的其實也是一個特殊的無狀態的Widget,可是這個Widget帶有一個狀態類,去標識這個widget的一些狀態。有狀態的Widget在渲染的時候,也是渲染成了一個無狀態的Widget。
咱們如今建立一個MyWidget
類,也就是一個widget控件,:
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
}
複製代碼
這裏重寫了一個build
方法,這個方法是幹嗎的呢? 實際上,這個方法就是將你如今自定義的這個小控件放到控件的渲染的樹中去。這個return
返回的是什麼,那麼這個控件就是什麼。他會從你的main
函數中的runApp
中的第一個控件去渲染,而後逐步的去渲染裏面內部的控件。 (**tips:**這裏建立的時候跟前面建立文件名是不同的,這裏建立類名是須要運用駝峯命名法的,而且首字母是大寫。這裏注意區分一下。) 那麼咱們如今能夠把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,
),
);
}
}
複製代碼
固然,其實咱們也能夠在main
方法中去自定義一個function,而後function返回的是一個組件,這種方式也是能夠的,代碼以下:
Widget func () {
return Text('Hello');
}
複製代碼
可是我我的以爲,若是說是比較複雜的控件的話,仍是定義一個類去封裝控件比較好,由於能夠把控件分裝到不一樣的文件中,供別人使用。
**tips:**這裏咱們發現MyWidget
方法返回的也是一個Center ()
,那麼咱們實際上是能夠把runApp
中的Center
方法省略掉。而且若是一個方法裏面,只有一句代碼,dart語言是能夠簡寫成以下的:
void main() => runApp(MyWidget());
複製代碼
這個是否是很熟悉,這就是咱們剛開始建立項目時,默認的工程裏面,main
函數的代碼就是這樣寫的。這個在JS ES6裏面好像也有。前端同窗估計會熟悉一些。 咱們點到Text
裏面去看源碼的時候,能看到以下簡化代碼:
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
後面的很好理解,就是這個類的可選參數,那麼下面的final
定義的是什麼呢?也好理解,就是屬性唄。 爲何用final
定義呢? 緣由是Text
是一個Stateless
的Widget,那麼建立出來以後就是固定了的,屬性也是一樣的道理。那麼這裏就確定是final
修飾,而不是var
修飾。這個final
其實能夠類比Swift或者JS裏的let
。 接下來,咱們建立一個_textStyle
對象,去設置一些咱們須要設置的Style:
final _textStyle = TextStyle(
color: Colors.red,
fontSize: 40.0,
);
複製代碼
而後把這個_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
裏面的東西提取出來。這就跟CSS有一些相似了。
這一次直接上代碼吧
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,
),
);
}
}
...
複製代碼
運行結果是這樣的:
這裏MaterialApp
其實上就是Flutter封裝的一些便於咱們去搭建APP的一系列組件。Scaffold
實際上咱們能夠理解爲UINavigationControllre
。其中也包含了AppBar
,也就是導航條,body
就是實際顯示在手機中的內容。theme
就是一些主題,可讓咱們本身去設置導航欄的顏色啊等等東西。這一點上來講比iOS確實是方便了不少。
咱們再建立一個名叫animal.dart
的文件,而後敲入以下代碼:
class Animal {
// 構造函數
const Animal({
this.name,
this.imageUrl,
});
final String name;
final String imageUrl;
}
複製代碼
這裏定義一個Animal
的類,const Animal()
就是構造函數,下面的final
定義的都是屬性,在構造函數裏面賦值name
和imageUrl
。這就構成了一個Animal
的模型。
咱們建立完模型以後,才應該建立一下數據源。咱們在animal.dart
文件中定義一下模型數組:
//定義一個模型數組
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
快速導入Animal
的頭文件。
咱們建立一個新的類名叫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
方法,而且返回的是一個Scaffold
,而後設置一下標題:
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'),
),
);
}
}
複製代碼
這裏都不用多說。而後咱們就須要設置咱們的body
了,代碼以下:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: ListView.builder(
itemCount: datas.length,
itemBuilder: _cellForRow,
),
);
}
複製代碼
這裏就是建立一個ListView
,這裏的itemCount
很明顯,就跟iOS中的numberOfRowsInSection
方法是一個道理,意思就是這個ListView
有多少行。itemBuilder
很顯然也就是cellForRowAtIndexPath
,既然iOS裏面咱們用的是代理去實現的,這裏咱們爲了更加的貼心iOS,咱們把這裏的實現抽離出來:
Widget _cellForRow (BuildContext context, int index) {
return Text('123');
}
複製代碼
咱們定義了一個_cellForRow
的Widget,這個Widget返回的就是一個row所對應顯示的內容。 **tips:**這裏說明一下:
咱們運行一下項目,就能看到以下的顯示
咱們上面只在row裏面添加了一個Text
,這在實際開發過程當中是遠遠不夠的,那麼咱們要怎麼去添加別的視圖在row中呢? 這裏就要用到Container
了。話很少說,線上代碼:
Widget _cellForRow(BuildContext context, int index) {
return Container(
color: Colors.grey[100],
margin: EdgeInsets.all(10),
child: Image.network(datas[index].imageUrl),
);
}
複製代碼
這裏的Container
就是指的容器,這一點上來講,咱們能夠類比前端的div
,也能夠類比iOS中的UIView
。包括其中的佈局方式也跟FlexBox
很相似,這一點咱們再下一篇文章中會來針對性的講一下。Container
裏面的實現就不用多說了,設置顏色爲100度灰、設置外邊距統一爲十、設置子視圖爲一張Image
而且是網絡請求的,請求的url是從datas
的數組中取得。
這裏若是要多加一個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
替換成一個children
就好了,這個children
裏面是一個Widget的數組,那麼理論上咱們就能夠無限制的往裏面添加Widget了。而且誰最早執行,哪一個控件就在最上面。
執行的結果是這樣的
除了Column
佈局以外,還有Row佈局和Stack佈局,咱們分別看看效果
上面是Row
佈局的,其實就是橫向的從左至右的佈局方式,這裏圖片太長了,已經把文字都擠出去了。
上面是Stack
佈局的,意思就是說把各個控件層疊起來擺放。
若是你如今要在文字和圖片之間弄一個間距,咱們能夠直接加一個SizeBox
SizedBox(
height: 20,
),
複製代碼
把SizeBox
也加入到children
中去,而且加到文字個圖片之間,這樣文字跟圖片之間就會有間距了。
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,
),
],
),
複製代碼
那麼到這裏,咱們的簡單的項目就算是完成了。接下來咱們來簡單認識幾個經常使用的Widget。
咱們前面介紹了這個控件,那麼若是咱們想要拼接字符串怎麼弄呢?上代碼:
class TextDemo extends StatelessWidget {
final TextStyle _textStyle = TextStyle(
fontSize: 16.0,
);
final String _title = '這是一個標題';
final String _detail = '這是一個內容';
@override
Widget build(BuildContext context) {
return Text(
'《${_title}》-- $_detail。最近Flutter已經瘋狂的刷屏了各個技術博客、技術網站,徹底有一統天下的氣勢。因此最近也決定開始嚐嚐鮮,從零開始一步步的來探索Flutter的世界。就從環境搭建開始,記錄一下本身探索Flutter的過程。',
textAlign: TextAlign.center,
style: _textStyle,
);
}
}
複製代碼
這裏咱們看到這個標題是咱們拼接到這個字符串上面的,因此說拼接的語法就是:
$_title
//或者
${_title}
複製代碼
在Text
中,咱們除了能夠設置textAlign
之外,咱們還能夠設置maxLines
,就是限制最大行數。設置了最大行數以後,若是字數超過了行數,接下來的是不顯示的,如圖所示
Text(
'《${_title}》-- $_detail。最近Flutter已經瘋狂的刷屏了各個技術博客、技術網站,徹底有一統天下的氣勢。因此最近也決定開始嚐嚐鮮,從零開始一步步的來探索Flutter的世界。就從環境搭建開始,記錄一下本身探索Flutter的過程。',
textAlign: TextAlign.center,
style: _textStyle,
maxLines: 3,
overflow: TextOverflow.ellipsis,
);
複製代碼
後面就多了...的符號。
直接線上代碼吧:
RichText(
text: TextSpan(
text: '<這是一個標題>',
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,
)
),
],
),
);
}
複製代碼
最後呈現的效果:
這裏能夠總結一下:
RichText
;children
就能夠了;children
是一個TextSpan
的數組;TextSpan
,而且自定義相應的TextSpan
,來完成富文本的要求。今天咱們從Flutter的基礎的main
函數一直講到經過ListView
去展現一些經常使用的界面,而且還介紹了一些經常使用的Widget,這固然不是所有,看完這個就覺得本身已經徹底搞懂Flutter的只能說太年輕了。後面我還會持續更新。預告一下,不出意外,下一篇應該會給你們介紹一些Flutter的佈局問題。上文中有提到,這裏先賣個關子。若是以爲文章對你有用,能夠幫我點個贊!須要技術交流的,能夠發郵件到個人郵箱:coderspr1nghall@gmail.com
。