Flutter开发使用PlatformView显示iOS原生View(50)

我们在使用Flutter开发跨平台开发移动APP时,会遇到Flutter的组件满足不了原生的效果,部分控件不如原生控件好用时,就想在Flutter 的Widget 中使用iOS原生View来组合实现良好的效果。PlatformViewFlutter 官方提供的一个可以嵌入 Android 和 iOS 平台原生 view 的小部件。

什么是 PlatformView?

  • PlatformView是 flutter 官方提供的一个可以嵌入 Android 和 iOS 平台原生 view 的小部件。
  • 在我们实际开发中,我们遇到一些 Flutter 官方没有提供的插件可以自己创建编写插件来实现部分功能,但是原生View在 Flutter 中会遮挡Widget,比如你想使用高德地图sdk、视频播放器、直播等原生控件,就无法很好的与 Flutter 项目结合。
  • Flutter 给Android提供了 AndroidView可以实现将 View 存放到Widget中。iOS 使用 UiKitView 将 View 存放到Widget中。

PlatformView是Flutter官方在1.0版本推出的组件,以解决开发者想在Flutter中嵌入Android和iOS平台原生View的Widget。例如想嵌入地图、视频播放器等原生组件,对于想尝试Flutter,但是又想低成本的迁移复杂组件的团队,可以尝试PlatformView,在 Dart 中的类对应到 iOS 和 Android 平台分别是UIKitView和AndroidView。

Flutter 中嵌套iOS原生组件流程:

1 info.plist文件设置

iOS端的UiKitView目前还只是preview状态, 默认是不支持的, 需要手动打开开关。 在info.plist文件中新增一行io.flutter.embedded_views_previewtrue.

io.flutter.embedded_views_preview

在这里插入图片描述

2 制作插件工程(名字:activity_indicator)Flutter开发使用PlatformView显示iOS原生View(50)_第1张图片

3 在lib文件夹下的activity_indicator.dart 中编写如下代码:

Flutter开发使用PlatformView显示iOS原生View(50)_第2张图片

import 'dart:async';

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 start() async {
    return _channel.invokeMethod('start');
  }

  Future 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 createState() {
    // TODO: implement createState
    return _UIActivityIndicatorState();
  }

}

class _UIActivityIndicatorState extends State{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    if(defaultTargetPlatform == TargetPlatform.iOS){
      return UiKitView(
        viewType: "plugins/activity_indicator",
        onPlatformViewCreated:_onPlatformViewCreated,
        creationParams: {
          "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));
  }
}

上面代码释意:
使用MethodChannel从Flutter执行本机代码,也会编写接收MethodChannelinvokeMethod参数的代码,并在本机端执行相应的处理。这次实现它以便可以通过ActivityIndi​​catorController执行本机代码。

UIKitView用于调用iOS视图,参数viewType用于确定本机端的目标View的返回。对于Android,我们使用AndroidView但指定viewType不会更改。此外,onPlatformViewCreated可以将ActivityIndi​​catorControllerUIActivityIndi​​cator小部件一起使用。要将参数传递给OC端,请使用creationParams

4 main.dart 修改

接下来,编辑example/lib/main.dart并创建一个屏幕。将使用之前创建的UIActivityIndi​​cator小部件。
Flutter开发使用PlatformView显示iOS原生View(50)_第3张图片

import 'package:flutter/material.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: [
          new Container(
            child: new Stack(
              children: [
                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: [
                FloatingActionButton(
                  onPressed: (){
                    controller.start();
                  },
                  child: new Text("Start"),
                ),
                FloatingActionButton(
                  onPressed: (){
                    controller.stop();
                  },
                  child: new Text("Stop"),
                )
              ],
            ),
          )
        ],
      ),
    );
  }
}

5 iOS 端实现

在ios/Classes增加FlutterActivityIndicator、UIColor+RGB文件

新建类,提供FlutterPlatformViewFlutterPlatformViewFactory协议

  • FlutterActivityIndicator.h
//  FlutterActivityIndicator.h
//  activity_indicator

