Flutter是Google開發的新一代跨平臺方案,Flutter能夠實現寫一份代碼同時運行在iOS和Android設備上,而且提供很好的性能體驗。Flutter使用Dart做爲開發語言,這是一門簡潔、強類型的編程語言。Flutter對於iOS和Android設備,提供了兩套視覺庫,能夠針對不一樣的平臺有不一樣的展現效果。前端
Flutter本來是爲了解決Web開發中的一些問題,而開發的一套精簡版Web框架,擁有獨立的渲染引擎和開發語言,但後來逐漸演變爲移動端開發框架。正是因爲Dart當初的定位是爲了替代JS成爲Web框架,因此Dart的語法更接近於JS語法。例如定義對象構建方法,以及實例化對象的方式等。git
在Google剛推出Flutter時,其發展很緩慢,終於在18年發佈第一個Beta版以後迎來了爆發性增加,發佈第一個Release版時增加速度更快。能夠從Github上Star數據看出來這個增加的過程。在19年最新的Flutter 1.2版本中,已經開放Web支持的Beta版。面試
目前已經有很多大型項目接入Flutter,阿里的閒魚、頭條的抖音、騰訊的NOW直播,都將Flutter當作應用程序的開發語言。除此以外,還有一些其餘中小型公司也在作。express
Flutter能夠理解爲開發SDK或者工具包,其經過Dart做爲開發語言,而且提供Material和Cupertino兩套視覺控件,視圖或其餘和視圖相關的類,都以Widget的形式表現。Flutter有本身的渲染引擎,並不依賴原平生臺的渲染。Flutter還包含一個用C++實現的Engine,渲染也是包含在其中的。編程
Flutter是一套全新的跨平臺方案,Flutter並不像React Native那樣,依賴原生應用的渲染,而是本身有本身的渲染引擎,並使用Dart當作Flutter的開發語言。Flutter總體框架分爲兩層,底層是經過C++實現的引擎部分,Skia是Flutter的渲染引擎,負責跨平臺的圖形渲染。Dart做爲Flutter的開發語言,在C++引擎上層是Dart的Framework。json
Flutter不只僅提供了一套視覺庫,在Flutter總體框架中包含各個層級階段的庫。例如實現一個遊戲功能,上面一些遊戲控件能夠用上層視覺庫,底層遊戲能夠直接基於Flutter的底層庫進行開發,而不須要調用原生應用的底層庫。Flutter的底層庫是基於Open GL實現的,因此Open GL能夠作的Flutter均可以。數組
在上層Framework中包含兩套視覺庫,符合Android風格的Material,和符合iOS風格的Cupertino。也能夠在此基礎上,封裝本身風格的系統組件。Cupertino是一套iOS風格的視覺庫,包含了iOS的導航欄、 button 、 alertView等。性能優化
Flutter對不一樣硬件平臺有不一樣的兼容,例如一樣的Material代碼運行在iOS和Android不一樣平臺上,有一些平臺特有的顯示和交互,Flutter依然對其進行了區分適配。例如滑動ScrollView時,iOS平臺是有回彈效果的,而Android平臺則是阻尼效果。例如iOS的導航欄標題是居中的,Android導航欄標題是向左的,等等。這些Flutter都作了區分兼容。bash
除了Flutter爲咱們作的一些適配外,有一些控件是須要咱們本身作適配的,例如AlertView,在Android和iOS兩個平臺下的表現就是不一樣的。這些iOS特性的控件都定義在Cupertino中,因此建議在進行App開發時,對一些控件進行上層封裝。服務器
例如AlertView則對其進行一個二次封裝,控件內部進行設備判斷並選擇不一樣的視覺庫,這樣能夠保證各個平臺的效果。
雖然Flutter對於iOS和Android兩個平臺,開發有cupertino和material兩個視覺庫,但實際開發過程當中的選擇,應該使用material當作視覺庫。由於Flutter對iOS的支持並非很好,主要對Android平臺支持比較好,material中的UI控件要比cupertino多好幾倍。
Dart是Google在2011年推出的一款應用於Web開發的編程語言,Dart剛推出的時候,定位是替代JS作前端開發,後來逐步擴展到移動端和服務端。
Dart是Flutter的開發語言,Flutter必須遵循Dart的語言特性。在此基礎上,也會有本身的東西,例如Flutter的上層Framework,本身的渲染引擎等。能夠說,Dart只是Flutter的一部分。
Dart是強類型的,對定義的變量不須要聲明其類型,Flutter會對其進行類型推導。若是不想使用類型推導,也能夠本身聲明指定的類型。
Flutter支持亞秒級熱重載,Android Studio和VSCode都支持Hot Reload的特性。但須要區分的是,熱重載和熱更新是不一樣的兩個概念,熱重載是在運行調試狀態下,將新代碼直接更新到執行中的二進制。而熱更新是在上線後,經過Runtime或其餘方式,改變現有執行邏輯。
Flutter支持AOT(Ahead of time)和JIT(Just in time)兩種編譯模式,JIT模式支持在運行過程當中進行Hot Reload。刷新過程是一個增量的過程,由系統對本次和上次的代碼作一次snapshot,將新的代碼注入到DartVM中進行刷新。但有時會不能進行Hot Reload,此時進行一次全量的Hot Reload便可。
而AOT模式則是在運行前預先編譯好,這樣在每次運行過程當中就不須要進行分析、編譯,此模式的運行速度是最快的。Flutter同時採用了兩種方案,在開發階段採用JIT模式進行開發,在release階段採用AOT模式,將代碼打包爲二進制進行發佈。
在開發原生應用時,每次修改代碼後都須要從新編譯,而且運行到硬件設備上。因爲Flutter支持Hot Reload,能夠進行熱重載,對項目的開發效率有很大的提高。
因爲Flutter實現機制支持JIT的緣由,理論上來講是支持熱更新以及服務器下發代碼的。能夠從服務器。可是因爲這樣會使性能變差,並且還有審覈的問題,因此Flutter並無採用這種方案。
Flutter的熱重載是基於State的,也就是咱們在代碼中常常出現的setState方法,經過這個來修改後,會執行相應的build方法,這就是熱重載的基本過程。
Flutter的hot reload的實現源碼在下面路徑中,在此路徑中包含run_cold.dart和run_hot.dart兩個文件,前者負責冷啓動,後者負責熱重載。
1 ~/flutter/packages/flutter_tools/lib/src/run_hot.dart
複製代碼
熱重載的代碼實如今run_hot.dart文件中,有HotRunner來負責具體代碼執行。當Flutter進行熱重載時,會調用restart函數,函數內部會傳入一個fullRestart的bool類型變量。熱重載分爲全量和非全量,fullRestart參數就是表示是否全量。以非全量熱重載爲例,函數的fullRestart會傳入false,根據傳入false參數,下面是部分核心代碼。
1 Future<OperationResult> restart({ bool fullRestart = false, bool pauseAfterRestart = false, String reason }) async {
2 if (fullRestart) {
3 // .....
4 } else {
5 final bool reloadOnTopOfSnapshot = _runningFromSnapshot;
6 final String progressPrefix = reloadOnTopOfSnapshot ? 'Initializing' : 'Performing';
7 final Status status = logger.startProgress(
8 '$progressPrefix hot reload...',
9 progressId: 'hot.reload'
10 );
11 OperationResult result;
12 try {
13 result = await _reloadSources(pause: pauseAfterRestart, reason: reason);
14 } finally {
15 status.cancel();
16 }
17 }
18 }
複製代碼
調用restart函數後,內部會調用_reloadSources函數,去執行內部邏輯。下面是大概邏輯執行流程。
在_reloadSources函數內部,會調用_updateDevFS函數,函數內部會掃描修改的文件,並將文件修改先後進行對比,隨後會將被改動的代碼生成一個kernel files文件。
隨後會經過HTTP Server將生成的kernel files文件發送給Dart VM虛擬機,虛擬機拿到kernel文件後會調用_reloadSources函數進行資源重載,將kernel文件注入正在運行的Dart VM中。當資源重載完成後,會調用RPC接口觸發Widgets的重繪。
如今市面上RN、Weex的技術方案基本同樣,因此這裏就以RN來表明相似的跨平臺方案。Flutter是基於GPU進行渲染的,而RN則將渲染交給原平生臺,而本身只是負責經過JSCore將視圖組織起來,並處理業務邏輯。因此在渲染效果和性能這塊,Flutter的性能比RN要強不少。
跨平臺方案通常都須要對各個平臺進行平臺適配,也就是建立各自平臺的適配層,RN的平臺適配層要比Flutter要大不少。由於從技術實現來講,RN是經過JSCore引擎進行原生代碼調用的,和原生代碼交互不少,因此須要更多的適配。而Flutter則只須要對各自平臺獨有的特性進行適配便可,例如調用系統相冊、粘貼板等。
Flutter技術實現是基於更底層實現的,對平臺依賴度不是很高,相對來講,RN對平臺的依賴度是很高的。因此RN將來的技術升級,包括擴展之類的,都會受到很大的限制。而Flutter將來的潛力將會很大,能夠作不少技術改進。
在Flutter中將顯示以及和顯示相關的部分,都統必定義爲widget,下面列舉一些widget包含的類型:
用於顯示的視圖,例如ListView、Text、Container等。
用來操做視圖,例如Transform等動畫相關。
視圖佈局相關,例如Center、Expanded、Column等。
在Flutter中,全部的視圖都是由Widget組成,Label、AppBar、ViewController等。在Flutter的設計中,組合的優先級要大於繼承,總體視圖類結構繼承層級很淺但單層不少類。若是想定製或封裝一些控件,也應該以組合爲主,而不是繼承。
在iOS開發中,我也常常採用這種設計方案,組合大於繼承。由於若是繼承層級過多的話,一個是不便於閱讀代碼,還有就是很差維護代碼。例如底層須要改一個通用的樣式,但這個類的繼承層級比較複雜,這樣改動的話影響範圍就比較大,會將一些不須要改的也改掉,這時候就會發現繼承很雞肋。但在iOS中有Category的概念,這也是一種組合的方式,能夠經過將一些公共的東西放在Category中,使繼承的方便性和組合的靈活性達到一個平衡。
Flutter中並無單獨的佈局文件,例如iOS的XIB這種,代碼都在Widget中定義。和UIView的區別在於,Widget只是負責描述視圖,並不參與視圖的渲染。UIView也是負責描述視圖,而UIView的layer則負責渲染操做,這是兩者的區別。
在應用程序啓動時,main方法接收一個Widget當作主頁面,因此任何一個Widget均可以當作根視圖。通常都是傳一個MaterialApp,也能夠傳一個Container當作根視圖,這都是被容許的。
在Flutter應用中,和界面顯示及用戶交互的對象都是由Widget構成的,例如視圖、動畫、手勢等。Widget分爲StatelessWidget和StatefulWidget兩種,分別是無狀態和有狀態的Widget。
StatefulWidget本質上也是無狀態的,其經過State來處理Widget的狀態,以達到有狀態,State出如今整個StatefulWidget的生命週期中。
當構建一個Widget時,能夠經過其build得到構建流程,在構建流程中能夠加入本身的定製操做,例如對其設置title或視圖等。
1 return Scaffold(
2 appBar: AppBar(
3 title: Text('ListView Demo'),
4 ),
5 body: ListView.builder(
6 itemCount: dataList.length,
7 itemBuilder: (BuildContext context, int index) {
8 return Text(dataList[index]);
9 },
10 ),
11 );
複製代碼
有些Widget在構建時,也提供一些參數來幫助構建,例如構建一個ListView時,會將index返回給build方法,來區別構建的Cell,以及構建的上下文context。
1 itemBuilder: (BuildContext context, int index) {
2 return Text(dataList[index]);
3 }
複製代碼
StatelessWidget是一種靜態Widget,即建立後自身就不能再進行改變。在建立一個StatelessWidget後,須要重寫build函數。每一個靜態Widget都會有一個build函數,在建立視圖對象時會調用此方法。一樣的,此函數也接收一個Widget類型的返回值。
1 class RectangleWidget extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) {
4 return Center (
5 // UI Code
6 );
7 }
8 }
複製代碼
Widget本質上是不可被改變的,但StatefulWidget將狀態拆分到State中去管理,當數據發生改變時由State去處理視圖的改變。
下面是建立一個動態Widget,當建立一個動態Widget須要配合一個State,而且須要重寫createState方法。重寫此函數後,指定一個Widget對應的State並初始化。
下面例子中,在StatefulWidget的父類中包含一個Key類型的key變量,這是不管靜態Widget仍是動態Widget都具有的參數。在動態Widget中定義了本身的成員變量title,並在自定義的初始化方法中傳入,經過下面DynamicWidget類的構造方法,並不須要在內部手動進行title的賦值,title即爲傳入的值,是由系統完成的。
1 class DynamicWidget extends StatefulWidget {
2 DynamicWidget({Key key, this.title}) : super (key : key);
3 final String title;
4
5 @override
6 DynamicWidgetState createState() => new DynamicWidgetState();
7 }
複製代碼
因爲上面動態Widget定義了初始化方法,在調用動態Widget時能夠直接用自定義初始化方法便可。
1 DynamicWidget(key: 'key', title: 'title');
複製代碼
StatefulWidget的改變是由State來完成的,State中須要重寫build方法,在build中進行視圖組織。StatefulWidget是一種響應式視圖改變的方式,數據源和視圖產生綁定關係,由數據源驅動視圖的改變。
改變StatefulWidget的數據源時,須要調用setState方法,並將數據源改變的操做寫在裏面。使用動態Widget後,是不須要咱們手動去刷新視圖的。系統在setState方法調用後,會從新調用對應Widget的build方法,從新繪製某個Widget。
下面的代碼示例中添加了一個float按鈕,並給按鈕設置了一個回調函數_onPressAction,這樣在每次觸發按鈕事件時都會調用此函數。counter是一個整型變量並和Text相關聯,當counter的值在setState方法中改變時,Text Widget也會跟着變化。
1 class DynamicWidgetState extends State<DynamicWidget> {
2 int counter = 0;
3 void _onPressAction() {
4 setState(() {
5 counter++;
6 });
7 }
8
9 @override
10 Widget build(BuildContext context) {
11 return new Scaffold(
12 body: Center(
13 child: Text('Button tapped $_counter.')
14 ),
15 floatingActionButton: FloatingActionButton(
16 onPressed: _onPressAction,
17 tooltip: 'Increment',
18 child: Icon(Icons.add)
19 )
20 );
21 }
22 }
複製代碼
在iOS中有UINavigationController的概念,其並不負責顯示,而是負責控制各個頁面的跳轉操做。在Flutter中能夠將MaterialApp理解爲iOS的導航控制器,其包含一個navigationBar以及導航棧,這和iOS是同樣的。
在iOS中除了用來顯示的視圖外,視圖還有對應的UIViewController。在Flutter中並無專門用來管理視圖而且和View一對一的類,但從顯示的角度來講,有相似的類Scaffold,其包含控制器的appBar,也能夠經過body設置一個widget當作其視圖。
theme是Flutter提供的界面風格API,MaterialApp提供有theme屬性,能夠在MaterialApp中設置全局樣式,這樣能夠統一整個應用的風格。
1 new MaterialApp(
2 title: title,
3 theme: new ThemeData(
4 brightness: Brightness.dark,
5 primaryColor: Colors.lightBlue[800],
6 accentColor: Colors.cyan[600],
7 )
8 );
複製代碼
若是不想使用系統默認主題,能夠將對應的控件或試圖用Theme包起來,並將Theme當作Widget賦值給其餘Widget。
1 new Theme(
2 data: new ThemeData(
3 accentColor: Colors.yellow,
4 ),
5 child: new FloatingActionButton(
6 onPressed: () {},
7 child: new Icon(Icons.add),
8 ),
9 );
複製代碼
有時MaterialApp設定的統一風格,並不能知足某個Widget的要求,可能還須要有其餘的外觀變化,能夠經過Theme.of傳入當前的BuildContext,來對theme進行擴展。
Flutter會根據傳入的context,順着Widget樹查找最近的Theme,並對Theme複製一份防止影響原有的Theme,並對其進行擴展。
1 new Theme(
2 data: Theme.of(context).copyWith(accentColor: Colors.yellow),
3 child: new FloatingActionButton(
4 onPressed: null,
5 child: new Icon(Icons.add),
6 ),
7 );
複製代碼
Flutter中能夠經過async、await組合使用,進行網絡請求。Flutter中的網絡請求大致有三種:
系統自帶的HttpClient網絡請求,缺點是代碼量相對而言比較多,並且對post請求支持不是很好。
三方庫http.dart,請求簡單。
三方庫dio,請求簡單。
http網絡庫定義在http.dart中,內部代碼定義很是全面,其中包括HttpStatus、HttpHeaders、Cookie等不少基礎信息,有助於咱們瞭解http請求協議。
由於是三方庫,因此須要在pubspec.yaml中加入下面的引用。
1 http: '>=0.11.3+12'
複製代碼
下面是http.dart的請求示例代碼,能夠看到請求很簡單,真正的請求代碼其實就兩行。生成一個Client請求對象,調用client實例的get方法(若是是post則調用post方法),並用Response對象去接收請求結果便可。
經過async修飾發起請求的方法,表示這是一個異步操做,並在請求代碼的前面加入await,修飾這裏的代碼須要等待數據返回,須要過一段時間後再處理。
請求回來的數據默認是json字符串,須要對其進行decode並解析爲數據對象纔可使用,這裏使用系統自帶的convert庫進行解析,並解析爲數組。
1 import 'package:http/http.dart' as http;
2
3 class RequestDemoState extends State<MyHomePage> {
4 List dataList = [];
5
6 @override
7 void initState() {
8 super.initState();
9 loadData();
10 }
11
12 // 發起網絡請求
13 loadData() async{
14 String requestURL = 'https://jsonplaceholder.typicode.com/posts';
15 Client client = Client();
16 Response response = await client.get(requestURL);
17
18 String jsonString = response.body;
19 setState(() {
20 // 數據解析
21 dataList = json.decode(jsonString);
22 });
23 }
24
25 @override
26 Widget build(BuildContext context) {
27 return Scaffold(
28 appBar: AppBar(
29 title: Text(widget.title)
30 ),
31 body: ListView.builder(
32 itemCount: dataList.length,
33 itemBuilder: (BuildContext context, int index) {
34 return Text(dataList[index]['title']);
35 },
36 ),
37 );
38 }
39 }
複製代碼
在調用Client進行post數據請求時,須要傳入一個字典進去,Client會經過將字典當作post的from表單。
1 void requestData() async {
2 var params = Map<String, String>();
3 params["username"] = "lxz";
4 params["password"] = "123456";
5
6 var client = http.Client();
7 var response = await client.post(url_post, body: params);
8 _content = response.body;
9 }
複製代碼
dio庫的調用方式和http庫相似,這裏不過多介紹。dio庫相對於http庫強大的在於,dio庫提供了更好的Cookie管理、文件的上傳下載、fromData表單等處理。因此,若是對網絡庫需求比較複雜的話,仍是建議使用dio。
1 // 引入外部依賴
2 dio: ^1.0.9
複製代碼
系統自帶有convert解析庫,在使用時直接import便可。convert相似於iOS自帶的JSON解析類NSJSONSerialization,能夠直接將json字符串解析爲字典或數組。
1 import 'dart:convert';
2 // 解析代碼
3 dataList = json.decode(jsonString);
複製代碼
可是,咱們在項目中使用時,通常都不會直接使用字典取值,這是一種很很差的作法。通常都會將字典或數組轉換爲模型對象,在項目中使用模型對象。能夠定義相似Model.dart 這樣的模型類,並在模型類中進行數據解析,對外直接暴露公共變量來讓外界獲取值。
但若是定義模型類的話,一個是要在代碼內部寫取值和賦值代碼,這些都須要手動完成。另外若是當服務端字段發生改變後,客戶端也須要跟着進行改變,因此這種方式並非很靈活。
能夠採用json序列化的三方庫json_serializable,此庫能夠將一個類標示爲自動JSON序列化的類,並對類提供JSON和對象相互轉換的能力。也能夠經過命令行開啓一個watch,當類中的變量定義發生改變時,相關代碼自動發生改變。
首先引入下面的三個庫,其中包括依賴庫一個,以及調試庫兩個。
1 dependencies:
2 json_annotation: ^2.0.0
3
4 dev_dependencies:
5 build_runner: ^1.0.0
6 json_serializable: ^2.0.0
複製代碼
定義一個模型文件,例如這裏叫作User.dart文件,並在內部定義一個User的模型類。隨後引入json_annotation的依賴,經過@JsonSerializable()標示此類須要被json_serializable進行合成。
定義的User類包含兩部分,實例變量和兩個轉換函數。在下面定義json轉換函數時,須要注意函數命名必定要按照下面格式命名,不然不能正常生成user.g.dart文件。
1 import 'package:json_annotation/json_annotation.dart';
2
3 // 定義合成後的新文件爲user.g.dart
4 part 'user.g.dart';
5
6 @JsonSerializable()
7
8 class User {
9 String name;
10 int age;
11 String email;
12
13 factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
14 Map<String, dynamic> toJson() => _$UserToJson(this);
15 }
複製代碼
下面就是user.dart指定生成的user.g.dart文件,其中包含JSON和對象相互轉換的代碼。
1 part of 'user.dart';
2
3 User _$UserFromJson(Map<String, dynamic> json) {
4 return User(
5 json['name'] as String, json['age'] as int, json['email'] as String);
6 }
7
8 Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
9 'name': instance.name,
10 'age': instance.age,
11 'email': instance.email
12 };
複製代碼
有的時候服務端返回的參數名和本地的關鍵字衝突,或者命名不規範,致使本地定義和服務器字段不一樣的狀況。這種狀況能夠經過@JsonKey關鍵字,來修飾json字段匹配新的本地變量。除此以外,也能夠作其餘修飾,例如變量不能爲空等。
1 @JsonKey(name: 'id')
2 final int user_id;
複製代碼
如今項目中依然是報錯的,隨後咱們在flutter工程的根目錄文件夾下,運行下面命令。
1 flutter packages pub run build_runner watch
複製代碼
此命令的好處在於,其會在後臺監聽模型類的定義,當模型類定義發生改變後,會自動修改本地源碼以適配新的定義。以文中User類爲例,當User.dart文件發生改變後,使用Cmd+s保存文件,隨後VSCode會將自定改變user.g.dart文件的定義,以適配新的變量定義。
iOS文件:iOS工程文件
Android:Android工程文件
lib:Flutter的dart代碼
assets:資源文件夾,例如font、image等均可以放在裏面
.gitignore:git忽略文件
這是一個系統文件,Flutter經過.packages文件來管理一些系統依賴庫,例如material、cupertino、widgets、animation、gesture等系統庫就在裏面,這些主要的系統庫由.packages下的flutter統一管理,源碼都在flutter/lib/scr目錄下。除此以外,還有一些其餘的系統庫或系統資源都在.packages中。
在Flutter中經過pubspec.yaml文件來管理外部引用,包含本地資源文件、字體文件、依賴庫等依賴,以及應用的一些配置信息。這些配置在項目中時,須要注意代碼對其的問題,不然會致使加載失敗。
當修改yaml文件的依賴信息後,須要執行flutter get packages命令更新本地文件。但並不須要這麼麻煩,能夠直接Cmd+s保存文件,VSCode編譯器會自動更新依賴。
1 // 項目配置信息
2 name: WeChat
3 description: Tencent WeChat App.
4 version: 1.0.0+1
5
6 // 常規依賴
7 dependencies:
8 flutter:125864
9 sdk: flutter
10 cupertino_icons: ^0.1.2
11 english_words: ^3.1.0
12
13 // 開發依賴
14 dev_dependencies:
15 flutter_test:
16 sdk: flutter
17
18 flutter:
19 uses-material-design: true
20 // 圖片依賴
21 assets:
22 - assets/images/ic_file_transfer.png
23 - assets/images/ic_fengchao.png
24
25 // 字體依賴
26 fonts:
27 - family: appIconFont
28 fonts:
29 - asset: assets/fonts/iconfont.ttf
複製代碼
和大多數編程語言同樣,dart也包含一個main方法,是Flutter程序執行的主入口,在main方法中寫的代碼就是在程序啓動時執行的代碼。main方法中會執行runApp方法,runApp方法相似於iOS的UIApplicationMain方法,runApp函數接收一個Widget用來作應用程序的顯示。
1 void main() {
2 runApp()
3 // code
4 }
複製代碼
在iOS中經過AppDelegate能夠獲取應用程序的生命週期回調,在Flutter中也能夠獲取到。能夠經過向Binding添加一個Observer,並實現didChangeAppLifecycleState方法,來監聽指定事件的到來。
可是因爲Flutter提供的狀態有限,在iOS平臺只能監聽三種狀態,下面是示例代碼。
1 class LifeCycleDemoState extends State<MyHomePage> with WidgetsBindingObserver {
2 @override
3 void initState() {
4 super.initState();
5 WidgetsBinding.instance.addObserver(this);
6 }
7
8 @override
9 void didChangeAppLifecycleState(AppLifecycleState state) {
10 super.didChangeAppLifecycleState(state);
11
12 switch (state) {
13 case AppLifecycleState.inactive:
14 print('Application Lifecycle inactive');
15 break;
16 case AppLifecycleState.paused:
17 print('Application Lifecycle paused');
18 break;
19 case AppLifecycleState.resumed:
20 print('Application Lifecycle resumed');
21 break;
22 default:
23 print('Application Lifecycle other');
24 }
25 }
26 }
複製代碼
在Flutter中是支持矩陣變化的,例如rotate、scale等方式。Flutter的矩陣變換由Widget完成,須要進行矩陣變換的視圖,在外面包一層Transform Widget便可,內部能夠設置其變換方式。
1 child: Container(
2 child: Transform(
3 child: Container(
4 child: Text(
5 "Lorem ipsum",
6 style: TextStyle(color: Colors.orange[300], fontSize: 12.0),
7 textAlign: TextAlign.center,
8 ),
9 decoration: BoxDecoration(
10 color: Colors.red[400],
11 ),
12 padding: EdgeInsets.all(16.0),
13 ),
14 alignment: Alignment.center,
15 transform: Matrix4.identity()
16 ..rotateZ(15 * 3.1415927 / 180),
17 ),
18 width: 320.0,
19 height: 240.0,
20 color: Colors.grey[300],
21 )
複製代碼
在Transform中能夠經過transform指定其矩陣變換方式,經過alignment指定變換的錨點。
在iOS中能夠經過UINavigationController對頁面進行管理,控制頁面間的push、pop跳轉。Flutter中使用Navigator和Routers來實現相似UINavigationController的功能,Navigator負責管理導航棧,包含push、pop的操做,能夠把UIViewController看作一個Routers,Routers被Navigator管理着。
Navigator的跳轉方式分爲兩種,一種是直接跳轉到某個Widget頁面,另外一種是爲MaterialApp構建一個map,經過key來跳轉對應的Widget頁面。map的格式是key : context的形式。
1 void main() {
2 runApp(MaterialApp(
3 home: MyAppHome(), // becomes the route named '/'
4 routes: <String, WidgetBuilder> {
5 '/a': (BuildContext context) => MyPage(title: 'page A'),
6 '/b': (BuildContext context) => MyPage(title: 'page B'),
7 '/c': (BuildContext context) => MyPage(title: 'page C'),
8 },
9 ));
10 }
複製代碼
跳轉時經過pushNamed指定map中的key,便可跳轉到對應的Widget。若是須要從push出來的頁面獲取參數,能夠經過await修飾push操做,這樣便可在新頁面pop的時候將參數返回到當前頁面。
1 Navigator.of(context).pushNamed('/b');
2
3 Map coordinates = await Navigator.of(context).pushNamed('/location');
4 Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});
複製代碼
VSCode有很好的語法檢查,若是有命名不規範等問題,都會以警告的形式表現出來。
駝峯命名法,方法名、變量名等,都以首字母小寫的駝峯命名法。類名也是駝峯命名法,但類名首字母大寫。
文件名,文件命名如下劃線進行區分,不使用駝峯命名法。
Flutter中建立Widget對象,能夠用new修飾,也能夠不用。
1 child: new Container(
2 child: Text(
3 'Hello World',
4 style: TextStyle(color: Colors.orange, fontSize: 15.0)
5 )
6 )
複製代碼
函數中能夠定義可選參數,以及必要參數。
下面是一個函數定義,這裏定義了一個必要參數url,以及一個Map類型的可選參數headers。
1 Future<Response> get(url, {Map<String, String> headers});
複製代碼
Dart中在函數定義前加下劃線,則表示是私有方法或變量。
Dart經過import引入外部引用,除此以外也能夠經過下面的語法單獨引入文件中的某部分。
1 import "dart:collection" show HashMap, IterableBase;
複製代碼
在Dart中常常能夠看到=>的調用方式,這種調用方式相似於一種語法糖,下面是一些經常使用的調用方式。
當進行函數調用時,能夠將普通函數調用轉換爲=>的調用方式,例以下面第一個示例。在此基礎上,若是調用函數只有一個參數,能夠將其改成第二個示例的方式,也就是能夠省略調用的括號,直接寫參數名。
1 (單一參數) => {函數聲明}
2 elements.map((element) => {
3 return element.length;
4 });
5
6 單一參數 => {函數聲明}
7 elements.map(element => {
8 return element.length;
9 });
複製代碼
當只有一個返回值,而且沒有邏輯處理時,能夠直接省略return,返回數值。
1 (參數1, 參數2, …, 參數N) => 表達式
2 elements.map(element => element.length);
複製代碼
當調用的函數中沒有參數時,能夠直接省略參數,寫一對空括號便可。
1 () => {函數實現}
複製代碼
VSCode支持對Dart語言進行重構,通常做用範圍都是在函數內小範圍的。
例如在建立Widget對象的地方,將鼠標焦點放在這裏,當前行的最前面會有提示。點擊提示後會有下面兩個選項:
Extract Local Variable將當前Widget及其子Widget建立的代碼,剝離到一個變量中,並在當前位置使用這個變量。
Extract Method將當前Widget及其子Widget建立的代碼,封裝到一個函數中,並在當前位置調用此函數。
除此以外,將鼠標焦點放在方法的一行,點擊最前面的提示,會出現下面兩個選項:
Convert to expression body將當前函數轉換爲一個表達式。
Convert to async function body將當前函數轉換爲一個異步線程中執行的代碼。
在Dart中添加任何附加效果,例如動畫效果或矩陣轉換,除了直接給Widget子類的屬性賦值外,就是在被當前Widget外面包一層,就可使當前Widget擁有對應的效果。
1 // 動畫效果
2 floatingActionButton: FloatingActionButton(
3 tooltip: 'Fade',
4 child: Icon(Icons.brush),
5 onPressed: () {
6 controller.forward();
7 },
8),
9
10 // 矩陣轉換
11 Transform(
12 child: Container(
13 child: Text(
14 "Lorem ipsum",
15 style: TextStyle(color: Colors.orange[300], fontSize: 12.0),
16 textAlign: TextAlign.center,
17 )
18 ),
19 alignment: Alignment.center,
20 transform: Matrix4.identity()
21 ..rotateZ(15 * 3.1415927 / 180),
22 ),
複製代碼
Cmd + Shift + p:能夠進行快速搜索。須要注意的是,默認是帶有一個>的,這樣搜索結果主要是dart代碼。若是想搜索其餘配置文件,或者安裝插件等操做,須要把>去掉。
Cmd + Shift + o:能夠在某個文件中搜索某個類,但前提是須要提早進入這個文件。例如進入framework.dart,搜索StatefulWidget類。
使用Flutter要注意代碼縮進,若是縮進有問題可能會影響最後的結果,尤爲是在.yaml中寫配置文件的時候。
由於 Flutter是開源的,因此遇到問題後能夠進入源碼中,找解決方案。
在代碼中要注意標點符號的使用,例如第二個建立Stack的代碼,若是上面是以逗號結尾,則後面的建立會失敗,若是上面是以分號結尾則沒問題。
1 Widget unreadMsgText = Container(
2 width: Constants.UnreadMsgNotifyDotSize,
3 height: Constants.UnreadMsgNotifyDotSize,
4 child: Text(
5 conversation.unreadMsgCount.toString(),
6 style: TextStyle(
7 color: Color(AppColors.UnreadMsgNotifyTextColor),
8 fontSize: 12.0
9 ),
10 ),
11 );
12
13 avatarContainer = Stack(
14 overflow: Overflow.visible,
15 children: <Widget>[
16 avatar
17 ],
18 );
複製代碼
最後送福利了,如今關注我而且加入羣聊能夠獲取包含源碼解析,自定義View,動畫實現,架構分享等。 內容難度適中,篇幅精煉,天天只需花上十幾分鍾閱讀便可。你們能夠跟我一塊兒探討,歡迎加羣探討,有flutter—性能優化—移動架構—資深UI工程師—NDK相關專業人員和視頻教學資料,還有更多面試題等你來拿~ Android開發交流羣:1018342383