[TOC]
0x00 Flutter
flutter是google開發的移動端UI框架,支持android和ios。該框架使用dart語言進行開發,在skia的基礎上開發了一套公共組件達到android與ios共用代碼的目的。
1. Flutter系統架構
2. Platform Channels
flutter使用methodChannel/flutterMethodChannel來訪問系統原生api。
0x01 使用Flutter開發應用程序
1. 下載配置Flutter SDK
- 由于眾所周知的原因,我們首先需要進行如下配置,讓flutter通過國內鏡像下載sdk等依賴。
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
- 安裝sdk
$ git clone -b beta https://github.com/flutter/flutter.git
$ export PATH=`pwd`/flutter/bin:$PATH
- 使用
flutter doctor
檢測本機環境,然后根據提示安裝依賴軟件。
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[?] Flutter (Channel beta, v0.1.5, on Mac OS X 10.11.6 15G19009, locale zh-Hans)
[?] Android toolchain - develop for Android devices (Android SDK 27.0.3)
[!] iOS toolchain - develop for iOS devices (Xcode 7.2.1)
? Flutter requires a minimum Xcode version of 9.0.0.
Download the latest version or update via the Mac App Store.
? ios-deploy not installed. To install:
brew install ios-deploy
? CocoaPods not installed.
CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side.
Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS.
For more info, see https://flutter.io/platform-plugins
To install:
brew install cocoapods
pod setup
[?] Android Studio (version 3.0)
[?] Android Studio (version 2.3)
[!] IntelliJ IDEA Community Edition (version 2017.2.6)
? Flutter plugin not installed; this adds Flutter specific functionality.
[!] VS Code (version 1.21.0)
[!] Connected devices
! No devices available
! Doctor found issues in 4 categories.
2. 使用Android Studio開發Demo
1. 創建應用
2. 項目結構
使用android studio創建出來的項目目錄結構大致如下:
-
ios
目錄包含了ios的全部代碼可以直接使用xcode(需要9.0+版本)進行開發。 -
android
目錄包含了android的全部代碼,直接使用android studio開發即可。 -
lib
目錄中包含了兩端通用的dart代碼,在打包生成應用時,全部的dart代碼會被編譯為本地代碼(如在android端,會被直接編譯為so文件)。
3. 調試與運行
-
選擇android/ios設備,然后點擊運行按鈕就可以將應用運行到android/ios設備上。
app_select_device_and_run.png
-
點擊調試安妮運行app,就可以調試dart代碼,調試界面與java完全一樣。
app_debug.png
4. 熱加載
flutter中熱加載的概念和android開發中的Instant run類似,同樣也是點擊??按鈕啟用熱加載功能。但是其擁有以下優點:
- 點擊保存按鈕或者保存快捷鍵,也會觸發熱加載功能(??按鈕功能相同)。
- 熱加載會保留先前的狀態。
- 快!快!快! 代碼增量編譯在秒級別,單行代碼改變反應到應用程序UI改變耗時約1.5秒左右。
所以大家就可以像寫web頁面一樣,可以邊寫->邊保存->邊查看效果,開發效率大大加快有沒有,再也不用等等等有沒有。。。
熱加載前 | 代碼變動 | 熱加載后 |
---|---|---|
可以看到應用程序的計數器的值是2. app_hot_reload_before.png
|
修改字符顯示,添加“吼吼吼吼”到文本中,然后保存觸發熱加載。 app_hot_reload_with_state.png
|
可以看到文本字符發生改變,但是計數器的值未發生變化,仍然是2. app_hot_reload_after.png
|
5. 應用程序結構與兼容性
1. 應用程序結構
- Debug (slow mode模式)
首先我們打開項目根目錄中的build
文件夾,該文件包含了編譯app的全部生成文件,其結構與android應用程序一致(注意:build
目錄在android studio中不顯示,可以通過terminal打開或查看)。
接下來,我們可以通過android studio直接打開該apk,可以發現僅僅只有一個頁面的flutter應用大小已經達到了25MB左右,分析其結構(見下圖),其包含了全部abi類型的so文件,導致apk整體比較大,排除掉x86
、x86_64
平臺的so文件之后,apk整體大小約11MB左右。
- Release模式
使用release模式時,apk大小約8.1MB,大小比較正常。
2. 應用程序兼容性
- andorid最低支持到 api 16;
- ios最低支持到ios 8.0;
3. 開發Toast模塊
這里我們直接使用android studio進行開發,如果大家需要直接使用flutter進行創建的話,可以直接參考platform-channels進行開發。
1. 使用dart編寫公共的toast模塊
import 'package:flutter/services.dart';
// 下劃線開頭的變量只在當前package中可見。
const _toast = const MethodChannel('com.coofee.flutterdemoapp/sdk/toast');
const int _LENGTH_SHORT = 0;
const int _LENGTH_LONG = 1;
void show(String text, int duration) async {
try {
await _toast.invokeMethod("show", {'text': text, 'duration': duration});
} on Exception catch (e) {
print(e);
} on Error catch (e) {
print(e);
}
}
void showShort(String text) {
show(text, _LENGTH_SHORT);
}
void showLong(String text) {
show(text, _LENGTH_LONG);
}
2. 編寫android端的代碼并注冊
package com.coofee.flutterdemoapp;
import android.os.Bundle;
import android.widget.Toast;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), "com.coofee.flutterdemoapp/sdk/toast")
.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if ("show".equals(methodCall.method)) {
String text = methodCall.argument("text");
int duration = methodCall.argument("duration");
Toast.makeText(MainActivity.this, text, duration).show();
}
}
});
}
}
3. 編寫ios端代碼并注冊
ios端的代碼與android端類似,但是需要使用FlutterMethodChannel
進行處理,其他操作與android端一致,打開AppDelegate.m
文件,添加如下代碼:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* toastChannel = [FlutterMethodChannel
methodChannelWithName:@"com.coofee.flutterdemoapp/sdk/toast"
binaryMessenger:controller];
[toastChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"show" isEqualToString:call.method]) {
// 展示toast;
NSLog(@"顯示toast....")
}
}];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
4. 在flutter中調用toast模塊
import 'sdk/toast.dart';
void _incrementCounter() {
showShort('你點擊了$_counter次');
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
5. 效果
0x02 使用Dart 2
1. 升級flutter sdk
使用dart 2時,必須保證,Flutter SDK版本必須大于等于以下版本:
- Beta channel: build 0.1.4 from 2018-02-19, or later
- Dev channel: build from 2018-02-22, or later
- Master channel: build from 2018-02-20, or later
在Terminal中執行flutter --version
命令,查看flutter的版本:
→ flutter --version
Flutter 0.1.5 ? channel beta ? https://github.com/flutter/flutter.git
Framework ? revision 3ea4d06340 (3 weeks ago) ? 2018-02-22 11:12:39 -0800
Engine ? revision ead227f118
Tools ? Dart 2.0.0-dev.28.0.flutter-0b4f01f759
在Terminal中執行flutter upgrade
可以升級flutter。
2. 在android studio中啟用dart 2
在android studio中啟用dart2后,需要重啟android studio使其生效。
3. dart 1 vs dart 2
從下圖可以看出dart2相對于dart1來說,省略了關鍵字new
,使得聲明式布局的可讀性更進一步。
4. dart2對apk大小的影響
從下圖可以看出來,使用dart2時生成的apk比dart1打5MB左右(slow mode模式)。
0x03 Flutter's modes
從下圖我們可以看到,flutter mode顯示在在app的右上角。
flutter的應用程序有以下4中模式,分別使用不同的命令生成,且不同模式下生成的應用大小不一(如:release模式會去掉x86相關的so文件)
mode | 命令 | |
---|---|---|
debug | flutter run | debug模式下的產物,且應用的右上角會顯示slow mode 字樣,支持debug。 |
release | flutter run --release | UI上面不顯示模式;禁止debug,且刪除了debug相關的信息;關閉全部的斷言檢測,減小包大小,使其達到最佳性能。 |
profile | flutter run --profile | 調試性能,不支持模擬器。 |
test | flutter test | 和debug模式類似,不支持headless和桌面平臺。 |