使用 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#
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);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库