#import 
#import 

NS_ASSUME_NONNULL_BEGIN

@interface FlutterActivityIndicatorController : NSObject

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

@end

@interface FlutterActivityIndicatorFactory : NSObject

- (instancetype)initWithMessenger:(NSObject*)messager;

@end
NS_ASSUME_NONNULL_END
  • FlutterActivityIndicator.m
//  FlutterActivityIndicator.m
//  activity_indicator

#import "FlutterActivityIndicator.h"
#import "UIColor+RGB.h"

@implementation FlutterActivityIndicatorFactory{
    NSObject*_messenger;
}

- (instancetype)initWithMessenger:(NSObject *)messager{
    self = [super init];
    if (self) {
        _messenger = messager;
    }
    return self;
}

-(NSObject *)createArgsCodec{
    return [FlutterStandardMessageCodec sharedInstance];
}

-(NSObject *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args{
    
   FlutterActivityIndicatorController*activity = [[FlutterActivityIndicatorController alloc] initWithWithFrame:frame viewIdentifier:viewId arguments:args binaryMessenger:_messenger];
    
    return activity;
}

@end

@implementation FlutterActivityIndicatorController{
    int64_t _viewId;
    FlutterMethodChannel* _channel;
    UIActivityIndicatorView * _indicator;
}

- (instancetype)initWithWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args binaryMessenger:(NSObject *)messenger{
    if ([super init]) {
        
        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;
        
        _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];
        }];
    }
    
    return self;
}

-(UIView *)view{
    return _indicator;
}

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

@end

OC 端接受参数,工厂方法创建View对象

  • UIColor+RGB.h
//  UIColor+RGB.h
//  activity_indicator

#import 
NS_ASSUME_NONNULL_BEGIN

@interface UIColor (RGB)
+ (UIColor*)colorWithHexString:(NSString*)stringToConvert;
@end

NS_ASSUME_NONNULL_END
  • UIColor+RGB.m
//  UIColor+RGB.m
//  activity_indicator

#import "UIColor+RGB.h"

@implementation UIColor (RGB)

+ (UIColor*)colorWithHexString:(NSString*)stringToConvert{
    if([stringToConvert hasPrefix:@"#"])
    {
        stringToConvert = [stringToConvert substringFromIndex:1];
    }
    NSScanner*scanner = [NSScanner scannerWithString:stringToConvert];
    unsigned hexNum;
    if(![scanner scanHexInt:&hexNum])
    {
        return nil;
    }
    return[UIColor colorWithRGBHex:hexNum];
}

+ (UIColor*)colorWithRGBHex:(UInt32)hex{
    int r = (hex >>16) &0xFF;
    int g = (hex >>8) &0xFF;
    int b = (hex) &0xFF;
    return[UIColor colorWithRed:r /255.0f
                         green:g /255.0f
                          blue:b /255.0f
                         alpha:1.0f];
}
@end

6 修改自动生成的文件 ActivityIndicatorPlugin.m

将原来的代码注释掉,修改为如下代码:

/*
#import "ActivityIndicatorPlugin.h"

@implementation ActivityIndicatorPlugin
+ (void)registerWithRegistrar:(NSObject*)registrar {
  FlutterMethodChannel* channel = [FlutterMethodChannel
      methodChannelWithName:@"activity_indicator"
            binaryMessenger:[registrar messenger]];
  ActivityIndicatorPlugin* instance = [[ActivityIndicatorPlugin alloc] init];
  [registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  if ([@"getPlatformVersion" isEqualToString:call.method]) {
    result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
  } else {
    result(FlutterMethodNotImplemented);
  }
}

@end
*/

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

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

@end

保证你的viewId指定的字符串与flutter端代码的ViewType指定的字符串相匹配

7 演示

Flutter开发使用PlatformView显示iOS原生View(50)_第4张图片

文章参考

Flutter使用 PlatforView 显示 iOS 原生 View
Flutter PlatformView: 如何通过原生view创建widget

你可能感兴趣的:(Flutter开发教程)