Flutter窗口管理插件glfw3的使用

Flutter窗口管理插件glfw3的使用

概述

glfw3 for Dart 是一个用于在Dart中进行窗口管理和OpenGL上下文创建的插件。它依赖于 ffi 包,适用于Windows 64位和Linux系统。

依赖

  • ffi ^2.1.2

注意事项

该插件目前支持Windows 64位和Linux系统。

示例代码

以下是一个完整的示例demo,展示了如何使用 glfw3 插件来创建和管理窗口,并处理各种事件回调。

import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:glfw3/glfw3.dart';

// 定义各种回调函数
void windowPosCallback(Pointer<GLFWwindow> window, int xpos, int ypos) {
  print('windowPosCallback: xpos=$xpos ypos=$ypos');
}

void windowSizeCallback(Pointer<GLFWwindow> window, int width, int height) {
  print('windowSizeCallback: width=$width height=$height');
}

void windowCloseCallback(Pointer<GLFWwindow> window) {
  print('windowCloseCallback!');
}

void windowRefreshCallback(Pointer<GLFWwindow> window) {
  print('windowRefreshCallback!');
}

void windowFocusCallback(Pointer<GLFWwindow> window, int focused) {
  print('windowFocusCallback: focused=$focused');
}

void windowIconifyCallback(Pointer<GLFWwindow> window, int iconified) {
  print('windowIconifyCallback: iconified=$iconified');
}

void windowMaximizeCallback(Pointer<GLFWwindow> window, int maximized) {
  print('windowMaximizeCallback: maximized=$maximized');
}

void framebufferSizeCallback(Pointer<GLFWwindow> window, int width, int height) {
  print('framebufferSizeCallback: width=$width height=$height');
}

void windowContentScaleCallback(Pointer<GLFWwindow> window, double xscale, double yscale) {
  print('windowContentScaleCallback: xscale=$xscale yscale=$yscale');
}

void mouseButtonCallback(Pointer<GLFWwindow> window, int button, int action, int mods) {
  print('mouseCallback: button=$button action=$action mods=$mods');
}

void cursorPosCallback(Pointer<GLFWwindow> window, double xpos, double ypos) {
  print('cursorPosCallback: xpos=$xpos ypos=$ypos');
}

void cursorEnterCallback(Pointer<GLFWwindow> window, int entered) {
  print('cursorEnterCallback: entered=$entered');
}

void scrollCallback(Pointer<GLFWwindow> window, double xoffset, double yoffset) {
  print('scrollCallback: xoffset=$xoffset yoffset=$yoffset');
}

void keyCallback(Pointer<GLFWwindow> window, int key, int scancode, int action, int mods) {
  print('keyCallback: key=$key scancode=$scancode action=$action mods=$mods');
}

void charCallback(Pointer<GLFWwindow> window, int codepoint) {
  print('charCallback: codepoint=${String.fromCharCode(codepoint)}');
}

void charModsCallback(Pointer<GLFWwindow> window, int codepoint, int mods) {
  print('charModsCallback: codepoint=${String.fromCharCode(codepoint)} mods=$mods');
}

void dropCallback(Pointer<GLFWwindow> window, int pathCount, Pointer<Pointer<Utf8>> paths) {
  for (var i = 0; i < pathCount; i++) {
    var path = (paths + i).value;
    print('dropCallback: path[$i]=${path.toDartString()}');
  }
}

// 主函数
int main() {
  if (glfwInit() == GLFW_FALSE) {
    return -1;
  }

  // 创建窗口
  var window = glfwCreateWindow(640, 480, "Hello GLFW", nullptr, nullptr);
  if (window == nullptr) {
    glfwTerminate();
    return -1;
  }

  // 设置回调函数
  glfwSetWindowPosCallback(window, Pointer.fromFunction(windowPosCallback));
  glfwSetWindowSizeCallback(window, Pointer.fromFunction(windowSizeCallback));
  glfwSetWindowCloseCallback(window, Pointer.fromFunction(windowCloseCallback));
  glfwSetWindowRefreshCallback(window, Pointer.fromFunction(windowRefreshCallback));
  glfwSetWindowFocusCallback(window, Pointer.fromFunction(windowFocusCallback));
  glfwSetWindowIconifyCallback(window, Pointer.fromFunction(windowIconifyCallback));
  glfwSetWindowMaximizeCallback(window, Pointer.fromFunction(windowMaximizeCallback));
  glfwSetFramebufferSizeCallback(window, Pointer.fromFunction(framebufferSizeCallback));
  glfwSetWindowContentScaleCallback(window, Pointer.fromFunction(windowContentScaleCallback));
  glfwSetMouseButtonCallback(window, Pointer.fromFunction(mouseButtonCallback));
  glfwSetCursorPosCallback(window, Pointer.fromFunction(cursorPosCallback));
  glfwSetScrollCallback(window, Pointer.fromFunction(scrollCallback));
  glfwSetKeyCallback(window, Pointer.fromFunction(keyCallback));
  glfwSetCharCallback(window, Pointer.fromFunction(charCallback));
  glfwSetCharModsCallback(window, Pointer.fromFunction(charModsCallback));
  glfwSetDropCallback(window, Pointer.fromFunction(dropCallback));

  // 主循环
  while (glfwWindowShouldClose(window) == GLFW_FALSE) {
    glfwSwapBuffers(window);
    glfwWaitEvents();
  }

  // 清理资源
  glfwTerminate();
  return 0;
}

