flutter 和ios平台混合开发

作者: 充满活力的早晨 | 来源:发表于2019-03-25 17:01 被阅读549次

    Flutter使用了一个灵活的系统,允许您调用特定平台的API,无论在Android上的Java或Kotlin代码中,还是iOS上的ObjectiveC或Swift代码中均可用。

    Flutter平台特定的API支持不依赖于代码生成,而是依赖于灵活的消息传递的方式:

    • 应用的Flutter部分通过平台通道(platform channel)将消息发送到其应用程序的所在的宿主(iOS或Android)。
    • 宿主监听平台通道,并接收该消息。然后它会调用特定于该平台的API(使用原生编程语言) - 并将响应发送回客户端,即应用程序的Flutter部分。

    框架概述: 平台通道

    使用平台通道在客户端(Flutter UI)和宿主(平台)之间传递消息,如下图所示:

    平台通道

    注意消息和响应是异步传递的,以确保用户界面保持响应(不会挂起)。

    在客户端,MethodChannel (API)可以发送与方法调用相对应的消息。 在宿主平台上,MethodChannel 在Android((API) 和 FlutterMethodChannel iOS (API) 可以接收方法调用并返回结果。这些类允许您用很少的“脚手架”代码开发平台插件。

    • 注意: 如果需要,方法调用也可以反向发送,宿主作为客户端调用Dart中实现的API。 这个quick_actions插件就是一个具体的例子

    平台通道数据类型支持和解码器

    标准平台通道使用标准消息编解码器,以支持简单的类似JSON值的高效二进制序列化,例如 booleans,numbers, Strings, byte buffers, List, Maps(请参阅StandardMessageCodec了解详细信息)。

      • 当您发送和接收值时,这些值在消息中的序列化和反序列化会自动进行。*

    下表显示了如何在宿主上接收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 进入该目录并且创建一个新的应用程序

    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("当前点击次数:$_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平台的混合开发代码。我们准备开发的内容如下

    • 1 点击hit按钮,我们从ios平台获取已经点击的次数
    • 2.我们从ios平台的计时器中获取回调给flutter的计时时间。

    我们调用ios平台的方法是通过MethodChannel 类来实现的
    我们监听来自ios的调用是通过EventChannel 类来实现的

    5. 实现点击hit 按钮获取ios平台回传回来的点击次数

    <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 函数中添加如下函数
      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一样才会调用到函数中
        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
    
    

    这个时候,我们就实现了flutter 平台调用ios平台的函数了

    停止应用,重新启动app。保证app对ios平台的代码进行了重新编译。

    运行结果如下


    6.实现ios平台调用flutter平台的方法

    ios 平台调用flutter 是通过flutter监听事件来完成的

    <1> 引入头文件
    import 'package:flutter/services.dart';
    
    
    <2>添加成员变量
    class _PlatformChannelState extends State<PlatformChannel> {
      static const EventChannel eventChannel =
          EventChannel('time.flutter.io/count');
    ...
    }
    
    <3>添加监听事件
     @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
    

    增加代理函数

    - (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;
    }
    
    

    这样就实现了ios 调用flutter函数


    flutter 中文网相关文章

    相关文章

      网友评论

        本文标题:flutter 和ios平台混合开发

        本文链接:https://www.haomeiwen.com/subject/xfgvvqtx.html