官方文檔
- Flutter官方開發(fā)文檔地址: https://flutter.dev/docs
- Flutter中文開發(fā)文檔地址: https://flutterchina.club/docs/
預研內(nèi)容主要分為幾個部分:
- Flutter環(huán)境搭建(技術框架迫像, 安裝,編輯器)
- Flutter的UI及交互(布局幸撕,交互既们,手勢,動畫响蓉,路由導航)
- Flutter資源文件管理(圖片資源始赎,文件資源迹炼,文件的讀寫)
- Flutter數(shù)據(jù)存儲
- Flutter的網(wǎng)絡請求(網(wǎng)絡HTTP,JSON序列化)
- Flutter與原生平臺(平臺特定代碼交互)
- Flutter開發(fā)語言(Dart語言)
- Flutter其他
Flutter環(huán)境搭建
- 技術框架
在Flutter中用Widget來描述界面,Widget只是View的“配置信息”裂垦,編寫的時候利用Dart語言一些聲明式特性來得到類似結構化標記語言的可讀性顺囊。Widget根據(jù)布局形成一個層次結構。每個widget嵌入其中蕉拢,并繼承其父項的屬性特碳。沒有單獨的“應用程序”對象,相反晕换,根widget扮演著這個角色午乓。在Flutter中,一切皆為Widget闸准,甚至包括css樣式益愈。
- Flutter環(huán)境安裝
mac下終端操作:
vim ~/.bash_profile 添加路徑(沒有.bash_profile的時候,需要通過vim命令創(chuàng)建)
export PUB_HOSTED_URL=https://pub.flutter-io.cn //國內(nèi)用戶需要設置
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //國內(nèi)用戶需要設置
export PATH=PATH_TO_FLUTTER_GIT_DIRECTORY/flutter/bin:$PATH
修改 ~/.zshrc 夷家,在其中添加:source ~/.bash_profile (沒有.zshrc的時候腕唧,需要通過vim命令創(chuàng)建)
- 編輯器
Android Studio 安裝
安裝Flutter和Dart插件
在Flutter插件中,可使用以下模板:
前綴stless: 創(chuàng)建一個StatelessWidget的子類.
前綴stful: 創(chuàng)建一個StatefulWidget子類并且關聯(lián)到一個State子類.
前綴stanim: 創(chuàng)建一個StatefulWidget子類, 并且它關聯(lián)的State子類包括一個 AnimationController
Flutter的UI及交互
- 布局
Text:該 widget 可讓創(chuàng)建一個帶格式的文本瘾英。
Row枣接、 Column: 這些具有彈性空間的布局類Widget可讓您在水平(Row)和垂直(Column)方向上創(chuàng)建靈活的布局
Stack: 取代線性布局 (譯者語:和Android中的LinearLayout相似),Stack允許子 widget 堆疊
Container: Container 可讓您創(chuàng)建矩形視覺元素缺谴。container 可以裝飾為一個BoxDecoration
為了繼承主題數(shù)據(jù)但惶,widget需要位于MaterialApp內(nèi)才能正常顯示, 因此我們使用MaterialApp來運行該應用湿蛔。 //Scaffold是Material中主要的布局組件.
GestureDetector widget并不具有顯示效果膀曾,而是檢測由用戶做出的手勢。 當用戶點擊Container時阳啥, GestureDetector會調(diào)用它的onTap回調(diào)添谊, 在回調(diào)中,將消息打印到控制臺察迟。您可以使用GestureDetector來檢測各種輸入手勢斩狱,包括點擊耳高、拖動和縮放。
StatefulWidgets是特殊的widget所踊,它知道如何生成State對象泌枪,然后用它來保持狀態(tài)
在StatefulWidget調(diào)用createState之后,框架將新的狀態(tài)對象插入樹中秕岛,然后調(diào)用狀態(tài)對象的initState碌燕。 子類化State可以重寫initState,以完成僅需要執(zhí)行一次的工作继薛。 例如修壕,您可以重寫initState以配置動畫或訂閱platform services。initState的實現(xiàn)中需要調(diào)用super.initState當一個狀態(tài)對象不再需要時遏考,框架調(diào)用狀態(tài)對象的dispose叠殷。 您可以覆蓋該dispose方法來執(zhí)行清理工作。例如诈皿,您可以覆蓋dispose取消定時器或取消訂閱platform services林束。 dispose典型的實現(xiàn)是直接調(diào)用super.dispose。
stateless widget 沒有內(nèi)部狀態(tài). Icon稽亏、 IconButton, 和Text 都是無狀態(tài)widget, 他們都是 StatelessWidget的子類壶冒。stateful widget 是動態(tài)的. 用戶可以和其交互 (例如輸入一個表單、 或者移動一個slider滑塊),或者可以隨時間改變 (也許是數(shù)據(jù)改變導致的UI更新)
可以使用key來控制框架將在widget重建時與哪些其他widget匹配截歉。默認情況下胖腾,框架根據(jù)它們的runtimeType和它們的顯示順序來匹配。 使用key時瘪松,框架要求兩個widget具有相同的key和runtimeType咸作。
各種widgets的目錄索引,有UI不熟悉的宵睦,可以在這里找到說明:https://flutterchina.club/widgets/
各類Widgets的地址: https://flutterchina.club/widgets/basics/
各種UI布局需要用到的控件介紹: https://flutterchina.club/widgets/material/
iOS 風格的控件集合 介紹: https://flutterchina.club/widgets/cupertino/
- UI的一些注意事項
類MyAppBar和MyScaffold中使用了Container记罚、Row、Column壳嚎、Text桐智、IconButton、Icon烟馅、BoxDecoration说庭、Center、Expanded等常用Widget
Theme.of(context)將查找Widget樹并返回樹中最近的Theme郑趁。如果我們的Widget之上有一個單獨的Theme定義刊驴,則返回該值。如果不是,則返回App主題捆憎。 事實上舅柜,F(xiàn)loatingActionButton真是通過這種方式找到accentColor的!
ListView的構造函數(shù)需要一次創(chuàng)建所有項目攻礼,但ListView.builder的構造函數(shù)不需要,它將在列表項滾動到屏幕上時創(chuàng)建該列表項栗柒。
new ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return new ListTile(
title: new Text('${items[index]}'),
);
},
);
滑動刪除有直接可用的Widget礁扮;
將響應轉(zhuǎn)換為自定義Dart對象;
class Post {
final int userId;
final int id;
final String title;
final String body;
Post({this.userId, this.id, this.title, this.body});
factory Post.fromJson(Map<String, dynamic> json) {
return new Post(
userId: json['userId'],
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
http package提供了一種方便的方法來為請求添加headers瞬沦。您也可以使用dart:iopackage來添加太伊。
Flutter提供各種按鈕和類似的交互式widget。這些widget中的大多數(shù)實現(xiàn)了Material Design 指南逛钻, 它們定義了一組具有質(zhì)感的UI組件僚焦。可以使用GestureDetector來給任何自定義widget添加交互性曙痘。 可以在管理狀態(tài)和Flutter Gallery中找到GestureDetector的示例芳悲。
如果你要構建一個 CustomButton ,并在構造器中傳入它的 label边坤?那就組合 RaisedButton 和 label名扛,而不是擴展 RaisedButton。
Isolates 是分離的運行線程茧痒,并且不和主線程的內(nèi)存堆共享內(nèi)存肮韧。這意味著你不能訪問主線程中的變量,或者使用 setState() 來更新 UI旺订。正如它們的名字一樣弄企,Isolates 不能共享內(nèi)存。
在 Flutter 中区拳,最簡單的方法是使用 ListView widget拘领。它表現(xiàn)得既和 iOS 中的 ScrollView 一致,也能和 TableView 一致樱调,因為你可以給它的 widget 做垂直排布:
- 交互
- 手勢
要從widget層監(jiān)聽手勢院究,使用 GestureDetector.
- 動畫
參考動畫的說明: https://flutterchina.club/animations/
在 Flutter 中,使用 AnimationController 本涕。這是一個可以暫停业汰、尋找、停止菩颖、反轉(zhuǎn)動畫的 Animation<double> 類型样漆。它需要一個 Ticker 當 vsync 發(fā)生時來發(fā)送信號,并且在每幀運行時創(chuàng)建一個介于 0 和 1 之間的線性插值(interpolation)晦闰。你可以創(chuàng)建一個或多個的 Animation 并附加給一個 controller放祟。
- 路由
路由的介紹:https://docs.flutter.io/flutter/widgets/Navigator-class.html
在頁面之間跳轉(zhuǎn)鳍怨,你有幾個選擇:
具體指定一個由路由名構成的 Map。(MaterialApp
直接跳轉(zhuǎn)到一個路由跪妥。(WidgetApp)
Navigator 類不僅用來處理 Flutter 中的路由鞋喇,還被用來獲取你剛 push 到棧中的路由返回的結果。通過 await等待路由返回的結果來達到這點眉撵。
Map coordinates = await Navigator.of(context).pushNamed('/location');
Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});
Flutter資源文件管理
- 圖片資源
iOS 把 images 和 assets 作為不同的東西侦香,而 Flutter 中只有 assets。被放到 iOS 中 Images.xcasset 文件夾下的資源在 Flutter 中被放到了 assets 文件夾中纽疟。assets 可以是任意類型的文件罐韩,而不僅僅是圖片。
例如污朽,你可以把 json 文件放置到 my-assets 文件夾中散吵。在 pubspec.yaml 文件中聲明 assets:`assets:
- my-assets/data.json
然后在代碼中使用 AssetBundle 來訪問它:
Future<String> loadAsset() async {
return await rootBundle.loadString('my-assets/data.json');
}`
images/my_icon.png // Base: 1.0x image
images/2.0x/my_icon.png // 2.0x image
images/3.0x/my_icon.png // 3.0x image
- 文件的讀寫
PathProvider 插件提供了一種平臺透明的方式來訪問設備文件系統(tǒng)上的常用位置。該類當前支持訪問兩個文件系統(tǒng)位置:
Flutter數(shù)據(jù)存儲
在 Flutter 中蟆肆,可以使用 Shared Preferences plugin 來達到相似的功能矾睦。它包裹了 UserDefaluts 以及 Android 上等價的 SharedPreferences 的功能。
在 iOS 中炎功,你通過 CoreData 來存儲結構化的數(shù)據(jù)顷锰。這是一個 SQL 數(shù)據(jù)庫的上層封裝,讓查詢和關聯(lián)模型變得更加簡單亡问。在 Flutter 中官紫,使用 SQFlite 插件來實現(xiàn)這個功能。
Flutter的網(wǎng)絡請求
- 網(wǎng)絡HTTP
使用dio 來發(fā)起網(wǎng)絡請求州藕,它是一個強大易用的dart http請求庫束世,支持Restful API、FormData床玻、攔截器毁涉、請求取消、Cookie管理锈死、文件上傳/下載
參見具體說明: https://flutterchina.club/networking/
- JSON序列化
使用dart:convert庫可以簡單解碼和編碼JSON;要對簡單的JSON進行編碼贫堰,請將簡單值(字符串,布爾值或數(shù)字字面量)或包含簡單值的Map待牵,list等傳給encode方法:
Flutter與原生平臺
- 平臺特定代碼交互
通過MethodChannel實現(xiàn)其屏。在Flutter當中定義平臺管道,定義平臺需要捕獲的方法名稱->Appdelegate當中注冊定義的管道->管道方法調(diào)用的時候缨该,實現(xiàn)平臺方法的調(diào)用(達到Flutter調(diào)用平臺方法的目的)
- Flutter頁面路由 與 原生頁面之間跳轉(zhuǎn)實現(xiàn):
Flutter 的代碼并不直接在平臺之下運行偎行,相反,Dart 代碼構建的 Flutter 應用在設備上以原生的方式運行,卻“側步躲開了”平臺提供的 SDK蛤袒。這意味著熄云,例如,你在 Dart 中發(fā)起一個網(wǎng)絡請求妙真,它就直接在 Dart 的上下文中運行缴允。你并不會用上平常在 iOS 或 Android 上使用的原生 API。你的 Flutter 程序仍然被原生平臺的 ViewController 管理作一個 view珍德,但是你并不會直接訪問 ViewController 自身练般,或是原生框架。
我怎么訪問 GPS 傳感器菱阵?使用 location 社區(qū)插件踢俄。
我怎么訪問攝像頭缩功?image_picker 在訪問攝像頭時非常常用晴及。
Flutter開發(fā)語言
- Dart語言一些語法特性:
所有沒有初始化的變量值都是 null。
注意: 只有當名字沖突的時候才使用 this嫡锌。否則的話虑稼, Dart 代碼風格樣式推薦忽略 this。
注意: 如果在構造函數(shù)的初始化列表中使用 super()势木,需要把它放到最后蛛倦。 詳情參考 Dart 最佳實踐。
在構造函數(shù)體執(zhí)行之前除了可以調(diào)用超類構造函數(shù)之外啦桌,還可以 初始化實例參數(shù)溯壶。 使用逗號分隔初始化表達式。
class Point {
num x;
num y;
Point(this.x, this.y);
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map jsonMap)
: x = jsonMap['x'],
y = jsonMap['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
有時候一個構造函數(shù)會調(diào)動類中的其他構造函數(shù)甫男。 一個重定向構造函數(shù)是沒有代碼的且改,在構造函數(shù)聲明后,使用 冒號調(diào)用其他構造函數(shù)板驳。
Point.alongXAxis(num x) : this(x, 0);
如果一個構造函數(shù)并不總是返回一個新的對象又跛,則使用 factory 來定義 這個構造函數(shù)。例如若治,一個工廠構造函數(shù) 可能從緩存中獲取一個實例并返回慨蓝,或者 返回一個子類型的實例。
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to the _ in front
// of its name.
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) {
print(msg);
}
}
}
Abstract methods(抽象函數(shù)): 實例函數(shù)端幼、 getter礼烈、和 setter 函數(shù)可以為抽象函數(shù), 抽象函數(shù)是只定義函數(shù)接口但是沒有實現(xiàn)的函數(shù)婆跑,由子類來 實現(xiàn)該函數(shù)济丘。如果用分號來替代函數(shù)體則這個函數(shù)就是抽象函數(shù)。
abstract class Doer {
// ...Define instance variables and methods...
void doSomething(); // Define an abstract method.
}
class EffectiveDoer extends Doer {
void doSomething() {
// ...Provide an implementation, so the method is not abstract here...
}
}
操作符可以被覆寫。 例如摹迷,如果你定義了一個 Vector 類疟赊, 你可以定義一個 + 函數(shù)來實現(xiàn)兩個向量相加。
如果你使用 noSuchMethod() 函數(shù)來實現(xiàn)每個可能的 getter 峡碉、setter近哟、 以及其他類型的函數(shù),你可以使用 @proxy 注解來避免警告信息:
枚舉類型通常稱之為 enumerations 或者 enums鲫寄, 是一種特殊的類吉执,用來表現(xiàn)一個固定 數(shù)目的常量。枚舉的 values 常量可以返回 所有的枚舉值地来。
Mixins 是一種在多類繼承中重用 一個類代碼的方法戳玫。使用 with 關鍵字后面為一個或者多個 mixin 名字來使用 mixin
class Musician extends Performer with Musical {
// ...
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
如果你查看 List 類型的 API 文檔, 則可以看到 實際的類型定義為 List<E>未斑。 這個 <…> 聲明 list 是一個 泛型 (或者 參數(shù)化) 類型咕宿。 通常情況下,使用一個字母來代表類型參數(shù)蜡秽, 例如 E, T, S, K, 和 V 等府阀。T 是一個備用類型。這是一個類型占位符芽突, 在開發(fā)者調(diào)用該接口的時候會指定具體類型试浙。
如果你導入的兩個庫具有沖突的標識符, 則你可以使用庫的前綴來區(qū)分寞蚌。 例如田巴,如果 library1 和 library2 都有一個名字為 Element 的類, 你可以這樣使用:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element(); // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
Dart 有一些語言特性來支持 異步編程挟秤。 最常見的特性是 async 方法和 await 表達式壹哺。要使用 await,其方法必須帶有 async 關鍵字:
在一個方法上添加 async 關鍵字煞聪,則這個方法返回值為 Future斗躏。 例如,下面是一個返回字符串 的同步方法:
String lookUpVersionSync() => '1.0.0';
如果使用 async 關鍵字昔脯,則該方法 返回一個 Future啄糙,并且 認為該函數(shù)是一個耗時的操作。Future<String> lookUpVersion() async => '1.0.0'
;
在 await expression 中云稚, expression 的返回值通常是一個 Future隧饼; 如果返回的值不是 Future,則 Dart 會自動把該值放到 Future 中返回静陈。 Future 對象代表返回一個對象的承諾(promise)燕雁。 await expression 執(zhí)行的結果為這個返回的對象诞丽。 await expression 會阻塞住,直到需要的對象返回為止拐格。如果 await 無法正常使用僧免,確保是在一個 async 方法中。 例如要在 main() 方法中使用 await捏浊, 則 main() 方法的函數(shù)體必須標記為 async:
異步 for 循環(huán)具有如下的形式:使用 break 或者 return 語句可以 停止接收 stream 的數(shù)據(jù)懂衩, 這樣就跳出了 for 循環(huán)并且 從 stream 上取消注冊了。
await for (variable declaration in expression) {
// Executes each time the stream emits a value.
}
所有的 Dart 代碼在 isolates 中運行而不是線程金踪。 每個 isolate 都有自己的堆內(nèi)存浊洞,并且確保每個 isolate 的狀態(tài)都不能被其他 isolate 訪問。
第三方Flutter框架Demo 及文章參考
其他
- Flutter安裝包大小的問題胡岔?
在安卓的安裝包當中會根據(jù)工程的不同增加不同的大小法希。本地打的包和發(fā)布的包大小也可能不一樣。
部分團隊經(jīng)驗:在Release模式下靶瘸,安卓端的安裝包大小增加約為3~4M 苫亦,iOS端的安裝包大小增加約為13~14M,實際增加的大小跟業(yè)務端代碼和資源大小有關系奕锌;
其他團隊的經(jīng)驗著觉,引入 Flutter 之前村生,漲樂財富通的安裝包為 94MB惊暴,引入之后大小為 100MB,發(fā)現(xiàn)增大了 6MB趁桃,這其中主要是引入了 Flutter 的 SDK辽话,增加的大小在可以接受的范圍。
- Flutter的組件化集成(將Flutter代碼集成進現(xiàn)有的工程)卫病?
對現(xiàn)有工程有侵入油啤。不能完全按照官方的指導來集成。需要把Flutter編譯產(chǎn)物放入主工程蟀苛。
對混合棧的管理益咬,參考閑魚團隊的開源方案。
- 預研結果
采用Flutter框架幽告,能夠滿足日常80%以上的基礎UI展示和常見需求。如果涉及復雜動畫或者特定平臺特性的調(diào)用裆甩,也可以使用Flutter的管道特性冗锁,和平臺進行交互實現(xiàn)。
Flutter的性能雖然不如官方宣稱60FPS嗤栓,但是在流暢性上也接近原生冻河,而且主要能夠在安卓和iOS平臺上實現(xiàn)統(tǒng)一且支持部分定制箍邮,盡管安裝包和內(nèi)存上會有部分提升空間。
開發(fā)過程中遇到的實際問題及解決:
- 按鈕做倒計時功能
- 導航的時候叨叙,如果用Navigator.push處理锭弊,新的路由頁面如果用MaterailPageRoute創(chuàng)建,那么 main.dart中 run 的需要是一個MaterailApp
- 在編輯的時候擂错,有的時候編輯器輸入顏色的時候廷蓉,會fetching Doc 俯渤,然后AS就會請求文檔地址雹姊,網(wǎng)絡阻塞造成卡死晃洒。 根據(jù)查找的資料萍嬉,在mac下需要找到 ~/Library/preference/Android studio x.x下面的jav.table.xml這樣的文件忘衍,替換里面JAVDOC節(jié)點下的文檔來源地址url爬迟,或者有解決方式是直接刪除掉這個文件施敢。 試了一下刪除锦爵,效果不是很好子房,但是有所改善形用。可以參考這個回答: Android Studio hangs at fetching documentation
- ListView自定義顯示Item证杭。 建立數(shù)據(jù)源 -> 自定義Adapter,處理Item顯示的內(nèi)容 -> list的內(nèi)容設置給Adapter -> Adapter設置給ListView;