Desarrollo del complemento Flutter

Prefacio

He estado haciendo desarrollo de iOS durante muchos años. Aunque me he encontrado con muchos problemas y he resuelto muchos problemas, no se han registrado, porque soy vago, y a veces lo olvido debido a mi ajetreado trabajo, y no he escrito o mantenido iOS relacionado. Estoy profundamente avergonzado de este artículo técnico sin pensarlo. Ha pasado mucho tiempo desde que se lanzó Flutter desde la versión 1.0. También hay muchas empresas que han comenzado a intentar ser personas malhumoradas. Sucede que la empresa ahora está usando el desarrollo de Flutter, pero el complemento de Flutter no Es tan rico como iOS nativo o Android nativo. A veces las necesidades de PM son tan difíciles de manejar. Es inevitable que necesite desarrollar complementos usted mismo. Luego, compartiré mi pequeña experiencia en el desarrollo de complementos, con la esperanza de ayudar a algunos amigos.

Una preparación

No entraré en detalles sobre la construcción del entorno de Flutter aquí. Para crear una nueva plantilla de componente, hay dos formas de crear una, una es a través de la línea de comandos y la otra a través de Android Studio

Crear a través de la línea de comando:

flutter create -t plugin flutter_plugin
复制代码

Creación de Android Studio

Seleccione el segundo elemento Iniciar un nuevo proyecto de Flutter

 

Elija el complemento Flutter

Segundo paso de Android.jpg

 

Complete el nombre del complemento

3.jpg

 

El lugar donde se eleva el círculo rojo se selecciona de acuerdo con su propio idioma. El proyecto de Android predeterminado es java. El proyecto de iOS es Object-c. Si necesita Kotlin y Swift, debe marcarlo. Siempre he usado Object-c, por lo que las siguientes explicaciones están escritas en OC

4.jpg

 

Este es el directorio de plantillas de complementos recién creado

5.jpg

 

Si escribe código nativo, busque el directorio de iOS o Android correspondiente en el directorio de ejemplo. Tomando iOS como ejemplo, el código de iOS recién creado no contiene la carpeta del pod, por lo que no se puede ejecutar. Primero debe ejecutar la instalación del pod en la carpeta de iOS en el ejemplo. Para instalar las dependencias necesarias

 

 

6.jpg

 

 

Entonces puedes ejecutar el proyecto de muestra

7.jpg

 

 

En este punto, el trabajo de preparación del desarrollo del complemento de Flutter ha terminado, ingresemos al desarrollo real del complemento.

Moler el puño Huo Huo Xiang Zhu Yang-plug-in desarrollo

Comencemos primero con el código de Dart

Este es el código de dardo que acaba de crear la plantilla.

8.jpg

 

 

Primero simplifiquemos el código, eliminemos el código inútil y luego comentemos el código de error relacionado

///先定义一个controller创建成功回调 后面会用到
typedef void TestViewCreatedCallback(TestFlutterPluginDemo controller);
class TestFlutterPluginDemo {
  MethodChannel _channel;
  TestFlutterPluginDemo.init(int id){
    _channel = new MethodChannel('test_flutter_plugin_demo');
  }
}
复制代码

TestFlutterPluginDemo Esta clase que entiendo es principalmente responsable de interactuar con los métodos nativos, llamar a los métodos nativos y monitorear los métodos nativos, y luego nos permite crear una nueva clase de vista principalmente para cargar la vista nativa

///我是使用的 StatefulWidget 使用StatelessWidget 也是一样
class TestView extends StatefulWidget {
  ///根据自己的需求创建初始化参数
  final TestViewCreatedCallback onCreated; ///是上面创建的回调
  final String titleStr;

  TestView({
    Key key,
   this.onCreated,
   this.titleStr,
});

  @override
  _TestViewState createState() => _TestViewState();
}

class _TestViewState extends State<TestView> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: _loadNativeView(),
    );
  }
