我们在使用Flutter开发跨平台开发移动APP时,会遇到Flutter
的组件满足不了原生的效果,部分控件不如原生控件好用时,就想在Flutter 的Widget
中使用iOS
原生View
来组合实现良好的效果。PlatformView
是 Flutter
官方提供的一个可以嵌入 Android 和 iOS 平台原生
view 的小部件。
View
在 Flutter 中会遮挡Widget
,比如你想使用高德地图sdk、视频播放器、直播等原生控件,就无法很好的与 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。
iOS端的UiKitView
目前还只是preview状态, 默认是不支持的, 需要手动打开开关。 在info.plist
文件中新增一行io.flutter.embedded_views_preview
为true
.
io.flutter.embedded_views_preview
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执行本机代码,也会编写接收MethodChannel
和invokeMethod
参数的代码,并在本机端执行相应的处理。这次实现它以便可以通过ActivityIndicatorController
执行本机代码。
UIKitView用于调用iOS视图,参数viewType
用于确定本机端的目标View的返回。对于Android,我们使用AndroidView但指定viewType不会更改。此外,onPlatformViewCreated可以将ActivityIndicatorController
与UIActivityIndicator
小部件一起使用。要将参数传递给OC端,请使用creationParams
。
接下来,编辑example/lib/main.dart并创建一个屏幕。将使用之前创建的UIActivityIndicator小部件。
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"),
)
],
),
)
],
),
);
}
}
在ios/Classes增加FlutterActivityIndicator、UIColor+RGB
文件
新建类,提供FlutterPlatformView
和FlutterPlatformViewFactory
协议
// 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
// 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
// activity_indicator
#import
NS_ASSUME_NONNULL_BEGIN
@interface UIColor (RGB)
+ (UIColor*)colorWithHexString:(NSString*)stringToConvert;
@end
NS_ASSUME_NONNULL_END
// 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
将原来的代码注释掉,修改为如下代码:
/*
#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
指定的字符串相匹配
Flutter使用 PlatforView 显示 iOS 原生 View
Flutter PlatformView: 如何通过原生view创建widget