Flutter
概要
Flutter是一個(gè)由谷歌開發(fā)的開源移動(dòng)應(yīng)用軟件開發(fā)工具包书在。它是以dart為基礎(chǔ)做出的一套SDK,支持在Android和iOS上構(gòu)建APP案腺。簡(jiǎn)單來(lái)說(shuō)它與RN另凌、Weex等眾多混合開發(fā)平臺(tái)框架一樣,都是一套代碼實(shí)現(xiàn)多平臺(tái)發(fā)布的跨平臺(tái)框架孙乖。
Flutter與其他跨平臺(tái)的最大不同之處是它自建了一個(gè)2D渲染引擎.
- Flutter 框架
* Flutter框架可分為Framework層和Engine層浙炼;
- Flutter Framework: 整個(gè)框架層都是用Dart語(yǔ)言實(shí)現(xiàn),該層提供一套基礎(chǔ)庫(kù)唯袄, 用于處理動(dòng)畫弯屈、繪圖和手勢(shì)等。并且基于繪圖封裝了一套 UI組件庫(kù)恋拷,并且細(xì)分為兩種風(fēng)格的組件.
1. Materail : Android風(fēng)格的Widget;
2. Cupertino: IOS風(fēng)格的Widget;
- Flutter Engine: 這是一個(gè)純 C++實(shí)現(xiàn)的框架層资厉,包含了 Skia引擎(高性能渲染引擎)、Dart運(yùn)行環(huán)境蔬顾、文字排版引擎等宴偿。
1. 配置Flutter環(huán)境
參考:
flutter doctor
1.1 初始化項(xiàng)目
- 純flutter工程d
flutter create myapp
cd myapp
flutter devices
flutter run -d <deviceID>
- flutter module
flutter create -t module smarthome_flutter
- 默認(rèn)情況下湘捎,模板支持使用Java編寫Android代碼、用Objective-C編寫iOS代碼窄刘;要使用Kotlin或Swift窥妇,請(qǐng)使用-i和/或-a標(biāo)志:
flutter create -i swift -a kotlin myapp
SmartHome集成flutter 目前采用的是flutter module的模式
1.2 Flutter 版本
#查看當(dāng)前使用的分支
flutter channel
#切換分支 stable為flutter的穩(wěn)定版分支
flutter channel stable
flutter channel master
flutter channel beta
flutter channel dev
1.3 升級(jí) Flutter channel 和 packages
- 要同時(shí)更新Flutter SDK和你的依賴包,在你的應(yīng)用程序根目錄(包含pubspec.yaml文件的目錄)中運(yùn)行flutter upgrade 命令
flutter upgrade
- 如果您修改了pubspec.yaml文件娩践,或者只想更新應(yīng)用依賴的包(不包括Flutter SDK)活翩,請(qǐng)使用以下命令:
#獲取pubspec.yaml文件中列出的所有依賴包
flutter packages get
#獲取pubspec.yaml文件中列出的所有依賴包的最新版本
flutter packages upgrade
2. 如何集成Flutter
2.1 確保所有的submodule已下載
Native工程下 feature-flutter
分支 執(zhí)行 git submodule update
2.2 生成flutter module的個(gè)人本地配置
cd [Your SmartHome Project Path]/smarthome_flutter
flutter run
2.3 cocoapods 集成flutter
回到Native工程的路徑下 pod install
2.4 Q&A
- 集成flutter后,xcode 編譯運(yùn)行APP時(shí) 翻伺,報(bào):
error: Multiple commands produce .......
* 打開.ios/Runner.xcworkspace -> TARGETS -> Runner -> Build Phases -> Embed Frameworks 然后刪除 flutter.frameworks
- flutter 中webView打不開材泄,log: Trying to embed a platform view but the PaintContext does not support embedding
- 在.iOS/ 目錄下Runner.xcworkspace工程中 的info.plist 文件中添加配置
- key = io.flutter.embedded_views_preview
- value = YES
-
Lost connection to device
- brew upgrade --fetch-HEAD usbmuxd
3. 原生和Flutter相互通信 platform channels
通過platform channels 在flutter 和宿主(ios/andriod)之間傳遞消息,如下圖所示:
Samples
- flutter端 關(guān)鍵代碼
import 'package:flutter/services.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:smarthome_flutter/common/tools/SHEventBus.dart';
part 'platfrom.g.dart';
var sh_event_bus = EventBus();
class SHPlatform{
static final MethodChannel channel = _setupChannel();
static MethodChannel _setupChannel(){
var channel = MethodChannel("com.jd.smarthome",StandardMethodCodec());
channel.setMethodCallHandler((MethodCall call) async {
String method = call.method;
dynamic arguments = call.arguments;
switch(method){
case 'updateInfo':
sh_event_bus.emit('updateHomePage');
break;
default:
throw new PlatformException(code:"");
}
});
return channel;
}
static DeviceInfo _deviceInfo;
static Future<DeviceInfo> getDeviceInfo() async{
if(_deviceInfo == null){
return SHPlatform.channel.invokeMethod("deviceInfo")
.then((onValue){
_deviceInfo = DeviceInfo.fromJson(Map<String, dynamic>.from(onValue));
return _deviceInfo;
});
}else{
return Future.value(_deviceInfo);
}
}
}
@JsonSerializable()
class DeviceInfo{
String platform;
String hardPlatform;
String systemVersion;
String appVersion;
String channel;
String deviceId;
String tgt;
String pin;
DeviceInfo();
factory DeviceInfo.fromJson(Map<String, dynamic> json) => _$DeviceInfoFromJson(json);
}
- iOS 端關(guān)鍵代碼
-(void)showFlutterView{
FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine];
__weak __typeof(self) weakSelf = self;
// 要與main.dart中一致
NSString *channelName = @"com.jd.smarthome";
//FlutterMethodChannel *messageChannel;
self.messageChannel = [FlutterMethodChannel methodChannelWithName:channelName
binaryMessenger:flutterEngine];
[self.messageChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
if ([call.method isEqualToString:@"toNativeSomething"]) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"flutter回調(diào)" message:[NSString stringWithFormat:@"%@",call.arguments] delegate:self cancelButtonTitle:@"確定" otherButtonTitles:nil];
[alertView show];
// 回調(diào)給flutter
if (result) {
result(@10);
}
} else if ([call.method isEqualToString:@"toNativePush"]) {
//[GLOBAL_Nav pushViewController:vc animated:YES andLastName:getClassName];
} else if ([call.method isEqualToString:@"toNativePop"]) {
[GLOBAL_Nav popViewControllerAnimated:YES];
}else if([call.method isEqualToString:@"deviceInfo"]){
NSDictionary* data = @{
@"platform":kPlatValue,
@"hardPlatform":[CommonMethod getPlatformName],
@"systemVersion":[CommonMethod getSystemVersion],
@"appVersion":[CommonMethod getAppVersion],
@"channel":kChannelValue,
@"deviceId":[OpenUDID value],
@"tgt":[LoginObjectClass sharedLoginObjectClass].A2String,
@"pin":[LoginObjectClass sharedLoginObjectClass].pin,
};
result(data);
}else{
result(FlutterMethodNotImplemented);
}
}];
FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
flutterViewController.navigationItem.title = @"Flutter Demo";
[GLOBAL_Nav pushViewController:flutterViewController animated:YES andLastName:getClassName];
[self updateFlutterHomePage];
}
-(void)updateFlutterHomePage{
[self.messageChannel invokeMethod:@"updateInfo" arguments:@{@"page": @"1"} result:^(id _Nullable result) {
if([result isKindOfClass:[FlutterError class]]){
FlutterError *error = (FlutterError *)result;
SHLogError(kLogFlutter, @"updateInfo with error message: %@", error.message);
}else if (result == FlutterMethodNotImplemented) {
SHLogError(kLogFlutter, @"updateInfo was unexepectedly not implemented ");
}else{
SHLogInfo(kLogFlutter, @"updateInfo success");
}
}];
}
4 Flutter 狀態(tài)管理 ----Provider
4.1 為什么需要狀態(tài)管理
在 State 屬于某一個(gè)特定的 Widget吨岭,在多個(gè) Widget 之間進(jìn)行交流的時(shí)候脸爱,雖然你可以使用 callback 解決,但是當(dāng)嵌套足夠深的話未妹,我們?cè)黾臃浅6嗫膳碌睦a簿废。
隨著功能的增加,你的應(yīng)用程序?qū)?huì)有幾十個(gè)甚至上百個(gè)狀態(tài)络它。這個(gè)時(shí)候應(yīng)用應(yīng)該會(huì)像下圖一樣族檬。
這時(shí)候,我們便迫切的需要一個(gè)架構(gòu)來(lái)幫助我們理清這些關(guān)系化戳,狀態(tài)管理框架應(yīng)運(yùn)而生单料。
4.2 怎么用provider
4.2.1、添加依賴
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
webview_flutter: ^0.3.9+1
dio: 2.1.10
json_annotation: ^2.0.0
provider: ^3.0.0
intl: ^0.15.7
crypto: ^2.0.6
logging:
cached_network_image: ^1.1.1
flutter_easyrefresh: ^1.2.7
4.2.2点楼、創(chuàng)建數(shù)據(jù)Model
//這里使用了 mixin 混入了 ChangeNotifier扫尖,這個(gè)類能夠幫助我們自動(dòng)管理所有l(wèi)isteners。當(dāng)調(diào)用 notifyListeners() 時(shí)掠廓,它會(huì)通知所有聽眾進(jìn)行刷新换怖。
class CounterModel with ChangeNotifier {
int _count = 0;
int get value => _count;
void increment() {
_count++;
notifyListeners();
}
}
4.2.3、創(chuàng)建頂層共享數(shù)據(jù)
class GlobalConfig{
static get version => '0.0.3';
static get counter => CounterModel();
static ThemeData get themeData => ThemeData(
primaryColor: Color(0xff181B34),
textTheme: TextTheme(
title: TextStyle(
fontSize: 14,
color: Colors.white
)
)
);
static List<SingleChildCloneableWidget> get providers => [
Provider<String>.value(value:GlobalConfig.version),
ChangeNotifierProvider<CounterModel>.value(
value: GlobalConfig.counter,
)
];
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: GlobalConfig.providers,
child: MaterialApp(
title: 'SmartHome demo',
theme: GlobalConfig.themeData,
home: MyHomePage(
title: "APP",
),
debugShowCheckedModeBanner: false,
),
);
}
}
4.2.4蟀瞧、在子頁(yè)面中獲取狀態(tài)
- Provider.of(context)
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final _counter = Provider.of<CounterModel>(context);
return Scaffold(
appBar: AppBar(
title: Text('FirstPage'),
),
body: Center(
child: Text(
'Value: ${_counter.value}',
style: TextStyle(fontSize: 11),
),
),
);
}
}
- Consumer
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second Page'),
),
body: Consumer2<CounterModel,string>(
builder: (context, CounterModel counter, string version, _) => Center(
child: Text(
'Value: ${counter.value}' + version,
style: TextStyle(fontSize: 11,
),
),
),
),
);
}
}
- Different ?
- 通過閱讀源碼可以發(fā)現(xiàn)沉颂,Consumer 就是通過 Provider.of<T>(context) 來(lái)實(shí)現(xiàn)的。但是從實(shí)現(xiàn)來(lái)講 Provider.of<T>(context) 比 Consumer 簡(jiǎn)單好用悦污,但是我們推薦優(yōu)先使用Consumer铸屉,好處在于能夠在復(fù)雜項(xiàng)目中,極大地縮小你的控件刷新范圍切端。Provider.of<T>(context) 將會(huì)把調(diào)用了該方法的 context 作為聽眾彻坛,并在 notifyListeners 的時(shí)候通知其刷新。
5 Map -> model
介紹一下官方推薦的json_serializable package包。 它是一個(gè)自動(dòng)化的源代碼生成器昌屉,可以在開發(fā)階段為我們生成JSON序列化模板.
5.1钙蒙、在項(xiàng)目中設(shè)置json_serializable
dependencies:
# Your other regular dependencies here
json_annotation: ^2.0.0
dev_dependencies:
# Your other dev_dependencies here
build_runner: ^1.0.0
json_serializable: ^3.0.0
5.2 以json_serializable的方式創(chuàng)建model類
import 'package:json_annotation/json_annotation.dart';
part 'skill_model.g.dart';
@JsonSerializable()
class SkillList {
SkillList();
int page_size;
int sum_size;
int current_page;
List<SkillListItem> skills;
factory SkillList.fromJson(Map<String, dynamic> json) => _$SkillListFromJson(json);
Map<String, dynamic> toJson() => _$SkillListToJson(this);
}
//一個(gè)SkillList.fromJson 構(gòu)造函數(shù), 用于從一個(gè)map構(gòu)造出一個(gè) User實(shí)例 map structure
//一個(gè)toJson 方法, 將 User 實(shí)例轉(zhuǎn)化為一個(gè)map.
- json_serializable第一次創(chuàng)建類時(shí),您會(huì)看到與下圖類似的錯(cuò)誤怠益。
#一次性生成
flutter packages pub run build_runner build
#持續(xù)生成
flutter packages pub run build_runner watch
- 使用
request.parseFunction = (json){
SkillList tmp = SkillList.fromJson(json);
Map<String, dynamic> newMap = tmp.toJson();
return SkillList.fromJson(json);
};
6 異步async、await和Future
Flutter中瘾婿,雖然Dart是基于單線程模型的蜻牢,但是這并不意味著我們沒法完成異步操作。在Dart中我們可以通過async關(guān)鍵字來(lái)聲明一個(gè)異步方法偏陪,在異步方法中可以使用await表達(dá)式掛起該異步方法中的某些步驟,從而實(shí)現(xiàn)等待某步驟完成的目的; Future是Dart中提供的一個(gè)類抢呆,它用于封裝一段在將來(lái)會(huì)被執(zhí)行的代碼邏輯。
Note
async修飾的異步方法需要聲明返回一個(gè)Future類型笛谦,如果方法體內(nèi)沒有主動(dòng)的返回一個(gè)Future類型抱虐,系統(tǒng)會(huì)將返回值包含到一個(gè)Future中返回。
await表達(dá)式的表達(dá)式部分需要返回一個(gè)Future對(duì)象饥脑。
await表達(dá)式需要在一個(gè)async修飾的方法中使用才會(huì)生效恳邀。 關(guān)于async和await的更多詳情可以參閱官方文檔。
end
推薦