///加载原生视图
  Widget _loadNativeView(){
    ///根据不同的平台显示相应的视图
    if(Platform.isAndroid){ ///加载安卓原生视图
      return AndroidView(
        viewType: 'testView',///视图标识符 要和原生 保持一致 要不然加载不到视图
        onPlatformViewCreated:onPlatformViewCreated,///原生视图创建成功的回调
        creationParams: <String, dynamic>{ ///给原生传递初始化参数 就是上面定义的初始化参数
          'titleStr':widget.titleStr,
        },
        /// 用来编码 creationParams 的形式,可选 [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]
        /// 如果存在 creationParams,则该值不能为null
        creationParamsCodec: const StandardMessageCodec(),
      );
    }else if(Platform.isIOS){///加载iOS原生视图
      return UiKitView(
        viewType: 'testView',///视图标识符 要和原生 保持一致 要不然加载不到视图
        onPlatformViewCreated:onPlatformViewCreated,///原生视图创建成功的回调
        creationParams: <String, dynamic>{ ///给原生传递初始化参数 就是上面定义的初始化参数
          'titleStr':widget.titleStr,
        },
        /// 用来编码 creationParams 的形式,可选 [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]
        /// 如果存在 creationParams,则该值不能为null
        creationParamsCodec: const StandardMessageCodec(),
      );
    }else{
      return Text('这个平台老子不支持');
    }
  }
  ///我也是刚学Flutter 所以我理解的:这个基本上是固定写法   哈哈
  Future<void> onPlatformViewCreated(id) async {
    if (widget.onCreated == null) {
      return;
    }
    widget.onCreated(new TestFlutterPluginDemo.init(id));
  }
}
复制代码

A partir de ahora, se ha escrito el código principal de Dart y los comentarios en el código son muy detallados. Los amigos pueden seguir el código paso a paso para mejorar la memoria y la familiaridad con el código. El resto es cargar en main.dart Ver,

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

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
 
  ///定义一个测试类的属性 用来调用原生方法 和原生交互
  var testFlutterPluginDemo;

  @override
  void initState() {
    super.initState();

  }
  
  @override
  Widget build(BuildContext context) {
    ///初始化 测试视图的类
    TestView testView = new TestView(
      onCreated: onTestViewCreated,
    );
    
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Column(
          children: <Widget>[
            Container(
              height: 200,
              width: 400,
              child: testView,///使用原生视图
            )
          ],
        )
      ),
    );
  }

  void onTestViewCreated(testFlutterPluginDemo){
    this.testFlutterPluginDemo = testFlutterPluginDemo;
  }
}
复制代码

Después de agregar el código para cargar la vista en main.dart, el código relacionado con Dart de desarrollo del complemento está terminado, comencemos a escribir el código nativo y enfaticemos que el código iOS es un ejemplo del lenguaje OC.

Iniciar código nativo

Primero veamos el directorio de archivos de código del proyecto de prueba

9.jpg

 

 

Se encontró que hay dos archivos GeneratedPluginRegistrant.hy GeneratedPluginRegistrant.m, pero estos dos archivos no son las páginas donde necesitamos escribir código. Las páginas donde escribimos código están ocultas más profundamente en el siguiente directorio

10.jpg

 

Las clases deben encontrar el archivo en la carpeta pod / Development Pods dentro del desarrollo de estos dos documentos, pero también deben crear una nueva clase en este directorio, los componentes hechos que se utilizan para desarrollar iOS los estudiantes deben estar muy familiarizados, el principio es similar para comenzar el desarrollo de código nativo vamos a crear primero una nueva fábrica para conectarse a la vista de aleteo. la nueva fábrica debe heredar de NSObject, y luego, cuando la creación de una nueva clase de vista, sino que también debe heredar de NSObject para escribir el código para las dos clases de diseño de página nativa de la siguiente

 

factory.h

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

@interface TestFlutterPluginViewFactory : NSObject<FlutterPlatformViewFactory>

