使用 C# 承载 Flutter Desktop 进行插件开发

FlutterSharp

Google 官方迟迟没有支持使用 C# 进行 Flutter Desktop 的插件开发,本 Demo 初步跑通,原创不易,转载请注明出处,如果对您有所帮助,请赐一个 ✨ 哈哈,详见 https://github.com/LiveOrNot/FlutterSharp

Environment#

Visual Studio 2022 Preview

Visual Studio Code

.NET Framework 4.0 +(.NET Core / .NET 5.0 / .NET 6.0 Desktop 也是可以的)

Flutter Sdk 3.0.5

Tutorials#

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using FlutterSharp.Annotations;
using FlutterSharp.Services;
using FlutterSharp.Services.Implements;

namespace FlutterSharp.Wpf
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private IFlutterSharp _flutterSharp;

        public MainWindow()
        {
            InitializeComponent();

            _flutterSharp = new FlutterSharpBuilder()
                .UseConfiguration(new FlutterSharpConfiguration()
                {
                    PluginAssemblies = new List<Assembly>() { Assembly.GetEntryAssembly() },
                    ProjectPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\flutter_demo\build\windows\runner\Debug")
                }).Build();

            _flutterSharp.Initialize();

            Host.Children.Add(_flutterSharp.Host);
        }

        [Export(typeof(FlutterPlugin))]
        [PartCreationPolicy(CreationPolicy.Shared)]
        public class TestPlugin : FlutterPlugin
        {
            public override string Name => nameof(TestPlugin);

            protected override string ChannelName => "fluttersharp.plugins/test_plugin";

            [FlutterMethod("callCsharp")]
            public string CallCsharp(string message)
            {
                return $"Flutter 调用 C# 成功: {message}";
            }

            public void CallFlutter(string message)
            {
                CallFlutterMethod(new object[] { message });
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var testPlugin = _flutterSharp.Container.GetExportedValues<FlutterPlugin>().FirstOrDefault(p => p.Name == "TestPlugin");
            (testPlugin as TestPlugin)?.CallFlutter(TbMessage?.Text ?? string.Empty);
        }
    }
}
import 'package:flutter/services.dart';

const MethodChannel _channel = MethodChannel('fluttersharp.plugins/test_plugin');


class TestPlugin {
  Function? _onCallback;

  TestPlugin() {
    _channel.setMethodCallHandler(callbackHandler);
  }

  Future<dynamic> callbackHandler(dynamic arg) {    
    _onCallback!(arg.arguments["message"]);
    return Future.value(null);
  }

  void setCallbackHandler({Function? onCallback}) {
    _onCallback = onCallback;
  }

  Future<String?> callCsharp(String message) async {
    final Map<String, Object> args = <String, Object>{
      'message': message
    };
    return await _channel.invokeMethod<String>("callCsharp", args);
  }

}

Showcase#

使用 Flutter 调用 C# 方法
使用 C# 回调 Flutter 方法

Theory#

使用 P/Invoke 直接调用 Flutter Desktop 产物 flutter_windows.dll,按 Flutter Cmake 工程源码对 FLUTTER_EXPORT 的方法进行依次调用,并将最终产生的 View 嵌入一个自定义控件中

using System;
using System.Runtime.InteropServices;

namespace FlutterSharp.Integrations
{
    public abstract class FlutterSafeHandle : SafeHandle
    {
        public FlutterSafeHandle(bool ownsHandle = true) : base(IntPtr.Zero, ownsHandle)
        {
        }

        public override bool IsInvalid => handle == IntPtr.Zero;

        protected override bool ReleaseHandle()
        {
            SetHandle(IntPtr.Zero);
            return true;
        }
    }

    public class FlutterDesktopViewControllerState : FlutterSafeHandle
    { }

    public class FlutterDesktopViewControllerRef : FlutterDesktopViewControllerState
    { }

    public class FlutterDesktopView : FlutterSafeHandle
    { }

    public class FlutterDesktopViewRef : FlutterDesktopView
    { }