更多关于Flutter窗口管理插件glfw3的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter窗口管理插件glfw3的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中直接使用GLFW3进行窗口管理并不常见,因为GLFW3是一个专门用于C/C++的库,而Flutter是一个用于构建跨平台UI的框架,主要使用Dart语言。然而,你可以通过一些方法将GLFW3与Flutter结合使用,比如通过平台通道(Platform Channels)调用原生代码。

以下是一个基本的例子,展示如何在Flutter中通过平台通道调用原生代码(在这个例子中,原生代码使用GLFW3来创建一个窗口)。请注意,这个例子假设你已经在Android和iOS平台上设置了原生代码支持。

1. 设置Flutter项目

首先,创建一个新的Flutter项目:

flutter create glfw_flutter_example
cd glfw_flutter_example

2. 添加平台通道支持

lib目录下,打开main.dart文件,并添加平台通道代码:

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

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

class MyApp extends StatelessWidget {
  static const platform = MethodChannel('com.example.glfw_flutter_example/glfw');

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('GLFW3 Flutter Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: _createWindow,
            child: Text('Create GLFW Window'),
          ),
        ),
      ),
    );
  }

  Future<void> _createWindow() async {
    try {
      final result = await platform.invokeMethod('createWindow');
      print('GLFW Window created: $result');
    } on PlatformException catch (e) {
      print("Failed to create GLFW window: '${e.message}'.");
    }
  }
}

3. 在Android平台上实现原生代码

android/app/src/main/java/com/example/glfw_flutter_example/目录下,创建一个新的Java类,比如GlfwPlugin.java

package com.example.glfw_flutter_example;

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;

import org.lwjgl.glfw.GLFW;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;

public class GlfwPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
    private MethodChannel channel;
    private Context applicationContext;

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "com.example.glfw_flutter_example/glfw");
        channel.setMethodCallHandler(this);
    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
        if (call.method.equals("createWindow")) {
            createWindow();
            result.success("Window created");
        } else {
            result.notImplemented();
        }
    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        channel.setMethodCallHandler(null);
    }

    private void createWindow() {
        if (!GLFW.glfwInit()) {
            throw new RuntimeException("Failed to initialize GLFW");
        }

        try (MemoryStack stack = MemoryStack.stackPush()) {
            long window = GLFW.glfwCreateWindow(800, 600, "GLFW Window", MemoryUtil.NULL, MemoryUtil.NULL);
            if (window == MemoryUtil.NULL) {
                throw new RuntimeException("Failed to create GLFW window");
            }

            GLFW.glfwMakeContextCurrent(window);
            GLFW.glfwSwapInterval(1); // Enable vsync

            // Here you can add more GLFW window management code
            // ...

            // For simplicity, we just sleep for a few seconds to keep the window open
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            GLFW.glfwDestroyWindow(window);
        }

        GLFW.glfwTerminate();
    }

    @Override
    public void onAttachedToActivity(ActivityPluginBinding binding) {
        applicationContext = binding.getActivity().getApplicationContext();
    }

    @Override
    public void onDetachedFromActivityForConfigChanges() {
        // No-op
    }

    @Override
    public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
        applicationContext = binding.getActivity().getApplicationContext();
    }

    @Override
    public void onDetachedFromActivity() {
        // No-op
    }
}

注意:上面的Java代码使用了LWJGL的GLFW绑定,而不是直接使用GLFW3的C库。这是因为直接在Android上使用C库需要更多的配置工作,而LWJGL提供了一个方便的Java绑定。你可能需要在build.gradle文件中添加LWJGL的依赖。

4. 在iOS平台上实现原生代码(可选)

对于iOS平台,你需要使用Objective-C或Swift来编写原生代码,并通过Flutter的平台通道进行通信。这个过程与Android类似,但实现细节会有所不同。由于篇幅限制,这里不提供完整的iOS实现代码。

5. 注册插件

android/app/src/main/kotlin/com/example/glfw_flutter_example/MainActivity.kt(如果你使用的是Kotlin)或MainActivity.java中添加插件注册代码:

package com.example.glfw_flutter_example

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        FlutterEngineCache
                .getInstance()
                .put("my_engine_id", flutterEngine)
        GlfwPlugin().registerWith(flutterEngine.dartExecutor.binaryMessenger)
    }
}

(对于Java,相应的代码会略有不同)

总结

上面的例子展示了如何在Flutter中通过平台通道调用原生代码来创建GLFW窗口。然而,请注意,直接在移动平台上使用GLFW可能并不是最佳实践,因为GLFW主要是为桌面平台设计的。在移动平台上,你可能更倾向于使用平台特定的窗口和图形API,如Android的SurfaceView和iOS的UIKit/Metal。

此外,由于GLFW和Flutter的运行环境差异较大,直接在Flutter应用中嵌入GLFW窗口可能会遇到一些挑战,比如窗口管理、事件处理等。因此,在实际项目中,你可能需要更深入地了解Flutter和原生平台的交互机制,并进行相应的适配和优化。

回到顶部