/// 重写一个构造方法 来接收 Flutter 相关蚕食
/// @param messenger Flutter类 包含回调方法等信息
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;

@end
复制代码

fábrica.m

#import "TestFlutterPluginViewFactory.h"
#import "TestFlutterPluginView.h"
@interface TestFlutterPluginViewFactory ()

@property(nonatomic)NSObject<FlutterBinaryMessenger>* messenger;

@end

@implementation TestFlutterPluginViewFactory

- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
    self = [super init];
    if (self) {
        self.messenger = messenger;
    }
    return self;
}

#pragma mark -- 实现FlutterPlatformViewFactory 的代理方法
- (NSObject<FlutterMessageCodec>*)createArgsCodec {
    return [FlutterStandardMessageCodec sharedInstance];
}

/// FlutterPlatformViewFactory 代理方法 返回过去一个类来布局 原生视图
/// @param frame frame
/// @param viewId view的id
/// @param args 初始化的参数
- (NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args{
    
    TestFlutterPluginView *testFlutterPluginView = [[TestFlutterPluginView alloc] initWithFrame:frame viewId:viewId args:args messager:self.messenger];
    return testFlutterPluginView;
    
}

@end
复制代码

testView.h

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

@interface TestFlutterPluginView : NSObject<FlutterPlatformView>
- (id)initWithFrame:(CGRect)frame
             viewId:(int64_t)viewId
               args:(id)args
           messager:(NSObject<FlutterBinaryMessenger>*)messenger;
@end
复制代码

testView.m

#import "TestFlutterPluginView.h"

@interface TestFlutterPluginView ()
/** channel*/
@property (nonatomic, strong)  FlutterMethodChannel  *channel;
@end

@implementation TestFlutterPluginView
{
    CGRect _frame;
    int64_t _viewId;
    id _args;
   
}

- (id)initWithFrame:(CGRect)frame
  viewId:(int64_t)viewId
    args:(id)args
messager:(NSObject<FlutterBinaryMessenger>*)messenger
{
    if (self = [super init])
    {
        _frame = frame;
        _viewId = viewId;
        _args = args;
        
        ///建立通信通道 用来 监听Flutter 的调用和 调用Fluttter 方法 这里的名称要和Flutter 端保持一致
        _channel = [FlutterMethodChannel methodChannelWithName:@"test_flutter_plugin_demo" binaryMessenger:messenger];
        
        __weak __typeof__(self) weakSelf = self;

        [_channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
            [weakSelf onMethodCall:call result:result];
        }];
       
    }
    return self;
}

- (UIView *)view{
    
    UIView *nativeView = [[UIView alloc] initWithFrame:_frame];
    nativeView.backgroundColor = [UIColor redColor];
    
    return nativeView;
     
}

#pragma mark -- Flutter 交互监听
-(void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{
    
}
复制代码

Se crean la clase de fábrica y las clases de vista, ahora es posible crear una plantilla al comienzo de esta clase. TestFlutterPluginDemoPlugin la asociación es / Development Pods encuentra estos dos archivos dentro de la carpeta Classes en el código inútil eliminado que viene con El código de ejemplo de la conexión es el siguiente: TestFlutterPluginDemoPlugin.h

#import "TestFlutterPluginDemoPlugin.h"
#import "TestFlutterPluginViewFactory.h"

@implementation TestFlutterPluginDemoPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  
    TestFlutterPluginViewFactory *testViewFactory = [[TestFlutterPluginViewFactory alloc] initWithMessenger:registrar.messenger];
//这里填写的id 一定要和dart里面的viewType 这个参数传的id一致
    [registrar registerViewFactory:testViewFactory withId:@"testView"];
    
}

@end
复制代码

Ahora el código para mostrar la vista está terminado. Si desea cargar correctamente, agréguelo a info.plist

	<true/>
复制代码

