Flutter自定义功能插件spi_flutter_package的使用方法

Flutter自定义功能插件spi_flutter_package的使用方法

Getting Started

  1. 首先,在您的Flutter项目中安装该工具。建议将其添加到dev_dependencies中,因为它是一个开发工具,所以无需随项目源代码一起发布:
dev_dependencies:
  spi_flutter_package:
    path: version

接下来,在您的Flutter项目中创建一个自定义目录:lib/channel,用于保存您的Flutter通道Dart代码:

|____main.dart
|____channel //您的Flutter通道
| |____flutter2native //Flutter调用原生代码目录
| | |____account.dart //您的接口代码
| |____native2flutter //原生代码调用Flutter目录
| | |____flutter_fps.dart //您的接口代码

创建account.dart文件:

abstract class IAccount {
  Future<bool> login(String userName, String password);
  void logout();
  Future<String> getName();
  Future<int> getAge();
}

更多代码请参阅example

  1. 在Flutter项目的test目录下,打开任何dart文件并编写以下代码:
import 'package:spi_flutter_package/spi_flutter_package.dart';
import 'package:spi_flutter_package/file_config.dart';

void main() async {
  await spiFlutterPackageStart([
    FlutterPlatformConfig()
      ..sourceCodePath = "./lib/channel" // Flutter源代码路径
      ..channelName = "com.siyehua.spiexample.channel" // 通道名称
    ,
    AndroidPlatformConfig()
      ..savePath = "./android/app/src/main/kotlin" // Android保存路径
    ,
    IosPlatformConfig()
      ..iosProjectPrefix = "MQQFlutterGen_" // iOS前缀
      ..savePath = "./ios/Classes" // iOS保存路径
    ,
  ], nullSafe: true);
}

点击左上角的▶️图标,运行main()函数,您将在自定义路径中找到生成的代码。更多信息可以查看generated code

当代码自动生成完成后,您可以在平台上添加实现类,例如Android平台:

public class AccountImpl implements IAccount {
    //....

    @Override
    public void logout() {
        Log.e("android", "logout method, nothing should call back");
    }

    @Override
    public void getName(ChannelManager.Result<String> callback) {
        Log.e("android", "getName method");

        callback.success("siyehua");
    }

    @Override
    public void getAge(ChannelManager.Result<Long> callback) {
        Log.e("android", "getAge method");
        callback.success(1L);
    }
}
  1. 现在,您可以使用此工具交换数据。

在Android项目中:

// 1. 初始化(仅一次)

```java
ChannelManager.init(flutterEngine.getDartExecutor(), new ChannelManager.JsonParse() {
    @Nullable
    @Override
    public String toJSONString(@Nullable Object object) {
        // 您的JSON解析
        return JSON.toJSONString(object);
    }

    @Nullable
    @Override
    public <T> T parseObject(@Nullable String text, @NonNull Class<T> clazz) {
        // 您的JSON解析
        return JSON.parseObject(text, clazz);
    }
});

// 2. 将您的实现类添加到ChannelManager ChannelManager.addChannelImpl(IAccount.class, new AccountImpl());


在Flutter项目中:

```dart
// 1. 初始化(仅一次)
ChannelManager.init();

// 2. 调用
IAccount account = ChannelManager.getChannel(IAccount);
var result = await account.login("userName", "password");
print(result);
account.logout();
var name = await account.getName();
print(name);
var age = await account.getAge();
print(age);

现在,运行您的项目并检查结果。更多信息可以查看example

生成的代码

在Flutter项目中,代码将保存在flutterPath/generated目录下:

|____main.dart
|____channel
| |____generated // 自动创建
| | |____channel
| | | |____impl
| | | | |____iaccount_impl.dart
| | | |____parse
| | | | |____object_parse.dart
| | | |____ChannelManager.dart
| |____flutter2native // Flutter调用原生代码
| | |____account.dart
| |____native2flutter // 原生代码调用Flutter
| | |____flutter_fps.dart

在Android项目中,您也可以在androidSavePath + 包名目录下找到自动生成的代码:

.
|____com
| |____siyehua
| | |____spiexample
| | | |____MainActivity2.java
| | | |____AccountImpl.java
| | | |____MainActivity.kt
| | | |____channel // 自动创建
| | | | |____flutter2native
| | | | | |____IAccount.java
| | | | |____native2flutter
| | | | | |____Fps2.java
| | | | | |____FpsImpl.java
| | | | | |____Fps2Impl.java
| | | | | |____Fps.java
| | | | |____ChannelManager.java

注意:自动生成的代码不应被编辑

Proguard

如果您启用了Proguard,请在proguard-rules.pro文件中添加以下代码:

