flutter 和ios平臺混合開發(fā)

Flutter使用了一個靈活的系統(tǒng),允許您調(diào)用特定平臺的API,無論在Android上的Java或Kotlin代碼中,還是iOS上的ObjectiveC或Swift代碼中均可用。

Flutter平臺特定的API支持不依賴于代碼生成,而是依賴于靈活的消息傳遞的方式:

  • 應用的Flutter部分通過平臺通道(platform channel)將消息發(fā)送到其應用程序的所在的宿主(iOS或Android)。
  • 宿主監(jiān)聽平臺通道,并接收該消息。然后它會調(diào)用特定于該平臺的API(使用原生編程語言) - 并將響應發(fā)送回客戶端,即應用程序的Flutter部分。

框架概述: 平臺通道

使用平臺通道在客戶端(Flutter UI)和宿主(平臺)之間傳遞消息,如下圖所示:

平臺通道

注意消息和響應是異步傳遞的,以確保用戶界面保持響應(不會掛起)。

在客戶端,MethodChannel (API)可以發(fā)送與方法調(diào)用相對應的消息。 在宿主平臺上,MethodChannel 在Android((API) 和 FlutterMethodChannel iOS (API) 可以接收方法調(diào)用并返回結果。這些類允許您用很少的“腳手架”代碼開發(fā)平臺插件。

  • 注意: 如果需要,方法調(diào)用也可以反向發(fā)送,宿主作為客戶端調(diào)用Dart中實現(xiàn)的API。 這個quick_actions插件就是一個具體的例子

平臺通道數(shù)據(jù)類型支持和解碼器

標準平臺通道使用標準消息編解碼器,以支持簡單的類似JSON值的高效二進制序列化,例如 booleans,numbers, Strings, byte buffers, List, Maps(請參閱StandardMessageCodec了解詳細信息)。

    • 當您發(fā)送和接收值時,這些值在消息中的序列化和反序列化會自動進行。*

下表顯示了如何在宿主上接收Dart值,反之亦然:

Dart Android iOS
null null nil (NSNull when nested)
bool java.lang.Boolean NSNumber numberWithBool:
int java.lang.Integer NSNumber numberWithInt:
int, if 32 bits not enough java.lang.Long NSNumber numberWithLong:
int, if 64 bits not enough java.math.BigInteger FlutterStandardBigInteger
double java.lang.Double NSNumber numberWithDouble:
String java.lang.String NSString
Uint8List byte[] FlutterStandardTypedData typedDataWithBytes:
Int32List int[] FlutterStandardTypedData typedDataWithInt32:
Int64List long[] FlutterStandardTypedData typedDataWithInt64:
Float64List double[] FlutterStandardTypedData typedDataWithFloat64:
List java.util.ArrayList NSArray
Map java.util.HashMap NSDictionary

舉例

1打開終端隨便cd一個目錄下,生成flutterIosMix 目錄

 mkdir flutterIosMix |ls

2 進入該目錄并且創(chuàng)建一個新的應用程序

cd flutterIosMix 
flutter create flutteriosmix

這個時候的目錄結構如下


目錄結構

3.用vscode 打開該工程并運行該工程

結果如下:


image.png

這個時候有默認工程的代碼

4.修改main.dart 代碼

修改main.dart 代碼如下

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

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: PlatformChannel(),
    );
  }
}

class PlatformChannel extends StatefulWidget {
  @override
  _PlatformChannelState createState() => _PlatformChannelState();
}



class _PlatformChannelState extends State<PlatformChannel> {

  int _hitNum = 0;
  int _time = 0;

  Future<void> _hitEvent() async{

  }

 @override
  Widget build(BuildContext context) {
    return Material(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text("當前點擊次數(shù):$_hitNum", key: const Key('hitEvent')),
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: RaisedButton(
                  child: const Text('hit'),
                  onPressed: _hitEvent,
                ),
              ),
            ],
          ),
          Text("計時器時間${_time}s"),
        ],
      ),
    );
  }
}

運行上述代碼結果如下


這是準備好的工程基本工程.接下來我們就準備flutter 和ios平臺的混合開發(fā)代碼。我們準備開發(fā)的內(nèi)容如下

  • 1 點擊hit按鈕,我們從ios平臺獲取已經(jīng)點擊的次數(shù)
  • 2.我們從ios平臺的計時器中獲取回調(diào)給flutter的計時時間。

