在Flutter項目開發中有時候有些常用的框架沒有Flutter版本,這樣的場景就需要接入原生sdk並完成與原生sdk通訊 這里主要講解如何實現與iOS的混合開發
大致思路就是創建Flutter_module項目,並將Flutter項目以及引用的第三方庫編譯成靜態Framework並在iOS中通過pod的方式引入
第一步:創建一個原生的iOS工程
1.創建一個空文件夾 名字叫 flutter_iOS_Mixture
2.在flutter_iOS_Mixture文件夾中創建XCode工程,並在工程中執行
pod init
pod install
第二步:創建Flutter_Module
1.定位到flutter_iOS_Mixture文件夾目錄,並在終端執行命令,創建flutter module
flutter create -t module flutter_project
執行完畢后,工程中的目錄結構
2.查看目錄結構查看隱藏文件請使用快捷鍵打開隱藏文件
command + shift + .
目錄結構
.
├── flutter_project
│ ├── README.md
│ ├── flutter_project.iml
│ ├── flutter_project_android.iml
│ ├── lib
│ ├── pubspec.lock
│ ├── pubspec.yaml
│ └── test
└── iOS_App
├── iOS_App
├── iOS_App.xcodeproj
├── iOS_AppTests
└── iOS_AppUITests
3.打開Flutter工程,並在pubspec.yaml文件中添加兩個第三方框架 執行 pub get
cupertino_icons: ^0.1.2
webview_flutter: ^0.3.19+9
url_launcher: ^5.1.2
第三步:將Flutter編譯成靜態Framework並引用到iOS工程中
這里就有個分支了兩種解決方案 1種是直接在iOS中添加依賴,就可以實現Flutter與iOS的混合,操作簡單,但是有個缺點就是如果是多人開發項目的話,直接引入,需要每個開發者都需要有Flutter環境才可以正常編譯通過,否則會報錯,這樣侵入性太強,但是如果開發人數少,使用這種方式確實可以提升開發效率(不能每次修改Flutter內容后都需要重新將Flutter打包成Framework,節約了不少時間),這也是蘋果官方推薦使用的解決方案
直接在Podfile文件中加入如果內容,Flutter與iOS的橋接就算完成了
flutter_application_path = '../flutter_project/'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
install_all_flutter_pods(flutter_application_path)
全部文件如下:
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
flutter_application_path = '../flutter_project/'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'iOS_App' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
install_all_flutter_pods(flutter_application_path)
# Pods for iOS_App
target 'iOS_AppTests' do
inherit! :search_paths
# Pods for testing
end
target 'iOS_AppUITests' do
# Pods for testing
end
end
執行pod install將Flutter引入到iOS項目中
chenhaodeMac:iOS_App chenhao$ pod install
Analyzing dependencies
Downloading dependencies
Installing FlutterPluginRegistrant 0.0.1
Installing url_launcher (0.0.1)
Installing url_launcher_macos (0.0.1)
Installing url_launcher_web (0.0.1)
Installing webview_flutter (0.0.1)
Generating Pods project
Integrating client project
Pod installation complete! There are 7 dependencies from the Podfile and 7 total pods installed.
查看XCode工程發現導入的Flutter庫也被加入到了iOS中
以上是Flutter與iOS橋接的第一種方式
2.將Flutter作為一個組件加入到iOS工程中,這樣需要使用的時候,直接通過pod導入就行了,這樣的好處是任何人都可以導入該插件,不需要電腦中裝有Flutter環境,但是這種方式橋接操作相對繁雜,Flutter項目中內容有修改,需要重新打包並提交iOS工程中才可以生效,下面主要介紹這種方式如何實現與iOS的橋接
1> 創建一個Pod庫在flutter_iOS_Mixture根目錄執行命令創建pod lib
pod lib create flutter_lib
chenhaodeMac:flutter_iOS_ Mixture chenhao$ pod lib create flutter_lib
Cloning `https://github.com/CocoaPods/pod-template.git` into `flutter_lib`.
Configuring flutter_lib template.
To get you started we need to ask a few questions, this should only take a minute.
If this is your first time we recommend running through with the guide:
- https://guides.cocoapods.org/making/using-pod-lib-create.html
( hold cmd and double click links to open in a browser. )
What platform do you want to use?? [ iOS / macOS ]
> iOS
What language do you want to use?? [ Swift / ObjC ]
> ObjC
Would you like to include a demo application with your library? [ Yes / No ]
> NO
Which testing frameworks will you use? [ Specta / Kiwi / None ]
> None
Would you like to do view based testing? [ Yes / No ]
> No
What is your class prefix?
> ASS
Running pod install on your new library.
Analyzing dependencies
Downloading dependencies
Installing flutter_lib (0.1.0)
Generating Pods project
Integrating client project
[!] Please close any current Xcode sessions and use `flutter_lib.xcworkspace` for this project from now on.
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
Ace! you're ready to go!
此時的目錄結構如下
.
├── flutter_lib
│ ├── Example
│ ├── LICENSE
│ ├── README.md
│ ├── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
│ ├── flutter_lib
│ └── flutter_lib.podspec
├── flutter_project
│ ├── README.md
│ ├── flutter_project.iml
│ ├── flutter_project_android.iml
│ ├── lib
│ ├── pubspec.lock
│ ├── pubspec.yaml
│ └── test
└── iOS_App
├── Podfile
├── Podfile.lock
├── Pods
├── iOS_App
├── iOS_App.xcodeproj
├── iOS_App.xcworkspace
├── iOS_AppTests
└── iOS_AppUITests
在flutter_lib中創建ios_frameworks文件夾用來存放Flutter編譯后的靜態文件
找到flutter_lib中flutter_lib.podspec找打並修改引用 在文件最后添加如下一段代碼
s.ios.deployment_target = '8.0'
s.static_framework = true
p = Dir::open("ios_frameworks")
arr = Array.new
arr.push('ios_frameworks/*.framework')
s.ios.vendored_frameworks = arr
#s.source_files = 'flutter_lib/Classes/**/*'
# s.resource_bundles = {
# 'flutter_lib' => ['flutter_lib/Assets/*.png']
# }
# s.public_header_files = 'Pod/Classes/**/*.h'
# s.frameworks = 'UIKit', 'MapKit'
# s.dependency 'AFNetworking', '~> 2.3'
下面開始執行一段腳本 將Flutter編譯並打包,將生成的frameworks自動移入到flutter_lib中的ios_frameworks中,這個ios_frameworks也正好是剛剛修改的flutter_lib.podspec引入的路徑,將腳本放在flutter項目根目錄中,腳本內容
if [ -z $out ]; then
out='ios_frameworks'
fi
echo "准備輸出所有文件到目錄: $out"
echo "清除所有已編譯文件"
find . -d -name build | xargs rm -rf
flutter clean
rm -rf $out
rm -rf build
flutter packages get
addFlag(){
cat .ios/Podfile > tmp1.txt
echo "use_frameworks!" >> tmp2.txt
cat tmp1.txt >> tmp2.txt
cat tmp2.txt > .ios/Podfile
rm tmp1.txt tmp2.txt
}
echo "檢查 .ios/Podfile文件狀態"
a=$(cat .ios/Podfile)
if [[ $a == use* ]]; then
echo '已經添加use_frameworks, 不再添加'
else
echo '未添加use_frameworks,准備添加'
addFlag
echo "添加use_frameworks 完成"
fi
echo "編譯flutter"
flutter build ios --debug --no-codesign
#flutter build ios --release --no-codesign
echo "編譯flutter完成"
mkdir $out
cp -r build/ios/Debug-iphoneos/*/*.framework $out
#cp -r build/ios/Release-iphoneos/*/*.framework $out
cp -r .ios/Flutter/App.framework $out
cp -r .ios/Flutter/engine/Flutter.framework $out
echo "復制framework庫到臨時文件夾: $out"
libpath='../flutter_lib/'
rm -rf "$libpath/ios_frameworks"
mkdir $libpath
cp -r $out $libpath
echo "復制庫文件到: $libpath"
執行腳本后發現flutter_lib中的ios_frameworks中多了一些flutter的使用的庫文件
sh build_ios.sh
在podfile文件中引入組件化的flutter庫
pod 'flutter_lib', :path => '../flutter_lib'
執行pod install
chenhaodeMac:iOS_App chenhao$ pod install
Analyzing dependencies
Downloading dependencies
Installing flutter_lib (0.1.0)
Generating Pods project
Integrating client project
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
此時Flutter與iOS的第二種橋接方式算是操作完了,此時flutter_lib已經通過pod引入到了項目中
第四步:iOS與Flutter互相通訊
1.iOS中調用Flutter工程
//初始化FlutterViewController
self.flutterViewController = [[FlutterViewController alloc] init];
//這里可以傳遞參數用來控制flutter做一些操作
[self.flutterViewController setInitialRoute:@"{\"msg\":\"我是iOS傳入的指令\"}"];
self.flutterViewController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:self.flutterViewController animated:YES completion:nil];
2.iOS與flutter通訊
通訊方式共有三種
- BasicMessageChannel通用數據傳輸,全雙工,實時傳遞
- MethodChannel方法傳遞通道,傳遞只執行一次 全雙工
- EventChannel事件監聽通道持續監聽如果電池電量的監聽
這里只寫MethodChannel寫幾個方法實現flutter與iOS的方法互調
在iOS中首先要創建消息通道並初始化通道名,這樣后面所有消息都通過這個通道名對應的通道傳遞
//初始化通道
FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"MSGChannel" binaryMessenger:self.flutterViewController.binaryMessenger];
self.methodChannel = methodChannel;
//通過block回調監聽通道中來自flutter的消息體 這里做一個dismiss方法,由於iOS中將flutter頁面push出來,次數實現dismiss方法,給flutter發送dismss消息,就知道是讓iOS將當前頁面關閉的動作,iOS收到后,執行關閉操作
__weak typeof(self) weakself = self;
[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
__strong typeof(weakself) strongself = weakself;
//dissmiss當前頁面
if([call.method isEqualToString:@"dismiss"]){
[strongself dismissViewControllerAnimated:YES completion:nil];
}
if (result) {
result(@"成功關閉頁面");
}
}];
//iOS中也可以主動給Flutter發消息通過invokeMethod 只需要注意消息通道名要跟初始化保持一致
[self.methodChannel invokeMethod:@"MSGChannel" arguments:@"我是iOS發送過來的消息"];
在flutter中,首先要在main方法中通過window.defaultRouteName的方式獲取iOS中傳入的Route參數
flutter中同樣需要創建消息通道
//創建消息通道並初始化消息名 這個名字要與iOS對應
static const MethodChannel methodChannel = MethodChannel('MSGChannel');
//設置消息監聽
methodChannel.setMethodCallHandler((MethodCall call){
//接收到消息
print(call.method);
print(call.arguments);
return Future.value(1);
});
//發送消息通過invokeMethod方法
methodChannel.invokeMethod('dismiss');
flutter中完整代碼如下
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp(route: window.defaultRouteName));
class MyApp extends StatefulWidget {
String route;
MyApp({@required this.route});
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
//收到iOS中傳入指令
print(widget.route);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//創建消息通道並初始化消息名 這個名字要與iOS對應
static const MethodChannel methodChannel = MethodChannel('MSGChannel');
@override
void initState() {
super.initState();
//設置消息監聽
methodChannel.setMethodCallHandler((MethodCall call){
//接收到消息
print(call.method);
print(call.arguments);
return Future.value(true);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('iOS與Flutter通訊'),
),
body: Center(
child: GestureDetector(
onTap: () {
//發送消息通過invokeMethod方法
methodChannel.invokeMethod('dismiss');
},
child: Container(
alignment: Alignment.center,
color: Colors.red,
width: 100,
height: 40,
child: Text(
'點擊返回iOS',
style: TextStyle(
color: Colors.white,
),
),
),
),
),
);
}
}
最終效果圖如下:


需要特別注意一點:當flutter中內容修改后,需要重新執行sh腳本,將flutter重新打包成framework,在iOS中操作才會有效,要不然改動后,iOS中還是使用的之前的老版本
demo放在github中,如果需要請自取:https://github.com/qqcc1388/flutter_iOS_Mixture
參考來源:
https://juejin.im/post/5e228d21518825265c248e7b
https://blog.csdn.net/qq_28478281/article/details/92416686
https://www.jianshu.com/p/c1034513be13
轉載請標注出處https://www.cnblogs.com/qqcc1388/p/12693991.html和參考源 謝謝!