Flutter使用 PlatforView 顯示 iOS 原生 View

前言

  • 2018年一直在使用 flutter 寫項目,從0.2.0開始到現(xiàn)在1.0版本的發(fā)布,終于開始慢慢的爬出坑位了,但是因為部分控件感覺還是不如原生控件好用,一直在摸索怎么將原生view 可以放在 flutter 中并且不會遮擋住 flutter 的 widget。終于,看到官網(wǎng)提供了 PlatformView部件,因為我本身是一名 iOS 開發(fā)人員,這里只提供 iOS 的教程,Android 開發(fā)教程在這里

什么是 PlatformView?

  • PlatformView是 flutter 官方提供的一個可以嵌入 Android 和 iOS 平臺原生 view 的小部件。
  • 在我們實際開發(fā)中,我們遇到一些 flutter 官方?jīng)]有提供的插件可以自己創(chuàng)建編寫插件來實現(xiàn)部分功能,但是原生View在 flutter 中會遮擋住flutter 中的小部件,比如你想使用高德地圖sdk、視頻播放器、直播等原生控件,就無法很好的與 flutter 項目結(jié)合。
  • 之前知道flutter 給 Android(google 的親兒子)提供了 AndroidView可以實現(xiàn)將 view 存放到部件中,教程也不少,無奈,iOS (畢竟不是親的)在網(wǎng)上使用 UiKitView的教程太少,目前就只看到日本的一個作者用 swift寫的 教程,終于有了可以參考的 Demo,下面我就用 Object-C 來說一下教程:

制作插件

我們需要創(chuàng)建一個 項目插件,我這里使用 默認的 Object-C和 Java語言。
Plugin
activity_indicator.dart

首先,我將創(chuàng)建一個StatefulWidget類,在class下顯示本機視圖。
使用文件名activity_indicator.dart編寫以下代碼。


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



typedef void UIActivityIndicatorWidgetCreatedCallback(ActivityIndicatorController controller);

class ActivityIndicatorController {
  ActivityIndicatorController._(int id)
      : _channel = MethodChannel('plugins/activity_indicator_$id');

  final MethodChannel _channel;

  Future<void> start() async {
    return _channel.invokeMethod('start');
  }

  Future<void> stop() async {
    return _channel.invokeMethod('stop');
  }
}

class UIActivityIndicator extends StatefulWidget{

  const UIActivityIndicator({
    Key key,
    this.onActivityIndicatorWidgetCreated,
    this.hexColor,

  }):super(key:key);

  final UIActivityIndicatorWidgetCreatedCallback onActivityIndicatorWidgetCreated;
  final String hexColor;

  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _UIActivityIndicatorState();
  }

}

class _UIActivityIndicatorState extends State<UIActivityIndicator>{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    if(defaultTargetPlatform == TargetPlatform.iOS){
      return UiKitView(
        viewType: "plugins/activity_indicator",
        onPlatformViewCreated:_onPlatformViewCreated,
        creationParams: <String,dynamic>{
          "hexColor":widget.hexColor,
          "hidesWhenStopped":true,

        },
        creationParamsCodec: new StandardMessageCodec(),

      );

    }
    return Text('activity_indicator插件尚不支持$defaultTargetPlatform ');
  }

  void _onPlatformViewCreated(int id){
    if(widget.onActivityIndicatorWidgetCreated == null){
      return;
    }
    widget.onActivityIndicatorWidgetCreated(new ActivityIndicatorController._(id));
  }

}
調(diào)用 iOS視圖

UIKitView用于調(diào)用iOS視圖,如下所示。
對于指定的參數(shù),viewType用于確定本機端的目標View的返回。
對于Android,我們使用AndroidView但指定viewType不會更改。

此外,onPlatformViewCreated可以將ActivityIndi??catorController與UIActivityIndi??cator小部件一起使用。
要將參數(shù)傳遞給本機端,請使用creationParams。

  Widget build(BuildContext context) {
    // TODO: implement build
    if(defaultTargetPlatform == TargetPlatform.iOS){
      return UiKitView(
        viewType: "plugins/activity_indicator",
        onPlatformViewCreated:_onPlatformViewCreated,
        creationParams: <String,dynamic>{
          "hexColor":widget.hexColor,
          "hidesWhenStopped":true,

        },
        creationParamsCodec: new StandardMessageCodec(),

      );

    }
    return Text('activity_indicator插件尚不支持$defaultTargetPlatform ');
  }
從 Flutter 運行原生代碼

使用MethodChannel從Flutter執(zhí)行本機代碼。
這也會編寫接收MethodChannel和invokeMethod參數(shù)的代碼,并在本機端執(zhí)行相應(yīng)的處理。
這次實現(xiàn)它,以便可以通過ActivityIndi??catorController執(zhí)行本機代碼。

class ActivityIndicatorController {
  ActivityIndicatorController._(int id)
      : _channel = MethodChannel('plugins/activity_indicator_$id');

  final MethodChannel _channel;

  Future<void> start() async {
    return _channel.invokeMethod('start');
  }

  Future<void> stop() async {
    return _channel.invokeMethod('stop');
  }
}
main.dart

接下來,編輯example / main.dart并創(chuàng)建一個屏幕。
我將使用我之前創(chuàng)建的UIActivityIndi??cator小部件。

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

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

void main() => runApp(MaterialApp(
  home: ActivityIndicatorExample(),
));

