<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          iOS 原生項(xiàng)目嵌入 Flutter

          共 14555字,需瀏覽 30分鐘

           ·

          2021-12-27 02:54


          ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????


          作者:晨曦_iOS

          https://juejin.cn/post/7036393182053007367


          雖然一般不建議在原生項(xiàng)目中嵌入Flutter,但是Flutter也可以支持這種方式,下面我們來(lái)看一下具體的實(shí)現(xiàn)。

          原生嵌入 Flutter 的工程配置

          如圖,我們想使原生嵌入Flutter的話,使用Android Studio創(chuàng)建項(xiàng)目的時(shí)候就要選擇Module進(jìn)行創(chuàng)建,使之作為一個(gè)模塊來(lái)開(kāi)發(fā)。

          打開(kāi)我們新建的flutter_module工程目錄可以看到,與創(chuàng)建的Flutter App相比,文件里面仍然有AndroidiOS工程文件,但是這里只是為了讓我們做調(diào)試用的,而且這兩個(gè)文件都是隱藏文件,不過(guò)AndroidiOS工程中不建議加入原生代碼,而且即使加了,打包的時(shí)候也不會(huì)被打包進(jìn)去。flutter_module是一個(gè)純Flutter的工程。

          • Podfile文件配置
          flutter_application_path = '../flutter_module'
          load File.join(flutter_application_path,'.iOS','Flutter','podhelper.rb')

          platform :ios, '9.0'

          target 'NativeDemo' do
            install_all_flutter_pods(flutter_application_path)
            use_frameworks!

            # Pods for NativeDemo

          end

          我們使用Xcode創(chuàng)建一個(gè)原生工程,NativeDemo,使用終端,cdNativeDemo目錄下,pod init,然后配置Podfile文件,然后執(zhí)行pod install

          pod install完成之后,打開(kāi)原生項(xiàng)目,引用頭文件#import <Flutter/Flutter.h>,可以成功的話就代表配置成功,現(xiàn)在的話原生工程與Flutter就有聯(lián)系了,下面我們就可以實(shí)現(xiàn)代碼了,來(lái)使原生工程中嵌入Flutter

          原生項(xiàng)目調(diào)起 Flutter 頁(yè)面

          • 原生代碼部分
          #import "ViewController.h"
          #import <Flutter/Flutter.h>

          @interface ViewController ()
          @property(nonatomic, strong) FlutterEngine* flutterEngine;
          @property(nonatomic, strong) FlutterViewController* flutterVc;
          @property(nonatomic, strong) FlutterBasicMessageChannel * msgChannel;
          @end

          @implementation ViewController

          -(FlutterEngine *)flutterEngine
          {
              if (!_flutterEngine) {
                  FlutterEngine * engine = [[FlutterEngine alloc] initWithName:@"hank"];
                  if (engine.run) {
                      _flutterEngine = engine;
                  }
              }
              return _flutterEngine;
          }

          - (IBAction)pushFlutter:(id)sender {
              
              self.flutterVc.modalPresentationStyle = UIModalPresentationFullScreen;

              //創(chuàng)建channel
              FlutterMethodChannel * methodChannel = [FlutterMethodChannel methodChannelWithName:@"one_page" binaryMessenger:self.flutterVc.binaryMessenger];
              //告訴Flutter對(duì)應(yīng)的頁(yè)面
              [methodChannel invokeMethod:@"one" arguments:nil];
              
              //彈出頁(yè)面
              [self presentViewController:self.flutterVc animated:YES completion:nil];
              
              //監(jiān)聽(tīng)退出
              [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
                  //如果是exit我就退出頁(yè)面!
                  if ([call.method isEqualToString:@"exit"]) {
                      [self.flutterVc dismissViewControllerAnimated:YES completion:nil];
                  }
              }];
          }
          - (IBAction)pushFlutterTwo:(id)sender {
              self.flutterVc.modalPresentationStyle = UIModalPresentationFullScreen;

              //創(chuàng)建channel
              FlutterMethodChannel * methodChannel = [FlutterMethodChannel methodChannelWithName:@"two_page" binaryMessenger:self.flutterVc.binaryMessenger];
              //告訴Flutter對(duì)應(yīng)的頁(yè)面
              [methodChannel invokeMethod:@"two" arguments:nil];
              
              //彈出頁(yè)面
              [self presentViewController:self.flutterVc animated:YES completion:nil];
              
              //監(jiān)聽(tīng)退出
              [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
                  //如果是exit我就退出頁(yè)面!
                  if ([call.method isEqualToString:@"exit"]) {
                      [self.flutterVc dismissViewControllerAnimated:YES completion:nil];
                  }
              }];
          }

          - (void)viewDidLoad {
              [super viewDidLoad];
              
              self.flutterVc = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
              self.msgChannel = [FlutterBasicMessageChannel messageChannelWithName:@"messageChannel" binaryMessenger:self.flutterVc.binaryMessenger];
              
              [self.msgChannel setMessageHandler:^(id  _Nullable message, FlutterReply  _Nonnull callback) {
                  NSLog(@"收到Flutter的:%@",message);
              }];
              
          }

          -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
          {
              static int a = 0;
              [self.msgChannel sendMessage:[NSString stringWithFormat:@"%d",a++]];
          }

          在原生代碼部分我們定義了三個(gè)屬性,flutterEngine代表引擎對(duì)象,flutterVcFlutterViewController類(lèi)型的控制器對(duì)象,msgChannel是通信方式中的一種channel,為FlutterBasicMessageChannel類(lèi)型,下面會(huì)有介紹。

          在這里我們實(shí)現(xiàn)了pushFlutterpushFlutterTwo兩個(gè)方法,代表調(diào)起兩個(gè)不同的Flutter頁(yè)面。在這兩個(gè)方法中,我們首先創(chuàng)建methodChannel對(duì)象,并分別傳入onetwo兩個(gè)字符串標(biāo)識(shí),并且binaryMessenger傳參傳入的都是self.flutterVc.binaryMessenger。在兩個(gè)方法中分別調(diào)用invokeMethod方法,向Flutter頁(yè)面發(fā)送消息,然后彈出頁(yè)面,并且實(shí)現(xiàn)setMethodCallHandler方法,在閉包中判斷call.method isEqualToString:@"exit",進(jìn)行頁(yè)面的退出。

          • Flutter 代碼部分
          // ignore_for_file: avoid_print

          import 'package:flutter/material.dart';
          import 'package:flutter/services.dart';

          void main() => runApp(const MyApp());

          class MyApp extends StatefulWidget {
            const MyApp({Key? key}) : super(key: key);

            @override
            _MyAppState createState() => _MyAppState();
          }

          class _MyAppState extends State<MyApp> {
            final MethodChannel _oneChannel = const MethodChannel('one_page');
            final MethodChannel _twoChannel = const MethodChannel('two_page');
            final BasicMessageChannel _messageChannel =
                const BasicMessageChannel('messageChannel', StandardMessageCodec());

            String pageIndex = 'one';

            @override
            void initState() {
              super.initState();

              _messageChannel.setMessageHandler((message) {
                print('收到來(lái)自iOS的$message');
                return Future(() {});
              });

              _oneChannel.setMethodCallHandler((call) {
                pageIndex = call.method;
                print(call.method);
                setState(() {});
                return Future(() {});
              });
              _twoChannel.setMethodCallHandler((call) {
                pageIndex = call.method;
                print(call.method);
                setState(() {});
                return Future(() {});
              });
            }

            @override
            Widget build(BuildContext context) {
              return MaterialApp(
                title: 'Flutter Demo',
                theme: ThemeData(
                  primarySwatch: Colors.blue,
                ),
                home: _rootPage(pageIndex),
              );
            }

            //根據(jù)pageIndex來(lái)返回頁(yè)面!
            Widget _rootPage(String pageIndex) {
              switch (pageIndex) {
                case 'one':
                  return Scaffold(
                    appBar: AppBar(
                      title: Text(pageIndex),
                    ),
                    body: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        ElevatedButton(
                          onPressed: () {
                            _oneChannel.invokeMapMethod('exit');
                          },
                          child: Text(pageIndex),
                        ),
                        TextField(
                          onChanged: (String str) {
                            _messageChannel.send(str);
                          },
                        )
                      ],
                    ),
                  );
                case 'two':
                  return Scaffold(
                    appBar: AppBar(
                      title: Text(pageIndex),
                    ),
                    body: Center(
                      child: ElevatedButton(
                        onPressed: () {
                          _twoChannel.invokeMapMethod('exit');
                        },
                        child: Text(pageIndex),
                      ),
                    ),
                  );
                default:
                  return Scaffold(
                    appBar: AppBar(
                      title: Text(pageIndex),
                    ),
                    body: Center(
                      child: ElevatedButton(
                        onPressed: () {
                          const MethodChannel('default_page').invokeMapMethod('exit');
                        },
                        child: Text(pageIndex),
                      ),
                    ),
                  );
              }
            }
          }

          Flutter代碼中我們定義了_oneChannel_twoChannel這兩個(gè)變量用了接收原生頁(yè)面發(fā)送的消息,并且向原生頁(yè)面發(fā)送消息。定義了變量pageIndex用來(lái)標(biāo)識(shí)創(chuàng)建那個(gè)頁(yè)面。

          initState方法中調(diào)用setMethodCallHandler方法,獲取到原生頁(yè)面?zhèn)鱽?lái)的數(shù)據(jù)并賦值給pageIndex,然后調(diào)用setState方法。

          build方法中我們調(diào)用_rootPage方法來(lái)判斷創(chuàng)建哪個(gè)頁(yè)面。并且分別在這兩個(gè)頁(yè)面的點(diǎn)擊事件中調(diào)用invokeMapMethod方法,代表退出頁(yè)面,原生頁(yè)面在setMethodCallHandler閉包中接收到exit數(shù)據(jù)后就會(huì)調(diào)用[self.flutterVc dismissViewControllerAnimated:YES completion:nil],進(jìn)行頁(yè)面的退出。

          Flutter 與原生的通信

          • MethodChannelFlutterNative端相互調(diào)用,調(diào)用后可以返回結(jié)果,可以Native端主動(dòng)調(diào)用,也可以Flutter主動(dòng)調(diào)用,屬于雙向通信。此方式為最常用的方式,Native端調(diào)用需要在主線程中執(zhí)行。
          • BasicMessageChannel: 用于使用指定的編解碼器對(duì)消息進(jìn)行編碼和解碼,屬于雙向通信,可以Native端主動(dòng)調(diào)用,也可Flutter主動(dòng)調(diào)用。
          • EventChannel:用于數(shù)據(jù)流(event streams)的通信,Native端主動(dòng)發(fā)送數(shù)據(jù)給Flutter,通常用于狀態(tài)的監(jiān)聽(tīng),比如網(wǎng)絡(luò)變化、傳感器數(shù)據(jù)等。

          Flutter與原生通信有三種方式,Flutter為我們提供了三種Channel,分別是MethodChannelBasicMessageChannelEventChannel。但是我們比較常用的就是MethodChannelBasicMessageChannel這兩種。因?yàn)?code style="font-size: 14px;font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">MethodChannel前面已經(jīng)講過(guò)了,所以這里我們介紹一下BasicMessageChannel的用法。

          BasicMessageChannel 用法

          BasicMessageChannel的用法與FlutterMethodChannel類(lèi)似,在上面的代碼示例中,首先在Flutter代碼中我們也是定義一個(gè)BasicMessageChannel類(lèi)型的變量_messageChannel,在_messageChannelsetMessageHandler閉包中接收來(lái)自于原生頁(yè)面發(fā)來(lái)的消息,調(diào)用_messageChannelsend方法向原生頁(yè)面進(jìn)行通信,在輸入框文字變化的時(shí)候都會(huì)調(diào)用send方法。在原生代碼中也是類(lèi)似,定義了msgChannel屬性,setMessageHandler中的block負(fù)責(zé)接收消息,sendMessage發(fā)送消息,在touchesBegan中向Flutter傳遞a的累加值。

          作者:晨曦_iOS

          https://juejin.cn/post/7036393182053007367


          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!

          點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點(diǎn)這里好文分享給更多人↓↓

          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人尻人人摸 | 影音先锋久久精品视频 | 日本操逼片 | 国产精品 男同 | 亚洲影音先锋 |