    public class FlutterDesktopEngine : FlutterSafeHandle
    { }

    public class FlutterDesktopEngineRef : FlutterDesktopEngine
    { }

    public class FlutterDesktopPluginRegistrar : FlutterSafeHandle
    { }

    public class FlutterDesktopPluginRegistrarRef : FlutterSafeHandle
    { }

    public class FlutterDesktopMessageResponseHandle : FlutterSafeHandle
    {
        public static implicit operator FlutterDesktopMessageResponseHandle(IntPtr handle)
        {
            return new FlutterDesktopMessageResponseHandle { handle = handle };
        }
    }

    public class FlutterDesktopMessenger : FlutterSafeHandle
    { }

    public class FlutterDesktopMessengerRef : FlutterSafeHandle
    { }

    public class FlutterDesktopTextureRegistrar : FlutterSafeHandle
    { }

    public class FlutterDesktopTextureRegistrarRef : FlutterSafeHandle
    { }

    public delegate void FlutterDesktopBinaryReply(byte[] data, IntPtr dataSize, IntPtr userData);

    public delegate void FlutterDesktopOnPluginRegistrarDestroyed(FlutterDesktopPluginRegistrarRef registrar);

    public delegate void FlutterDesktopMessageCallback(IntPtr messenger, FlutterDesktopMessage message, IntPtr userData);

    public delegate bool FlutterDesktopWindowProcCallback(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam, IntPtr userData, IntPtr result);

    [StructLayout(LayoutKind.Sequential)]
    public struct FlutterDesktopEngineProperties
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        public string AssetsPath;

        [MarshalAs(UnmanagedType.LPWStr)]
        public string IcuDataPath;

        [MarshalAs(UnmanagedType.LPWStr)]
        public string AotLibraryPath;

        public IntPtr SwitchesCount;

        public IntPtr Switches;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct FlutterDesktopMessage
    {
        public IntPtr StructSize;

        [MarshalAs(UnmanagedType.LPStr)]
        public string Channel;

        public IntPtr Message;

        public IntPtr MessageSize;

        public IntPtr ResponseHandle;
    }

    public static class FlutterInterop
    {
        private const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;

        public static string ProjectPath { get; private set; }

        [DllImport("kernel32")]
        private static extern IntPtr AddDllDirectory([MarshalAs(UnmanagedType.LPWStr)] string directory);