class ActivityIndicatorExample extends StatelessWidget{
  
  ActivityIndicatorController controller;
  
  void _onActivityIndicatorControllerCreated(ActivityIndicatorController _controller){
    controller = _controller;
  }
  
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(title: const Text("加載測試"),),
      body: Stack(
        alignment: Alignment.bottomCenter,
        children: <Widget>[
          new Container(
            child: new Stack(
              children: <Widget>[
                UIActivityIndicator(
                  hexColor: "FF0000",
                  onActivityIndicatorWidgetCreated: _onActivityIndicatorControllerCreated,
                ),
                new Container(
                  alignment: Alignment.center,
                  child: new Text("我是flutter控件,沒有被遮擋~"),
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(left: 45.0,right: 45.0,top: 0.0,bottom: 50.0),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                FloatingActionButton(
                  onPressed: (){
                    controller.start();
                  },
                  child: new Text("Start"),
                ),
                FloatingActionButton(
                  onPressed: (){
                    controller.stop();
                  },
                  child: new Text("Stop"),
                )
              ],
            ),
          )
        ],
      ),
    );
  }

}

iOS 端實現(xiàn)


FlutterActivityIndicator.h

新建類,提供FlutterPlatformView和FlutterPlatformViewFactory協(xié)議

#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>

NS_ASSUME_NONNULL_BEGIN

@interface FlutterActivityIndicatorController : NSObject<FlutterPlatformView>

- (instancetype)initWithWithFrame:(CGRect)frame
                   viewIdentifier:(int64_t)viewId
                        arguments:(id _Nullable)args
                  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;

@end

@interface FlutterActivityIndicatorFactory : NSObject<FlutterPlatformViewFactory>

- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messager;

@end
NS_ASSUME_NONNULL_END
從 Flutter 運行原生代碼

要從Flutter端執(zhí)行本機代碼,可以按如下方式使用MethodChannel。
它產(chǎn)生以前MethodChannel,當(dāng)您從ActivityIndi??catorController時,InvokeMethod,onMethodCall被調(diào)用,所以你遇到在參數(shù)中指定的字符串,以及運行過程中看到它的價值。

 _viewId = viewId;
 NSString* channelName = [NSString stringWithFormat:@"plugins/activity_indicator_%lld", viewId];
_channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger];
__weak __typeof__(self) weakSelf = self;
[_channel setMethodCallHandler:^(FlutterMethodCall *  call, FlutterResult  result) {
     [weakSelf onMethodCall:call result:result];
}];


-(void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{
    if ([[call method] isEqualToString:@"start"]) {
        [_indicator startAnimating];
    }else
    if ([[call method] isEqualToString:@"stop"]){
        [_indicator stopAnimating];
    }
    else {
        result(FlutterMethodNotImplemented);
    }
}

將參數(shù)從 Flutter 傳遞到 iOS

由于Flutter端的creationParams指定的值是args,因此將其轉(zhuǎn)換為類型并設(shè)置為UIActivityIndi??catorView的屬性。

NSDictionary *dic = args;
NSString *hexColor = dic[@"hexColor"];
 bool hidesWhenStopped = [dic[@"hidesWhenStopped"] boolValue];
        
_indicator = [[UIActivityIndicatorView alloc]init];
_indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
_indicator.color = [UIColor colorWithHexString:hexColor];
_indicator.hidesWhenStopped = hidesWhenStopped;
ActivityIndicatorPlugin.m

自動生成文件中,只需要這樣寫

#import "ActivityIndicatorPlugin.h"
#import "FlutterActivityIndicator.h"

@implementation ActivityIndicatorPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    [registrar registerViewFactory:[[FlutterActivityIndicatorFactory alloc] initWithMessenger:registrar.messenger] withId:@"plugins/activity_indicator"];
    
}
@end

要保證你的viewId指定的字符串與你 flutter 端代碼的
ViewType指定的字符串相匹配

最重要的一步操作

要在你的 info.plist中添加

<key>io.flutter.embedded_views_preview</key>
<true/>

要求必須這樣設(shè)置
https://github.com/flutter/flutter/issues/19030#issuecomment-437534853

演示


演示 Demo.gif

Demo 地址

下載地址

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

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

  • 本文主要介紹Flutter相關(guān)的東西,包括Fuchsia、Dart、Flutter特性、安裝以及整體架構(gòu)等內(nèi)容。 ...
    Q吹個大氣球Q閱讀 8,030評論 8 49
  • Flutter是什么? Flutter是一款移動應(yīng)用程序SDK,包含框架、widget和工具,為開發(fā)人員提供了一種...
    三季人閱讀 2,463評論 0 1
  • 前言 Flutter1.0穩(wěn)定版于2018年12月5號的終于發(fā)布了。我們?yōu)榇烁械礁吲d。對于開發(fā)者來說,有了穩(wěn)定版相...
    東經(jīng)315度閱讀 988評論 0 3
  • 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。http://www.itdecent.cn/p/f8dce...
    AWeiLoveAndroid閱讀 14,010評論 7 196
  • 下班回家,繼續(xù)讀《財富自由之路 》,讀完兩個章節(jié),掩面沉思,想起來自己曾經(jīng)的小目標(還不能叫夢想):考報關(guān)證,考會...
    Christy_22ba閱讀 165評論 0 0

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