预览的效果
创建Plugin
通过Android Studio 创建一个新的Flutter Plugin。
我们选择Flutter Plugin,这个是创建Plugin的
同样Flutter platform view也属于一个Plugin范畴;
分析一下我们创建的Plugin
如下图所示:我们的Plugin包含以下目录和文件,我们只分析对我们有用的问题;
- android
- ios
- example
- lib
- pubspec.yaml
我们分析仅已iOS开发为例,所以我们需要设计的文件夹不包括Android(Android的实现跟iOS的实现基本一致)
ios:
- assets
- Classes
同学们是不是很熟悉,这个和我们自定义的Cocoapods组件库。
如果不涉及图片或者其它资源,
我们只需要在Classes文件夹下提供我们iOS侧的实现即可。
如何实现请看下面的【对应平台实现】部分
lib:
- xxx.dart
这个文件夹下存放的是根据我们创建的文件夹名称命名的dart文件
对于简单的视图组件来说,我们在这一个文件内定义就可以了。
在这个组件内我们做了视图的定义、传递的参数、通讯的方法
具体的开发请看【自定义Widget】部分
pubspec.yaml
这个跟创建一个plugin是一个道理,
里面包含了我们创建的platformview plugin的一些基本信息
名称、开发者、版本号等。
一般我们只有在发布组件或者内部修改组件的时候
才会更改此文件。
自定义Widget
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
// 定义callback
typedef void JWCustomViewCreateCallback(JWCustomViewController controller);
// 创建通讯类
class JWCustomViewController {
// 构建通道
JWCustomViewController._(int id)
: _channel = MethodChannel('wanmei.custom.plugins/custom_$id');
final MethodChannel _channel;
// 定义方法
Future<void> sayHello() async {
return _channel.invokeMethod('sayHello');
}
}
// 定义有状态组件
class JWCustomView extends StatefulWidget {
// 定义传递进来的参数
const JWCustomView({Key key, this.onCreateCallback, this.showText}) : super (key: key);
final JWCustomViewCreateCallback onCreateCallback;
final String showText;
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _JWCustomViewState();
}
}
// 包裹组件返回
class _JWCustomViewState extends State<JWCustomView> {
@override
Widget build(BuildContext context) {
// TODO: implement build
// 进行平台判断
if (defaultTargetPlatform == TargetPlatform.iOS) {
// ios返回代码
return UiKitView(
viewType: "wanmei.custom.plugins/custom",
onPlatformViewCreated: _onPlatformViewCreated, // native端代码的初始化
creationParams: <String, dynamic> { // 传递的参数
"showText": widget.showText,
"otherColor": 'green',
},
creationParamsCodec: StandardMessageCodec(), // 传递参数的编码格式,如果需要传参。则是必须的;
);
} else if (defaultTargetPlatform == TargetPlatform.android) {
// android返回视图
// 我们这里暂时不考虑安卓平台的实现
return Text('暂时不考虑安卓平台的实现');
} else {
return Text('插件尚未支持对应平台');
}
return Container();
}
// 创建对应平台视图
void _onPlatformViewCreated(int id) {
// 如果创建方法的回调是空
if (widget.onCreateCallback == null) {
return;
}
// 不为空则调用平台方创建我们需要的视图
widget.onCreateCallback(JWCustomViewController._(id));
}
}
对应平台实现(iOS)
这部分大概有2个文件:
- xxxxPlugin.h
- xxxxCustom.h
xxxPlugin.h
#import <Flutter/Flutter.h>
@interface FlutterpluginlabelPlugin : NSObject<FlutterPlugin>
@end
xxxPlugin.m
#import "FlutterpluginlabelPlugin.h"
#import "FlutterCustomView.h"
@implementation FlutterpluginlabelPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
// 首先删除plugin模板生成的注册代码。因为我们修改了通UN的字符,而且我们不需要在这个文件内修改插件对应的action
// 通过自定义组件名进行通信开启注册ID
[registrar registerViewFactory:[[FlutterCustomViewFactory alloc] initWithMessenger:registrar.messenger] withId:@"wanmei.custom.plugins/custom"];
}
// 这是插件开发时候生成的默认代码,我们不需要,删除它getPlatformVersion
@end
xxxCustom.h
//
// FlutterCustomView.h
// flutterpluginlabel
//
// Created by wangjiawei on 2020/4/29.
//
#import <Foundation/Foundation.h>
// 引入Flutter插件
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface FlutterCustomView : NSObject
@end
// 实现自定时视图
@interface FlutterCustomViewController : NSObject<FlutterPlatformView>
- (instancetype)initWithWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
@end
// 实现消息通道
@interface FlutterCustomViewFactory : NSObject<FlutterPlatformViewFactory>
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messager;
@end
NS_ASSUME_NONNULL_END
xxxCustom.m
//
// FlutterCustomView.m
// flutterpluginlabel
//
// Created by wangjiawei on 2020/4/29.
//
#import "FlutterCustomView.h"
@implementation FlutterCustomView
@end
#pragma mark - 视图界面
@implementation FlutterCustomViewController {
// 持有视图属性
int64_t _viewId;
FlutterMethodChannel *_channel;
UILabel *_nativeLabel;
}
// 构建视图
- (instancetype)initWithWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args binaryMessenger:(NSObject<FlutterBinaryMessenger> *)messenger{
if ([super init]) {
// 获取参数
NSDictionary *dic = args;
NSString *otherColor = dic[@"otherColor"];
NSString *showText = dic[@"showText"];
_nativeLabel = [UILabel new];
_nativeLabel.text = showText;
_nativeLabel.textColor = [otherColor isEqualToString:@"green"] ? [UIColor greenColor]: [UIColor blackColor];
_nativeLabel.font = [UIFont boldSystemFontOfSize:24];
_viewId = viewId;
NSString* channelName = [NSString stringWithFormat:@"wanmei.custom.plugins/custom_%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 _nativeLabel;
}
// 处理方法调用
- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{
if ([[call method] isEqualToString:@"sayHello"]){
[_nativeLabel setText:@"你好呀,赛利亚!"];
} else {
result(FlutterMethodNotImplemented);
}
}
@end
#pragma mark - 消息通道
@implementation FlutterCustomViewFactory {
// 持有消息
NSObject<FlutterBinaryMessenger>*_messenger;
}
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger> *)messager{
self = [super init];
if (self) {
_messenger = messager;
}
return self;
}
- (NSObject<FlutterMessageCodec> *)createArgsCodec{
return [FlutterStandardMessageCodec sharedInstance];
}
- (NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args{
FlutterCustomViewController *imp = [[FlutterCustomViewController alloc] initWithWithFrame:frame viewIdentifier:viewId arguments:args binaryMessenger:_messenger];
return imp;
}
@end
在项目中使用
注意!!!
很关键的一个点,iOS这边如果需要为Flutter提供Native视图,需要修改Info.plist
在Flutter你打包的ios/Ruuner项目中的Info.plist添加如下代码
<key>io.flutter.embedded_views_preview</key>
<true/>
在example中使用:
我们观察到example的pubspec.yaml已经引用了我们自定义组件
这是创建plugin的时候自动添加的
我们只需要在Flutter项目中的pubspec.yaml同理引用即可
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutterpluginlabel/flutterpluginlabel.dart';
void main() => runApp(MaterialApp(
home: ActivityIndicatorExample(),
));
class ActivityIndicatorExample extends StatelessWidget{
// 引用与初始化
JWCustomViewController controller;
void _onActivityIndicatorControllerCreated(JWCustomViewController _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>[
// 是一个背景透明的视图
JWCustomView(
showText: '我这边创建一个原生视图!!!',
onCreateCallback: _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.sayHello();
},
child: new Text("Start"),
),
],
),
)
],
),
);
}
}