        public static IntPtr CreateSwitches(string[] switches)
        {
            var result = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)) * switches.Length);
            for (int i = 0; i < switches.Length; i++)
            {
                var s = Marshal.StringToHGlobalAnsi(switches[i]);
                Marshal.WriteIntPtr(result, i * Marshal.SizeOf(typeof(IntPtr)), s);
            }
            return result;
        }

        [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool SetDefaultDllDirectories(uint DirectoryFlags);

        public static void SetProjectPath(string projectPath)
        {
            SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
            AddDllDirectory(projectPath);
            ProjectPath = projectPath;
        }

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopViewControllerRef FlutterDesktopViewControllerCreate(
            int width,
            int height,
            FlutterDesktopEngineRef engine);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopViewControllerDestroy(
            FlutterDesktopViewControllerRef controller);

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(
            FlutterDesktopViewControllerRef controller);

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopViewRef FlutterDesktopViewControllerGetView(
            FlutterDesktopViewControllerRef controller);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopViewControllerForceRedraw(
            FlutterDesktopViewControllerRef controller);

        [DllImport("flutter_windows")]
        public static extern bool FlutterDesktopViewControllerHandleTopLevelWindowProc(
            FlutterDesktopViewControllerRef controller,
            IntPtr hwnd,
            uint message,
            IntPtr wParam,
            IntPtr lParam,
            IntPtr result);

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopEngineRef FlutterDesktopEngineCreate(
            FlutterDesktopEngineProperties engineProperties);

        [DllImport("flutter_windows")]
        public static extern bool FlutterDesktopEngineDestroy(
            FlutterDesktopEngineRef engine);

        [DllImport("flutter_windows")]
        public static extern bool FlutterDesktopEngineRun(
            FlutterDesktopEngineRef engine,
            [MarshalAs(UnmanagedType.LPStr)] string entryPoint);

        [DllImport("flutter_windows")]
        public static extern ulong FlutterDesktopEngineProcessMessages(
            FlutterDesktopEngineRef engine);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopEngineReloadSystemFonts(
            FlutterDesktopEngineRef engine);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopEngineReloadPlatformBrightness(
            FlutterDesktopEngineRef engine);

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopPluginRegistrarRef FlutterDesktopEngineGetPluginRegistrar(
            FlutterDesktopEngineRef engine,
            [MarshalAs(UnmanagedType.LPStr)] string pluginName);

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopMessengerRef FlutterDesktopEngineGetMessenger(
            FlutterDesktopEngineRef engine);

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopTextureRegistrarRef FlutterDesktopEngineGetTextureRegistrar(
            FlutterDesktopTextureRegistrarRef textureRegistrar);

        [DllImport("flutter_windows")]
        public static extern IntPtr FlutterDesktopViewGetHWND(
            FlutterDesktopViewRef view);

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView(
            FlutterDesktopPluginRegistrarRef registrar);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopPluginRegistrarRegisterTopLevelWindowProcDelegate(
            FlutterDesktopPluginRegistrarRef registrar,
            FlutterDesktopWindowProcCallback callback,
            IntPtr userData);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate(
            FlutterDesktopPluginRegistrarRef registrar,
            FlutterDesktopWindowProcCallback callback);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopResyncOutputStreams();

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopMessengerRef FlutterDesktopPluginRegistrarGetMessenger(
            FlutterDesktopPluginRegistrarRef registrar);

        [DllImport("flutter_windows")]
        public static extern FlutterDesktopTextureRegistrarRef FlutterDesktopRegistrarGetTextureRegistrar(
            FlutterDesktopPluginRegistrarRef registrar);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopPluginRegistrarSetDestructionHandler(
            FlutterDesktopPluginRegistrarRef registrar,
            FlutterDesktopOnPluginRegistrarDestroyed callback);

        [DllImport("flutter_windows")]
        public static extern uint FlutterDesktopGetDpiForHWND(
            IntPtr hwnd);

        [DllImport("flutter_windows")]
        public static extern uint FlutterDesktopGetDpiForMonitor(
            IntPtr monitor);

        [DllImport("flutter_windows")]
        public static extern bool FlutterDesktopMessengerSend(
            FlutterDesktopMessengerRef messenger,
            [MarshalAs(UnmanagedType.LPStr)] string channel,
            byte[] message,
            IntPtr messageSize);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopMessengerSendResponse(
            FlutterDesktopMessengerRef messenger,
            FlutterDesktopMessageResponseHandle handle,
            byte[] data,
            IntPtr dataLength);

        [DllImport("flutter_windows")]
        public static extern bool FlutterDesktopMessengerSendWithReply(
            FlutterDesktopMessengerRef messenger,
            [MarshalAs(UnmanagedType.LPStr)] string channel,
            byte[] message,
            IntPtr messageSize,
            FlutterDesktopBinaryReply reply,
            IntPtr userData);

        [DllImport("flutter_windows")]
        public static extern void FlutterDesktopMessengerSetCallback(
            FlutterDesktopMessengerRef messenger,
            [MarshalAs(UnmanagedType.LPStr)] string channel,
            FlutterDesktopMessageCallback callback,
            IntPtr userData);

        [DllImport("flutter_windows")]
        public static extern bool FlutterDesktopTextureRegistrarUnregisterExternalTexture(
            FlutterDesktopTextureRegistrarRef textureRegistrar,
            long textureId);

        [DllImport("flutter_windows")]
        public static extern bool FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable(
            FlutterDesktopTextureRegistrarRef textureRegistrar,
            long textureId);
    }
}
posted @   LiveOrNot  阅读(2633)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示
主题色彩