Flutter
與RN
不一樣的是前者和原生iOS/Android組件好比UIButton
其實沒半毛線關係,全部元素都是Flutter
本身畫的,而RN
則只是作了個橋調用而已,明白這個可能對你下決定/忽悠老闆有很是大的意義android
如下開始,咱們假設你已經有了必定的Flutter基礎概念, 好比Release/Debug版本,好比如何跑起來一個HelloWordios
爸爸的指南戳這裏git
谷歌寫的很清楚了你們找着1234567就能夠二者混合了,固然你還能夠谷歌搜【flutter混合】github
配置中有一些爸爸們沒告訴你的事,仍是以iOS工程爲主,畢竟Android不熟:json
flutter create -t module xxx
中的 -t有不少實例裏是沒有-t
的,簡單來講不加-t
,你的iOS/android的工程將會隨git一塊兒提交,而加了後他們永遠不會被提交,別人拉新代碼都須要flutter create
來正常跑起來。xcode
咋看一下-t
會更高端一點,你不須要改任何原生工程全用flutter
寫就行,並且也不會由於sdk改動或者xcode改動影響到你的iOS文件夾,可是請誠實點面對這個社會:微信
podfile
是沒有!use_framwork
的因此若是-t
了,做爲架構師/研究者的你請自覺作好寫腳本去修改上面這些文件的準備。架構
同時請妥善運行
flutter packages get
,由於他會從新搞一下你的iOS文件夾的內容, 而flutter build --release
會默認幫你flutter packages get
,因此若是你有本身的初始化腳本,那執行順序應該是:app
flutter packages get
- 你的腳本
flutter build --release -no-pub
-no-pub
會忽略掉build
時的那次get
Build Phases
中的script
其實不是每次都須要run的Flutter Release
版本的構建有影響,
Flutter Debug
模式flutter
更新的工程師不須要關心他是否是跑了Debug
的主工程想跑Release
的Flutter
,不跑這個Script是會crash的,這就是由於Flutter Release
的配置須要這個腳原本完成flutter
佈局,並非研究原生與flutter
的耦合以及框架解藕Flutter run
是不合理的可能你也會用jenkins或者fastlane去給QA打包,這時候若是要生成產物請走Flutter build --release
命令,由於Flutter run
會直接把進程卡住而後你就沒法持續了,雖然你能夠選擇後臺運行run
,可是你無法保證同步~框架
至於爲何,能夠翻翻鹹魚寫的文章,仍是從簡化的說就是:
FlutterViewController
肚子裏就是Flutter引擎
,因此若是純flutter項目你會發現,其實vc就只有一個,他是在單個vc中畫新頁面來完成push操做的FlutterViewController
去push下一個FlutterViewController
,這樣無限增長的引擎會讓你的應用在短期內就崩潰~固然若是你是很厲害的那種必定要本身玩,那也能夠,不然請看2裏說的在這裏同樣適用~
用過的孩子必定對
query
這個key很憤怒,確實你也不明白爲何鹹魚官方並無說這個key,但事實上安卓側原生接到的參數都是經過這個包着的,即{ "query": { "name" : xxx }}
, 因此iOS的也能夠注意下
可是其實這樣並很差看~雖然官方推薦,可是官方還有個很優雅的姿式叫FlutterPlugin
說穿了MethodChannel
只是須要原生有個時機註冊進Flutter
引擎而已,至於何時,隨時都行,因此咱們找到了FlutterPlugin
的時機,全部第三方的flutter插件(這些插件多數其實也是經過channel調用原生來解決的),其實也是經過註冊的方式注入引擎的,若是你想看看怎麼來的,你能夠在你的工程裏找GeneratedPluginRegistrant.m
這個文件,這是個系統生成的文件,他會幫你註冊全部你引入flutter的插件:
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[FLTDeviceInfoPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTDeviceInfoPlugin"]];
...
}
@end
複製代碼
因此咱們有個大膽的想法,若是咱們本身也搞個plugin
,裏面塞個channel
,用來作全部flutter
與咱們現有主工程的進行交互,不就能夠裝的很優雅了嘛? 可是問題是:系統生成的GeneratedPluginRegistrant
我怎麼在裏面加上我本身的插件? 由於我本身的插件在主工程,並非在flutter裏引入的鴨...
這時候聰明的你可能已經想到了那在Swift時代被遺忘的黑魔法:
- (instancetype)init
{
if (self = [super init]) {
_viewController = [FLBFlutterViewControllerAdaptor new];
[_viewController view];
Class clazz = NSClassFromString(@"GeneratedPluginRegistrant");
if (clazz) {
if ([clazz respondsToSelector:NSSelectorFromString(@"registerWithRegistry:")]) {
[clazz performSelector:NSSelectorFromString(@"registerWithRegistry:")
withObject:_viewController];
}
}
}
return self;
}
複製代碼
上面這段是從flutter_boost
中找到的,因此你應該明白其實你也能夠進行偷換GeneratedPluginRegistrant
中的registerWithRegistry
方法來先註冊flutter本身的插件,再註冊主工程插件已保持優雅的姿態了吧~
FlutterResult
類裏規定了記得傳String
,固然傳人能讀得懂的字符串了,請重複這句【傳人能讀得懂的字符串】,因此千萬不要耍心機把一個data
搞成字符串而後傳給flutter
讓他變回data
甚至變回image
,記得,你讀不懂image
字符串,flutter
也不懂。而json
字符串你看得懂,因此flutter
也看得懂
因此複雜的文件,你只要知道,flutter
雖然讀不懂data
,可是他也能訪問NSTemporaryDirectory
, 因此你能夠經過暫時文件夾路徑的方式來支持雙方共享同一個數據,雖然也不太推薦,可是你好像也只能這麼幹了
private func saveToFile(image: UIImage) -> String? {
guard let data = image.jpg(compressionQuality: 1.0) else {
return nil
}
let tempDir = NSTemporaryDirectory()
let imageName = "image_picker_\(ProcessInfo().globallyUniqueString).jpg"
let filePath = tempDir.appending(imageName)
if FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil) {
return filePath
} else {
return nil
}
}
複製代碼
好比像樓上這樣
Dart
裏我要準備些啥若是一開始規劃是有原生的基礎服務就調原生的,好比請求登陸拍照等等,那理想中其實你能夠以爲flutter
自己代碼你只須要作業務就好了。
嗯,回想一下那個RN
中編碼5分鐘,聯調5小時的你嗎?因此若是有時間,請爲flutter
搭建全部用獲得的基礎設施,來大幅度加快調試速度:
下面這個模式可供你們參考,他能夠最大限度保證代碼可擴展性
咱們經過
上帝模式:isSonMode= false
即flutter
本身跑的模式
兒子模式:isSonMode= true
即在主工程裏跑的模式
上述的區分來判斷咱們須要調用哪一種基礎服務
好比這是咱們的dart端請求調用:
class RequestService {
static RequestService shared = RequestService();
factory RequestService() =>
GlobalConfig.isSonMode ? SonRequestService() : GodRequestService();
Future<Map> post(String url, Map para) async {
throw UnimplementedError("saveElement 方法木有實現哦");
}
Future<UploadItem> upload(UploadItem file) async {
throw UnimplementedError("saveElement 方法木有實現哦");
}
}
class GodRequestService implements RequestService {
Future<Map> post(String url, Map para) async {
// 你能夠在這裏調用dio或者http請求
}
Future<UploadItem> upload(UploadItem file) async {
...
}
}
複製代碼
Son
就是調用原生的channel,因此不貼了,因此咱們根據GlobalConfig.isSonMode
來切換使用哪一個具體的RequestService
實現,來隔離每一個RequestService
不會因爲判斷而形成的污染,
固然還有更花哨的判斷:
static Router _route =
GlobalConfig.isAndroid ? AndroidRouteBox() : GlobalConfig.isSonMode ? FlutterBoostRouteBox() : GodRouteBox();
複製代碼
好比路由咱們在flutter
上帝模式下用的是flutter
本身跳轉, iOS 主工程下是flutter_boost
, 安卓主工程下依舊是本身的跳轉
而在dart
業務代碼中你只須要作 RequestService.shared.post...
的調用,就能夠正常請求,不須要關心切換的問題了。
(固然請求抽象會在下期着重講,敬請期待~)
工具處處都有 稍微推薦下這個,固然極其讚美手寫毅力擔當~可是注意點:
dart
相比Swift
來講對類型的嚴格性更高int
/double
若是定義反了會炸,好比把1.0
傳給int
型屬性,在Swift
中毫無波瀾,在dart
中就是波濤洶涌,dart
中妥善點能夠用num
來修飾全部數字String
類型推薦都加上toString()
額外加個優雅的,Swift中的
var isUser : Bool {
return xxx == 1 && xxxx = 2
}
這樣的計算屬性,在dart中能夠寫成:
bool get isUser => xxx == 1 && xxxx = 2
或者
bool get isUser {
return xxx == 1 && xxxx = 2
}
複製代碼
這樣能夠下降你的
Widget
中過多的業務計算,這些定義仍是讓Model
來作吧
因爲引擎的繪製不一樣,致使flutter
的頁面生命週期並無合適的回調/代理等來觸發,不要想着didUpdateWidget
或者Dependency
裏來作些奇怪的請求刷新,那地方不是讓你用來作這個事情的~ 惟一你能夠作的好像就只有在initState
裏作完全部事情;
這裏就講點虛的,因此全部的操做須要嚴格從動做觸發,而不要是再從頁面級別觸發了,
舉個例子
Swift
:button點擊 -> 跳轉 -> 返回 -> ViewWillAppear 刷新全頁
Flutter
: button點擊 -> 刷新對應的Widget -> 跳轉
其實Swift
的例子也很差,可是其實咱們由於懶基本都這樣幹了,可是在dart
中咱們無法知道頁面生命週期,因此請用正確的時間作正確的事,固然這裏就容忍了滿屏幕的setState
,確定會有人告訴你這樣是不合理的了~怎麼纔算合理,這裏就先不說了,畢竟這裏的目的是爲了讓你們把應用跑起來能上線,至於優雅不優雅後面熟悉了天然就有感受了。
pubspec.yaml中這樣就好了
assets:
- assets/images/
- assets/images/2.0x/
- assets/images/3.0x/
複製代碼
有時候你會發現你新加了個圖結果位置都放對告終果沒出來,沒事你debug
多點幾回他就有了,若是確認名字沒問題的話,確實是可能有時候不會及時顯示出來的
比較有意思的是對於main入口:
return MaterialApp(
title: '我是個demo',
theme: ThemeData(
fontFamily: Platform.isIOS ? 'PingFang SC' : null,)
}
複製代碼
你須要這樣設置後,在iOS手機上你的字體纔會好看,不然會出現各類奇奇怪怪的樣子,不過又有個隱藏坑就是:
你的全部頁面的頂層widget
必須是material
系列的這個纔會生效,這些是Material
系列,
因此若是你的頁面裏直接是:
class DemoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: 頁面元素
);
}
}
複製代碼
你會發現你的字體依舊很奇怪,雖然頁面長得沒問題
是否是很簡單,看到這裏,坑確定還有,但至少大問題應該沒有了,你應該已經能夠愉快的跑起來你的應用了~