Flutter Plugin Desktop 示例

226 阅读2分钟
  1. 创建一个插件
flutter create --template=plugin --platforms=android,ios,windows flutter_plugin
  1. 目录结构
flutter_plugin/
├── android/            # Android 平台代码
├── ios/                # iOS 平台代码
├── lib/                # 插件的 Dart 代码
├── macos/              # macOS 平台代码
├── test/               # 插件的测试代码
├── windows/            # Windows 平台代码
├── pubspec.yaml        # 插件的配置信息
└── example/            # 示例应用,用于展示插件如何工作

3.编写插件的dart代码

lib/flutter_plugin.dart 文件中,你可以定义插件的 Dart API,通常是通过 MethodChannel 来调用平台特定的代码。

3.1编辑 lib/flutter_plugin.dart


import 'flutter_plugin_platform_interface.dart';

class FlutterPlugin {
  Future<String?> getPlatformVersion() {
    return FlutterPluginPlatform.instance.getPlatformVersion();
  }

  Future<int> add(int a,int b) {
    return FlutterPluginPlatform.instance.add(a,b);
  }
}

3.2lib/flutter_plugin_platform_interface.dart

import 'package:plugin_platform_interface/plugin_platform_interface.dart';

import 'flutter_plugin_method_channel.dart';

abstract class FlutterPluginPlatform extends PlatformInterface {
  /// Constructs a FlutterPluginPlatform.
  FlutterPluginPlatform() : super(token: _token);

  static final Object _token = Object();

  static FlutterPluginPlatform _instance = MethodChannelFlutterPlugin();

  /// The default instance of [FlutterPluginPlatform] to use.
  ///
  /// Defaults to [MethodChannelFlutterPlugin].
  static FlutterPluginPlatform get instance => _instance;

  /// Platform-specific implementations should set this with their own
  /// platform-specific class that extends [FlutterPluginPlatform] when
  /// they register themselves.
  static set instance(FlutterPluginPlatform instance) {
    PlatformInterface.verifyToken(instance, _token);
    _instance = instance;
  }

  Future<String?> getPlatformVersion() {
    throw UnimplementedError('platformVersion() has not been implemented.');
  }

  Future<int> add(int a,int b) {
    throw UnimplementedError('add() has not been implemented.');
  }
}

3.3编辑 lib/flutter_plugin_methoed_channel.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

import 'flutter_plugin_platform_interface.dart';

/// An implementation of [FlutterPluginPlatform] that uses method channels.
class MethodChannelFlutterPlugin extends FlutterPluginPlatform {
  /// The method channel used to interact with the native platform.
  @visibleForTesting
  final methodChannel = const MethodChannel('flutter_plugin');

  @override
  Future<String?> getPlatformVersion() async {
    final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
    return version;
  }
  @override
  Future<int> add(int a,int b) async {
    final sum = await methodChannel.invokeMethod<int>('add',{'a':a,'b':b});
    return sum??0;
  }
}

4.实现平台代码

#include "flutter_plugin.h"

// This must be included before many other Windows headers.
#include <windows.h>

// For getPlatformVersion; remove unless needed for your plugin implementation.
#include <VersionHelpers.h>

#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>

#include <memory>
#include <sstream>

namespace flutter_plugin {

// static
void FlutterPlugin::RegisterWithRegistrar(
    flutter::PluginRegistrarWindows *registrar) {
  auto channel =
      std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
          registrar->messenger(), "flutter_plugin",
          &flutter::StandardMethodCodec::GetInstance());

  auto plugin = std::make_unique<FlutterPlugin>();

  channel->SetMethodCallHandler(
      [plugin_pointer = plugin.get()](const auto &call, auto result) {
        plugin_pointer->HandleMethodCall(call, std::move(result));
      });

  registrar->AddPlugin(std::move(plugin));
}

FlutterPlugin::FlutterPlugin() {}

FlutterPlugin::~FlutterPlugin() {}

void FlutterPlugin::HandleMethodCall(
    const flutter::MethodCall<flutter::EncodableValue> &method_call,
    std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
  if (method_call.method_name().compare("getPlatformVersion") == 0) {
    std::ostringstream version_stream;
    version_stream << "Windows ";
    if (IsWindows10OrGreater()) {
      version_stream << "10+";
    } else if (IsWindows8OrGreater()) {
      version_stream << "8";
    } else if (IsWindows7OrGreater()) {
      version_stream << "7";
    }
    result->Success(flutter::EncodableValue(version_stream.str()));
  } else if((method_call.method_name().compare("add") == 0)){

      const auto* arguments =
              std::get_if<flutter::EncodableMap>(method_call.arguments());

      auto a = arguments->find(flutter::EncodableValue("a"));
      auto b = arguments->find(flutter::EncodableValue("b"));
      //first is key;second is value
      int valueA = std::get<int>(a->second);
      int valueB = std::get<int>(b->second);
      int sum = valueA + valueB;
      result->Success(flutter::EncodableValue(sum));
  }else {
    result->NotImplemented();
  }
}

}  // namespace flutter_plugin

5.测试

import 'dart:developer';

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

import 'package:flutter/services.dart';
import 'package:flutter_plugin/flutter_plugin.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';
  final _firstPluginFlutterPlugin = FlutterPlugin();
  TextEditingController a = TextEditingController(text: '0');
  TextEditingController b = TextEditingController(text: '1');
  int sum = 1;

  getInt(String sInt) {
    if (sInt.isNotEmpty) {
      return int.parse(sInt);
    }
  }

  getSumInt() async {
    if (a.text.isNotEmpty) {
      int c = (int.parse(a.text) + 1);
      sum = await _firstPluginFlutterPlugin.add(int.parse(b.text), c);
      a.text = c.toString();
    }
  }

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

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    String platformVersion;
    // Platform messages may fail, so we use a try/catch PlatformException.
    // We also handle the message potentially returning null.
    try {
      platformVersion = await _firstPluginFlutterPlugin.getPlatformVersion() ?? 'Unknown platform version';
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Column(
            children: [
              Text('Running on: $_platformVersion\n'),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  SizedBox(
                    width: 200,
                    child: TextField(
                      controller: a,
                      decoration: const InputDecoration(
                        border: OutlineInputBorder(),
                      ),
                      onChanged: (v) async {
                        if (v.isNotEmpty) {
                          sum = await _firstPluginFlutterPlugin.add(getInt(a.text), getInt(b.text));
                          setState(() {});
                        }
                      },
                    ),
                  ),
                  const SizedBox(
                    width: 20,
                  ),
                  SizedBox(
                    width: 200,
                    child: TextField(
                      controller: b,
                      decoration: const InputDecoration(
                        border: OutlineInputBorder(),
                      ),
                      onChanged: (v) async {
                        if (v.isNotEmpty) {
                          sum = await _firstPluginFlutterPlugin.add(getInt(a.text), getInt(b.text));
                          setState(() {});
                        }
                        ;
                      },
                    ),
                  ),
                  Text(' = $sum'),
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              ElevatedButton(
                  onPressed: () async {
                    await getSumInt();
                    setState(() {});
                  },
                  child: const Text('sum+1'))
            ],
          ),
        ),
      ),
    );
  }
}