我們調(diào)用ios平臺的方法是通過MethodChannel 類來實現(xiàn)的
我們監(jiān)聽來自ios的調(diào)用是通過EventChannel 類來實現(xiàn)的

5. 實現(xiàn)點擊hit 按鈕獲取ios平臺回傳回來的點擊次數(shù)

<1> 添加引用文件
import 'package:flutter/services.dart';

在main.dart 文件中頭部添加上述代碼。

<2>類_PlatformChannelState增加一個成員變量
class _PlatformChannelState extends State<PlatformChannel> {
  static const MethodChannel methodChannel =
      MethodChannel('hit.flutter.io/count');
...
}

這里我們生成一個MethodChannel類型的變量methodChannel 并且實例化

"hit.flutter.io/count" 這里需要注意,這是與ios平臺橋接的key,可以隨便命名,但是必須和ios平臺接受時候的key 一致。

<3> 在_hitEvent 函數(shù)中添加如下函數(shù)
  Future<void> _hitEvent() async{
  int hitNum;
  try {
      final int result = await methodChannel.invokeMethod('getBatteryLevel');
      hitNum =result;
    } on PlatformException {
    }
    if (hitNum !=null) {
    setState(() {
      _hitNum = hitNum;
    });  
    } 
  }

這個時候保存main.dart 代碼。點擊hit按鈕是崩潰的

<4> 打開ios工程代碼

打開ios工程ios->右鍵reveal in finder -> xcode 打開該工程

image.png
<5>編輯ios工程代碼

打開appdelegate.m 文件,修改文件如下

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#import <Flutter/Flutter.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
    FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
    ///hit.flutter.io/count 必須與flutter 請求的key一樣才會調(diào)用到函數(shù)中
    FlutterMethodChannel* batteryChannel = [FlutterMethodChannel methodChannelWithName:@"hit.flutter.io/count" binaryMessenger:controller];
    [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        ///這個是flutter 請求的事件
        if ([@"hitCount" isEqualToString:call.method]) {
            static int count =0;
            return result(@(++count));
        }
        
        
    }];
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

這個時候,我們就實現(xiàn)了flutter 平臺調(diào)用ios平臺的函數(shù)了

停止應用,重新啟動app。保證app對ios平臺的代碼進行了重新編譯。

運行結果如下


6.實現(xiàn)ios平臺調(diào)用flutter平臺的方法

ios 平臺調(diào)用flutter 是通過flutter監(jiān)聽事件來完成的

<1> 引入頭文件
import 'package:flutter/services.dart';

<2>添加成員變量
class _PlatformChannelState extends State<PlatformChannel> {
  static const EventChannel eventChannel =
      EventChannel('time.flutter.io/count');
...
}
<3>添加監(jiān)聽事件
 @override
  void initState() {
    super.initState();
    eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
  }

  void _onEvent(Object event) {
    setState(() {
      print("{$event}");
      _chargingStatus =
          "Battery status: ${event}";
    });
  }

  void _onError(Object error) {
    setState(() {
      _chargingStatus = 'Battery status: unknown.';
    });
  }

<4>打開ios工程代碼 并添加代碼如下
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];

···
    FlutterEventChannel* chargingChannel = [FlutterEventChannel eventChannelWithName:@"time.flutter.io/count" binaryMessenger:controller];
    [chargingChannel setStreamHandler:self];
    
 ···
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

給appdelegate類添加成員變量

@interface AppDelegate()
{
    FlutterEventSink _eventSink;

}
@end

增加代理函數(shù)

- (FlutterError*)onListenWithArguments:(id)arguments
                             eventSink:(FlutterEventSink)eventSink {
    _eventSink = eventSink;
    static int mm = 0;
    [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        if (!_eventSink) return;
        _eventSink(@(++mm));
    }];
    return nil;
}


- (FlutterError*)onCancelWithArguments:(id)arguments {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    _eventSink = nil;
    return nil;
}

這樣就實現(xiàn)了ios 調(diào)用flutter函數(shù)


flutter 中文網(wǎng)相關文章

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容