Ahora ejecutémoslo para ver el efecto.

11.jpg

 

 

Se completó la carga de la vista nativa. Comencemos a aprender cómo llamar y pasar valores entre los métodos de Flutter.

Flutter e interacción nativa

Primero escribamos el código de Dart y agreguemos el código de interacción nativo a la clase TestFlutterPluginDemo que creamos arriba

typedef void TestViewCreatedCallback(TestFlutterPluginDemo controller);
class TestFlutterPluginDemo {

  MethodChannel _channel;
  TestFlutterPluginDemo.init(int id){
    _channel = new MethodChannel('test_flutter_plugin_demo');
    _channel.setMethodCallHandler(platformCallHandler);///设置原生参数监听
  }

  ///Flutter 调用原生
  ///这里我传了一个 字符串 当然也可以传Map
  Future<void> changeNativeTitle(String str) async{
    return _channel.invokeListMethod('changeNativeTitle',str);
  }

  ///实现监听原生方法回调
  Future<dynamic> platformCallHandler(MethodCall call) async {
    switch (call.method) {
      case "clickAciton":
        print('收到原生回调 ---- $call.arguments');
        return ;
        break;
    }
  }
}
复制代码

Luego agreguemos un botón en main.dart para activar la llamada al nativo

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  ///定义一个测试类的属性 用来调用原生方法 和原生交互
  var testFlutterPluginDemo;

  @override
  void initState() {
    super.initState();

  }

  @override
  Widget build(BuildContext context) {
    ///初始化 测试视图的类
    TestView testView = new TestView(
      onCreated: onTestViewCreated,
    );

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Column(
          children: <Widget>[
            Container(
              height: 200,
              width: 400,
              child: testView,///使用原生视图
            ),
            FloatingActionButton( ///添加一个按钮 用来触发原生调用
              onPressed: onNativeMethon, ///点击方法里面调用原生
            )
          ],
        )
      ),
    );
  }

  void onTestViewCreated(testFlutterPluginDemo){
    this.testFlutterPluginDemo = testFlutterPluginDemo;
  }

  void onNativeMethon(){

    this.testFlutterPluginDemo.changeNativeTitle('Flutter 调用原生成功了');
  }

}
复制代码

El código de Dart está terminado, escribamos el código nativo. Agregue un botón al testView que acabamos de crear para mostrar la llamada de Flutter y llamar a Flutter, y luego escuche la llamada de Flutter en el método de resultado onMethodCall: (FlutterMethodCall *) call result: (FlutterResult).

- (UIView *)view{
    
    UIView *nativeView = [[UIView alloc] initWithFrame:_frame];
    nativeView.backgroundColor = [UIColor redColor];
    
    _button = [UIButton buttonWithType:UIButtonTypeSystem];
    [_button setTitle:@"我是按钮" forState:UIControlStateNormal];
    [_button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [_button setBackgroundColor:[UIColor lightGrayColor]];
    _button.frame = CGRectMake(100, 100, 100, 100);
    [nativeView addSubview:_button];
    [_button addTarget:self action:@selector(flutterMethod) forControlEvents:UIControlEventTouchUpInside];
    return nativeView;
     
}

#pragma mark -- Flutter 交互监听
-(void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{
    //监听Fluter
    if ([[call method] isEqualToString:@"changeNativeTitle"]) {
        [_button setTitle:call.arguments forState:UIControlStateNormal];
    }
    
}
//调用Flutter 
- (void)flutterMethod{
    [self.channel invokeMethod:@"clickAciton" arguments:@"我是参数"];
}
复制代码

Ahora corramos y veamos el efecto

246050b9-fc17-48c4-b482-35a07d869e8c.gif

 

12.jpg

 

En este punto, el desarrollo del complemento está completo, solo agregue el código de acuerdo con sus necesidades.
 

Supongo que te gusta

Origin blog.csdn.net/u013491829/article/details/109351940
Recomendado
Clasificación