Flutter
是Google
開發的新一代跨平臺方案,Flutter
能夠實現寫一份代碼同時運行在iOS和Android設備上,而且提供很好的性能體驗。Flutter
使用Dart
做爲開發語言,這是一門簡潔、強類型的編程語言。Flutter
對於iOS和Android設備,提供了兩套視覺庫,能夠針對不一樣的平臺有不一樣的展現效果。前端
Flutter
本來是爲了解決Web
開發中的一些問題,而開發的一套精簡版Web
框架,擁有獨立的渲染引擎和開發語言,但後來逐漸演變爲移動端開發框架。正是因爲Dart
當初的定位是爲了替代JS
成爲Web
框架,因此Dart
的語法更接近於JS
語法。例如定義對象構建方法,以及實例化對象的方式等。git
在Google
剛推出Flutter
時,其發展很緩慢,終於在18年發佈第一個Bate版以後迎來了爆發性增加,發佈第一個Release
版時增加速度更快。能夠從Github上Star數據看出來這個增加的過程。在19年最新的Flutter 1.2
版本中,已經開放Web
支持的Beta版。github
目前已經有很多大型項目接入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
等。bash
Flutter
對不一樣硬件平臺有不一樣的兼容,例如一樣的Material
代碼運行在iOS和Android不一樣平臺上,有一些平臺特有的顯示和交互,Flutter
依然對其進行了區分適配。例如滑動ScrollView
時,iOS平臺是有回彈效果的,而Android平臺則是阻尼效果。例如iOS的導航欄標題是居中的,Android導航欄標題是向左的,等等。這些Flutter
都作了區分兼容。服務器
除了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
兩個文件,前者負責冷啓動,後者負責熱重載。
~/flutter/packages/flutter_tools/lib/src/run_hot.dart
複製代碼
熱重載的代碼實如今run_hot.dart
文件中,有HotRunner
來負責具體代碼執行。當Flutter
進行熱重載時,會調用restart
函數,函數內部會傳入一個fullRestart
的bool
類型變量。熱重載分爲全量和非全量,fullRestart
參數就是表示是否全量。以非全量熱重載爲例,函數的fullRestart
會傳入false
,根據傳入false
參數,下面是部分核心代碼。
Future<OperationResult> restart({ bool fullRestart = false, bool pauseAfterRestart = false, String reason }) async {
if (fullRestart) {
// .....
} else {
final bool reloadOnTopOfSnapshot = _runningFromSnapshot;
final String progressPrefix = reloadOnTopOfSnapshot ? 'Initializing' : 'Performing';
final Status status = logger.startProgress(
'$progressPrefix hot reload...',
progressId: 'hot.reload'
);
OperationResult result;
try {
result = await _reloadSources(pause: pauseAfterRestart, reason: reason);
} finally {
status.cancel();
}
}
}
複製代碼
調用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或視圖等。
return Scaffold(
appBar: AppBar(
title: Text('ListView Demo'),
),
body: ListView.builder(
itemCount: dataList.length,
itemBuilder: (BuildContext context, int index) {
return Text(dataList[index]);
},
),
);
複製代碼
有些Widget
在構建時,也提供一些參數來幫助構建,例如構建一個ListView
時,會將index
返回給build
方法,來區別構建的Cell,以及構建的上下文context
。
itemBuilder: (BuildContext context, int index) {
return Text(dataList[index]);
}
複製代碼
StatelessWidget
是一種靜態Widget
,即建立後自身就不能再進行改變。在建立一個StatelessWidget
後,須要重寫build
函數。每一個靜態Widget
都會有一個build
函數,在建立視圖對象時會調用此方法。一樣的,此函數也接收一個Widget
類型的返回值。
class RectangleWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center (
// UI Code
);
}
}
複製代碼
Widget
本質上是不可被改變的,但StatefulWidget
將狀態拆分到State
中去管理,當數據發生改變時由State
去處理視圖的改變。
下面是建立一個動態Widget
,當建立一個動態Widget
須要配合一個State
,而且須要重寫createState
方法。重寫此函數後,指定一個Widget
對應的State
並初始化。
下面例子中,在StatefulWidget
的父類中包含一個Key類型的key
變量,這是不管靜態Widget
仍是動態Widget
都具有的參數。在動態Widget
中定義了本身的成員變量title,並在自定義的初始化方法中傳入,經過下面DynamicWidget
類的構造方法,並不須要在內部手動進行title的賦值,title即爲傳入的值,是由系統完成的。
class DynamicWidget extends StatefulWidget {
DynamicWidget({Key key, this.title}) : super (key : key);
final String title;
@override
DynamicWidgetState createState() => new DynamicWidgetState();
}
複製代碼
因爲上面動態Widget
定義了初始化方法,在調用動態Widget
時能夠直接用自定義初始化方法便可。
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
也會跟着變化。
class DynamicWidgetState extends State<DynamicWidget> {
int counter = 0;
void _onPressAction() {
setState(() {
counter++;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
body: Center(
child: Text('Button tapped $_counter.')
),
floatingActionButton: FloatingActionButton(
onPressed: _onPressAction,
tooltip: 'Increment',
child: Icon(Icons.add)
)
);
}
}
複製代碼
在iOS中有UINavigationController
的概念,其並不負責顯示,而是負責控制各個頁面的跳轉操做。在Flutter
中能夠將MaterialApp
理解爲iOS的導航控制器,其包含一個navigationBar
以及導航棧,這和iOS是同樣的。
在iOS中除了用來顯示的視圖外,視圖還有對應的UIViewController
。在Flutter
中並無專門用來管理視圖而且和View
一對一的類,但從顯示的角度來講,有相似的類Scaffold
,其包含控制器的appBar
,也能夠經過body
設置一個widget
當作其視圖。
theme
是Flutter
提供的界面風格API,MaterialApp
提供有theme
屬性,能夠在MaterialApp
中設置全局樣式,這樣能夠統一整個應用的風格。
new MaterialApp(
title: title,
theme: new ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.lightBlue[800],
accentColor: Colors.cyan[600],
)
);
複製代碼
若是不想使用系統默認主題,能夠將對應的控件或試圖用Theme
包起來,並將Theme
當作Widget
賦值給其餘Widget
。
new Theme(
data: new ThemeData(
accentColor: Colors.yellow,
),
child: new FloatingActionButton(
onPressed: () {},
child: new Icon(Icons.add),
),
);
複製代碼
有時MaterialApp
設定的統一風格,並不能知足某個Widget
的要求,可能還須要有其餘的外觀變化,能夠經過Theme.of
傳入當前的BuildContext
,來對theme
進行擴展。
Flutter
會根據傳入的context
,順着Widget
樹查找最近的Theme
,並對Theme
複製一份防止影響原有的Theme
,並對其進行擴展。
new Theme(
data: Theme.of(context).copyWith(accentColor: Colors.yellow),
child: new FloatingActionButton(
onPressed: null,
child: new Icon(Icons.add),
),
);
複製代碼
Flutter
中能夠經過async
、await
組合使用,進行網絡請求。Flutter
中的網絡請求大致有三種:
HttpClient
網絡請求,缺點是代碼量相對而言比較多,並且對post請求支持不是很好。http.dart
,請求簡單。dio
,請求簡單。http
網絡庫定義在http.dart
中,內部代碼定義很全,包括HttpStatus
、HttpHeaders
、Cookie
等不少基礎信息,有助於咱們瞭解http
請求協議。
由於是三方庫,因此須要在pubspec.yaml
中加入下面的引用。
http: '>=0.11.3+12'
複製代碼
下面是http.dart
的請求示例代碼,能夠看到請求很簡單,真正的請求代碼其實就兩行。生成一個Client
請求對象,調用client
實例的get方法(若是是post則調用post方法),並用Response
對象去接收請求結果便可。
經過async
修飾發起請求的方法,表示這是一個異步操做,並在請求代碼的前面加入await
,修飾這裏的代碼須要等待數據返回,須要過一段時間後再處理。
請求回來的數據默認是json
字符串,須要對其進行decode
並解析爲數據對象纔可使用,這裏使用系統自帶的convert
庫進行解析,並解析爲數組。
import 'package:http/http.dart' as http;
class RequestDemoState extends State<MyHomePage> {
List dataList = [];
@override
void initState() {
super.initState();
loadData();
}
// 發起網絡請求
loadData() async{
String requestURL = 'https://jsonplaceholder.typicode.com/posts';
Client client = Client();
Response response = await client.get(requestURL);
String jsonString = response.body;
setState(() {
// 數據解析
dataList = json.decode(jsonString);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title)
),
body: ListView.builder(
itemCount: dataList.length,
itemBuilder: (BuildContext context, int index) {
return Text(dataList[index]['title']);
},
),
);
}
}
複製代碼
在調用Client
進行post數據請求時,須要傳入一個字典進去,Client
會經過將字典當作post的from
表單。
void requestData() async {
var params = Map<String, String>();
params["username"] = "lxz";
params["password"] = "123456";
var client = http.Client();
var response = await client.post(url_post, body: params);
_content = response.body;
}
複製代碼
dio
庫的調用方式和http
庫相似,這裏不過多介紹。dio
庫相對於http
庫強大的在於,dio
庫提供了更好的Cookie
管理、文件的上傳下載、fromData
表單等處理。因此,若是對網絡庫需求比較複雜的話,仍是建議使用dio
。
// 引入外部依賴
dio: ^1.0.9
複製代碼
系統自帶有convert
解析庫,在使用時直接import
便可。convert
相似於iOS自帶的JSON解析類NSJSONSerialization
,能夠直接將json字符串解析爲字典或數組。
import 'dart:convert';
// 解析代碼
dataList = json.decode(jsonString);
複製代碼
可是,咱們在項目中使用時,通常都不會直接使用字典取值,這是一種很很差的作法。通常都會將字典或數組轉換爲模型對象,在項目中使用模型對象。能夠定義相似Model.dart
這樣的模型類,並在模型類中進行數據解析,對外直接暴露公共變量來讓外界獲取值。
但若是定義模型類的話,一個是要在代碼內部寫取值和賦值代碼,這些都須要手動完成。另外若是當服務端字段發生改變後,客戶端也須要跟着進行改變,因此這種方式並非很靈活。
能夠採用json序列化的三方庫json_serializable
,此庫能夠將一個類標示爲自動JSON序列化的類,並對類提供JSON和對象相互轉換的能力。也能夠經過命令行開啓一個watch
,當類中的變量定義發生改變時,相關代碼自動發生改變。
首先引入下面的三個庫,其中包括依賴庫一個,以及調試庫兩個。
dependencies:
json_annotation: ^2.0.0
dev_dependencies:
build_runner: ^1.0.0
json_serializable: ^2.0.0
複製代碼
定義一個模型文件,例如這裏叫作User.dart
文件,並在內部定義一個User
的模型類。隨後引入json_annotation
的依賴,經過@JsonSerializable()
標示此類須要被json_serializable
進行合成。
定義的User
類包含兩部分,實例變量和兩個轉換函數。在下面定義json轉換函數時,須要注意函數命名必定要按照下面格式命名,不然不能正常生成user.g.dart
文件。
import 'package:json_annotation/json_annotation.dart';
// 定義合成後的新文件爲user.g.dart
part 'user.g.dart';
@JsonSerializable()
class User {
String name;
int age;
String email;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
複製代碼
下面就是user.dart
指定生成的user.g.dart
文件,其中包含JSON和對象相互轉換的代碼。
part of 'user.dart';
User _$UserFromJson(Map<String, dynamic> json) {
return User(
json['name'] as String, json['age'] as int, json['email'] as String);
}
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
'name': instance.name,
'age': instance.age,
'email': instance.email
};
複製代碼
有的時候服務端返回的參數名和本地的關鍵字衝突,或者命名不規範,致使本地定義和服務器字段不一樣的狀況。這種狀況能夠經過@JsonKey
關鍵字,來修飾json字段匹配新的本地變量。除此以外,也能夠作其餘修飾,例如變量不能爲空等。
@JsonKey(name: 'id')
final int user_id;
複製代碼
如今項目中依然是報錯的,隨後咱們在flutter
工程的根目錄文件夾下,運行下面命令。
flutter packages pub run build_runner watch
複製代碼
此命令的好處在於,其會在後臺監聽模型類的定義,當模型類定義發生改變後,會自動修改本地源碼以適配新的定義。以文中User
類爲例,當User.dart
文件發生改變後,使用Cmd+s
保存文件,隨後VSCode會將自定改變user.g.dart
文件的定義,以適配新的變量定義。
這是一個系統文件,Flutter
經過.packages
文件來管理一些系統依賴庫,例如material
、cupertino
、widgets
、animation
、gesture
等系統庫就在裏面,這些主要的系統庫由.packages
下的flutter
統一管理,源碼都在flutter/lib/scr
目錄下。除此以外,還有一些其餘的系統庫或系統資源都在.packages
中。
在Flutter
中經過pubspec.yaml
文件來管理外部引用,包含本地資源文件、字體文件、依賴庫等依賴,以及應用的一些配置信息。這些配置在項目中時,須要注意代碼對其的問題,不然會致使加載失敗。
當修改yaml
文件的依賴信息後,須要執行flutter get packages
命令更新本地文件。但並不須要這麼麻煩,能夠直接Cmd+s
保存文件,VSCode編譯器會自動更新依賴。
// 項目配置信息
name: WeChat
description: Tencent WeChat App.
version: 1.0.0+1
// 常規依賴
dependencies:
flutter:125864
sdk: flutter
cupertino_icons: ^0.1.2
english_words: ^3.1.0
// 開發依賴
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
// 圖片依賴
assets:
- assets/images/ic_file_transfer.png
- assets/images/ic_fengchao.png
// 字體依賴
fonts:
- family: appIconFont
fonts:
- asset: assets/fonts/iconfont.ttf
複製代碼
和大多數編程語言同樣,dart
也包含一個main
方法,是Flutter
程序執行的主入口,在main
方法中寫的代碼就是在程序啓動時執行的代碼。main
方法中會執行runApp
方法,runApp
方法相似於iOS的UIApplicationMain
方法,runApp
函數接收一個Widget
用來作應用程序的顯示。
void main() {
runApp()
// code
}
複製代碼
在iOS中經過AppDelegate
能夠獲取應用程序的生命週期回調,在Flutter
中也能夠獲取到。能夠經過向Binding
添加一個Observer
,並實現didChangeAppLifecycleState
方法,來監聽指定事件的到來。
可是因爲Flutter
提供的狀態有限,在iOS平臺只能監聽三種狀態,下面是示例代碼。
class LifeCycleDemoState extends State<MyHomePage> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.inactive:
print('Application Lifecycle inactive');
break;
case AppLifecycleState.paused:
print('Application Lifecycle paused');
break;
case AppLifecycleState.resumed:
print('Application Lifecycle resumed');
break;
default:
print('Application Lifecycle other');
}
}
}
複製代碼
在Flutter
中是支持矩陣變化的,例如rotate
、scale
等方式。Flutter
的矩陣變換由Widget
完成,須要進行矩陣變換的視圖,在外面包一層Transform Widget
便可,內部能夠設置其變換方式。
child: Container(
child: Transform(
child: Container(
child: Text(
"Lorem ipsum",
style: TextStyle(color: Colors.orange[300], fontSize: 12.0),
textAlign: TextAlign.center,
),
decoration: BoxDecoration(
color: Colors.red[400],
),
padding: EdgeInsets.all(16.0),
),
alignment: Alignment.center,
transform: Matrix4.identity()
..rotateZ(15 * 3.1415927 / 180),
),
width: 320.0,
height: 240.0,
color: Colors.grey[300],
)
複製代碼
在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
的形式。
void main() {
runApp(MaterialApp(
home: MyAppHome(), // becomes the route named '/'
routes: <String, WidgetBuilder> {
'/a': (BuildContext context) => MyPage(title: 'page A'),
'/b': (BuildContext context) => MyPage(title: 'page B'),
'/c': (BuildContext context) => MyPage(title: 'page C'),
},
));
}
複製代碼
跳轉時經過pushNamed
指定map
中的key
,便可跳轉到對應的Widget
。若是須要從push出來的頁面獲取參數,能夠經過await
修飾push操做,這樣便可在新頁面pop的時候將參數返回到當前頁面。
Navigator.of(context).pushNamed('/b');
Map coordinates = await Navigator.of(context).pushNamed('/location');
Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});
複製代碼
VSCode有很好的語法檢查,若是有命名不規範等問題,都會以警告的形式表現出來。
Flutter
中建立Widget
對象,能夠用new
修飾,也能夠不用。child: new Container(
child: Text(
'Hello World',
style: TextStyle(color: Colors.orange, fontSize: 15.0)
)
)
複製代碼
下面是一個函數定義,這裏定義了一個必要參數url
,以及一個Map
類型的可選參數headers
。
Future<Response> get(url, {Map<String, String> headers});
複製代碼
Dart
中在函數定義前加下劃線,則表示是私有方法或變量。Dart
經過import
引入外部引用,除此以外也能夠經過下面的語法單獨引入文件中的某部分。import "dart:collection" show HashMap, IterableBase;
複製代碼
在Dart
中常常能夠看到=>
的調用方式,這種調用方式相似於一種語法糖,下面是一些經常使用的調用方式。
當進行函數調用時,能夠將普通函數調用轉換爲=>
的調用方式,例以下面第一個示例。在此基礎上,若是調用函數只有一個參數,能夠將其改成第二個示例的方式,也就是能夠省略調用的括號,直接寫參數名。
(單一參數) => {函數聲明}
elements.map((element) => {
return element.length;
});
單一參數 => {函數聲明}
elements.map(element => {
return element.length;
});
複製代碼
當只有一個返回值,而且沒有邏輯處理時,能夠直接省略return
,返回數值。
(參數1, 參數2, …, 參數N) => 表達式
elements.map(element => element.length);
複製代碼
當調用的函數中沒有參數時,能夠直接省略參數,寫一對空括號便可。
() => {函數實現}
複製代碼
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
擁有對應的效果。
// 動畫效果
floatingActionButton: FloatingActionButton(
tooltip: 'Fade',
child: Icon(Icons.brush),
onPressed: () {
controller.forward();
},
),
// 矩陣轉換
Transform(
child: Container(
child: Text(
"Lorem ipsum",
style: TextStyle(color: Colors.orange[300], fontSize: 12.0),
textAlign: TextAlign.center,
)
),
alignment: Alignment.center,
transform: Matrix4.identity()
..rotateZ(15 * 3.1415927 / 180),
),
複製代碼
Cmd + Shift + p
:能夠進行快速搜索。須要注意的是,默認是帶有一個>
的,這樣搜索結果主要是dart
代碼。若是想搜索其餘配置文件,或者安裝插件等操做,須要把>
去掉。Cmd + Shift + o
:能夠在某個文件中搜索某個類,但前提是須要提早進入這個文件。例如進入framework.dart
,搜索StatefulWidget
類。Flutter
要注意代碼縮進,若是縮進有問題可能會影響最後的結果,尤爲是在.yaml
中寫配置文件的時候。Flutter
是開源的,因此遇到問題後能夠進入源碼中,找解決方案。Stack
的代碼,若是上面是以逗號結尾,則後面的建立會失敗,若是上面是以分號結尾則沒問題。Widget unreadMsgText = Container(
width: Constants.UnreadMsgNotifyDotSize,
height: Constants.UnreadMsgNotifyDotSize,
child: Text(
conversation.unreadMsgCount.toString(),
style: TextStyle(
color: Color(AppColors.UnreadMsgNotifyTextColor),
fontSize: 12.0
),
),
);
avatarContainer = Stack(
overflow: Overflow.visible,
children: <Widget>[
avatar
],
);
複製代碼
簡書因爲排版的問題,閱讀體驗並很差,佈局、圖片顯示、代碼等不少問題。因此建議到我Github
上,下載Flutter編程指南 PDF
合集。把全部Flutter
文章總計三篇,都寫在這個PDF
中,並且左側有目錄,方便閱讀。
下載地址:Flutter編程指南 PDF 麻煩各位大佬點個贊,謝謝!😁