# 示例中,包名为 "com.siyehua.spiexample.channel"
-keep class {您的Android包名}.** {*;}

更多关于Flutter自定义功能插件spi_flutter_package的使用方法的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自定义功能插件spi_flutter_package的使用方法的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter开发中,虽然spi_flutter_package这个插件的具体功能和定义是未知的,但基于插件名称的合理推测,我们可以假设这个插件可能与某种硬件接口(如SPI,Serial Peripheral Interface)的交互有关。在Flutter中实现与原生硬件接口的交互通常需要借助平台通道(Platform Channels)来完成。以下是一个基于平台通道假设的示例代码,展示如何在Flutter应用中与一个假设的SPI接口进行交互。

1. 创建Flutter插件

首先,我们需要创建一个Flutter插件项目,用于封装与原生代码的交互逻辑。由于spi_flutter_package是假设的,这里我们创建一个名为spi_flutter_plugin的插件。

flutter create --template=plugin spi_flutter_plugin

2. 在原生代码中实现SPI接口

iOS(Swift)

ios/Classes/SpiFlutterPlugin.swift中,实现与SPI接口的交互逻辑。由于iOS不直接支持SPI接口,这里仅作为示例:

import Flutter

public class SpiFlutterPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterRegistrar) {
    let channel = FlutterMethodChannel(name: "spi_flutter_plugin", binaryMessenger: registrar.messenger())
    let instance = SpiFlutterPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch call.method {
    case "sendDataToSpi":
      if let data = call.arguments as? [UInt8] {
        // 假设这里有一个发送数据到SPI接口的函数
        // sendDataToSpi(data)
        result(true)
      } else {
        result(FlutterError(code: "INVALID_ARGUMENT", message: "Invalid argument", details: nil))
      }
    default:
      result(FlutterMethodNotImplementedError(methodName: call.method))
    }
  }
}

注意:由于iOS不直接支持SPI,这里的sendDataToSpi函数是一个假设的实现。

Android(Kotlin)

android/src/main/kotlin/com/example/spi_flutter_plugin/SpiFlutterPlugin.kt中,实现与SPI接口的交互逻辑。这里使用Kotlin编写:

package com.example.spi_flutter_plugin

import android.content.Context
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

class SpiFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
  private lateinit var channel: MethodChannel
  private var context: Context? = null

  override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPluginBinding) {
    channel = MethodChannel(flutterPluginBinding.binaryMessenger, "spi_flutter_plugin")
    channel.setMethodCallHandler(this)
    context = flutterPluginBinding.applicationContext
  }

  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    if (call.method == "sendDataToSpi") {
      val data = call.argument<ByteArray>("data")
      data?.let {
        // 假设这里有一个发送数据到SPI接口的函数
        // sendDataToSpi(it)
        result.success(true)
      } ?: result.error("INVALID_ARGUMENT", "Data argument missing", null)
    } else {
      result.notImplemented()
    }
  }

  override fun onDetachedFromEngine(@NonNull binding: FlutterPluginBinding) {
    channel.setMethodCallHandler(null)
  }

  override fun onAttachedToActivity(binding: ActivityPluginBinding) {
    // No-op
  }

  override fun onDetachedFromActivityForConfigChanges() {
    // No-op
  }

  override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
    // No-op
  }

  override fun onDetachedFromActivity() {
    // No-op
  }
}

同样,由于Android设备上的SPI接口需要通过JNI调用底层C/C++代码来实现,这里的sendDataToSpi函数也是一个假设的实现。

3. 在Flutter应用中使用插件

在Flutter应用的lib/main.dart中,导入并使用spi_flutter_plugin

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('SPI Flutter Plugin Demo'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              final spiPlugin = SpiFlutterPlugin();
              try {
                bool success = await spiPlugin.sendDataToSpi(Uint8List.fromList([0x01, 0x02, 0x03]));
                print("SPI send success: $success");
              } catch (e) {
                print("SPI send failed: $e");
              }
            },
            child: Text('Send Data to SPI'),
          ),
        ),
      ),
    );
  }
}

class SpiFlutterPlugin {
  static const MethodChannel _channel = MethodChannel('spi_flutter_plugin');

  Future<bool> sendDataToSpi(Uint8List data) async {
    final bool success = await _channel.invokeMethod('sendDataToSpi', {"data": data});
    return success;
  }
}

总结

以上代码展示了一个假设的spi_flutter_package插件的基本使用方法,包括插件的创建、原生代码的实现以及在Flutter应用中的调用。由于SPI接口的具体实现依赖于底层硬件和操作系统,实际开发中需要根据具体需求和环境进行调整。

回到顶部