Flutter集成老的iOS項目
- 2019 年 12 月 19 日
- 筆記
今天來學習下Flutter如何集成在老的iOS項目中 參考iOS老項目如何集成Flutter
方式一
cd some/path/ flutter create --template module my_flutter
方式二
使用Android Studio 創建Flutter Module,一直Next

螢幕快照 2019-12-17 15.28.11.png
創建好的工程目錄如下 some/path/ ├── my_flutter/ │ └── .ios/ │ └── Flutter/ │ └── podhelper.rb └── MyApp/ └── Podfile

螢幕快照 2019-12-17 14.54.23.png
Flutter目錄和iOS目錄必須是同級目錄
將Flutter模組嵌入到現有App
有兩種方法可以將Flutter嵌入到現有應用程式中。 1.Use the CocoaPods dependency manager and installed Flutter SDK. (Recommended.) 2.Create frameworks for the Flutter engine, your compiled Dart code, and all Flutter plugins. Manually embed the frameworks, and update your existing application』s build settings in Xcode.
iOS CocoaPods引入Flutter
source 'https://github.com/CocoaPods/Specs.git' platform :ios, "10.0" flutter_application_path = '../my_flutter/' load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb') def install_pods pod 'AFNetworking', '~> 3.2.1' end target 'MyApp' do install_pods install_all_flutter_pods(flutter_application_path) end
執行 pod install
用Flutter擼一個登錄頁面
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class LoginPage extends StatefulWidget { @override _LoginPageState createState() { return _LoginPageState(); } } class _LoginPageState extends State<LoginPage> { TextEditingController _pwdEditController; TextEditingController _userNameEditController; final FocusNode _userNameFocusNode = FocusNode(); final FocusNode _pwdFocusNode = FocusNode(); @override void initState() { super.initState(); _pwdEditController = TextEditingController(); _userNameEditController = TextEditingController(); _pwdEditController.addListener(() => setState(() => {})); _userNameEditController.addListener(() => setState(() => {})); } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar( backgroundColor: Colors.blue[400], elevation: 0, ), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ buildTopWidget(context), SizedBox(height: 80), buildEditWidget(context), buildLoginButton() ], ), ), ); } /// 頭部 Widget buildTopWidget(BuildContext context) { double height = 200.0; double width = MediaQuery.of(context).size.width; return Container( width: width, height: height, color: Colors.blue[400], child: Stack( overflow: Overflow.visible, // 超出部分顯示 children: <Widget>[ Positioned( left: (width - 90) / 2.0, top: height - 45, child: Container( width: 90.0, height: 90.0, decoration: BoxDecoration( ///陰影 boxShadow: [ BoxShadow(color: Theme.of(context).cardColor, blurRadius: 4.0) ], ///形狀 shape: BoxShape.circle, ///圖片 image: DecorationImage( fit: BoxFit.cover, image: NetworkImage( 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1569298229946&di=ea4ffb2b140ef40035772bbcee7bbdd5&imgtype=0&src=http%3A%2F%2Fcimg2.163.com%2Fcatchimg%2F20090909%2F8112139_3.jpg'), ), ), ), ) ], ), ); } /// 輸入框 Widget buildEditWidget(BuildContext context) { return Container( margin: EdgeInsets.only(left: 15, right: 15), child: Column( children: <Widget>[ buildLoginNameTextField(), SizedBox(height: 20.0), buildPwdTextField(), ], ), ); } /// 用戶名 Widget buildLoginNameTextField() { return Container( height: 40, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.all(Radius.circular(20.0)), ), child: Stack( children: <Widget>[ Positioned( left: 16, top: 11, width: 18, height: 18, child: Image.asset('images/login_user.png'), ), Positioned( left: 45, top: 10, bottom: 10, width: 1, child: Container( color: Colors.black, ), ), Positioned( left: 55, right: 10, top: 10, height: 30, child: TextField( controller: _userNameEditController, focusNode: _userNameFocusNode, decoration: InputDecoration( hintText: "請輸入用戶名", border: InputBorder.none, ), style: TextStyle(fontSize: 14), ), ) ], ), ); } /// 密碼 Widget buildPwdTextField() { return Container( height: 40, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.all(Radius.circular(20.0)), ), child: Stack( children: <Widget>[ Positioned( left: 16, top: 11, width: 18, height: 18, child: Image.asset('images/login_pwd.png'), ), Positioned( left: 45, top: 10, bottom: 10, width: 1, child: Container( color: Colors.black, ), ), Positioned( left: 55, right: 10, top: 10, height: 30, child: TextField( controller: _pwdEditController, focusNode: _pwdFocusNode, decoration: InputDecoration( hintText: "請輸入密碼", border: InputBorder.none, ), style: TextStyle(fontSize: 14), obscureText: true, /// 設置密碼 ), ) ], )); } /// 登錄按鈕 Widget buildLoginButton(){ return Container( margin: EdgeInsets.only(top: 40,left: 10,right: 10), padding: EdgeInsets.all(0), width: MediaQuery.of(context).size.width-20, height: 40, child: RaisedButton( onPressed: () { _getNativeMessage(); }, child: Text("登錄"), color: Colors.blue[400], textColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(20.0)), ), ), ); } void _getNativeMessage() async{ // 對應OC中的FlutterMethodChannel const platform = const MethodChannel('com.allen.test.call'); String message = 'null message'; String result; try { var map = { "userName": "develop","pwd":"Aqi123456"}; // OC回調中對應的」約定」 : getFlutterMessage,[1,2,3]:傳遞參數 result = await platform.invokeMethod('getFlutterMessage',map); } on PlatformException catch (e) { result = "error message $e"; } setState(() { message = result; }); } bool checkInput(){ if(_userNameEditController.text.length == 0){ return false; } else if (_pwdEditController.text.length == 0){ return false; } return true; } }
OC端程式碼
#import <Flutter/Flutter.h> - (IBAction)goFlutterAction:(UIButton *)sender { FlutterViewController *flutterViewController = [FlutterViewController new]; [flutterViewController setInitialRoute:@"flutter_login"]; [self.navigationController pushViewController:flutterViewController animated:YES]; __weak typeof(self) weakSelf = self; FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"com.allen.test.call" binaryMessenger:flutterViewController]; [channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) { // Flutter invokeMethod 特定方法的時候會觸發到這裡 if ([call.method isEqualToString:@"getFlutterMessage"]) { result(@"接收到flutter的消息,回傳資訊from OC"); NSLog(@"接收到flutter的參數:%@",call.arguments); [weakSelf goOCVC:call.arguments]; } }]; }
下面程式碼的作用就是我們要跳轉指定的Flutter 登錄頁面
[flutterViewController setInitialRoute:@"flutter_login"];
Flutter 端設置跳轉登錄的路由 flutter_login
import 'package:flutter/material.dart'; import 'widget/home_page.dart'; import 'widget/login_page.dart'; void main(){ runApp(new MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', debugShowCheckedModeBanner: false,// 顯示和隱藏 theme: ThemeData( primarySwatch: Colors.blue, ), home: HomePage(), routes: <String ,WidgetBuilder>{ "flutter_login": (_) => new LoginPage(), }, ); } }
Flutter端傳參數給OC
void _getNativeMessage() async{ // 對應OC中的FlutterMethodChannel const platform = const MethodChannel('com.allen.test.call'); String message = 'null message'; String result; try { var map = { "userName": "develop","pwd":"123456"}; // OC回調中對應的」約定」 : getFlutterMessage,[1,2,3]:傳遞參數 result = await platform.invokeMethod('getFlutterMessage',map); } on PlatformException catch (e) { result = "error message $e"; } setState(() { message = result; }); }
調用goFlutterAction方法就會跳轉到Flutter的登錄頁面,點擊Flutter的登錄就會把參數傳給OC,這就是簡單的集成。

