Flutter GTK窗口集成插件gtk_window的使用
Flutter GTK窗口集成插件gtk_window的使用
一个为flutter添加了一些缺失的gtk4窗口功能的包。适用于Linux,但也可以在MacOS和Windows上工作。
前 | 后 |
---|---|
![]() |
![]() |
![]() |
![]() |
特性
- 窗口命令按钮(最大化、最小化、关闭)
- 当可以弹出视图时,显示返回按钮
- 自定义前导和尾随小部件
- 底部的PreferredSize小部件
- 支持亮模式和暗模式
- 标题栏根据窗口焦点状态进行反应
- 在窗口调整大小时调用允许你添加自定义逻辑
- 圆角(感谢 handy_window)
开始使用
Linux
为了获得圆角,在 my_application_activate
类中将以下两行移到最后:
gtk_window_set_default_size(window, 1280, 720);
//gtk_widget_show(GTK_WIDGET(window));
改为:
static void my_application_activate(GApplication* application) {
...
gtk_widget_show(GTK_WIDGET(window));
gtk_widget_show(GTK_WIDGET(view));
}
GTKHeaderBar 的使用
它是AppBar小部件的替代品。因此,它允许你只需替换Material即可用于桌面客户端。
基本标题
import 'package:gtk_window/gtk_window.dart';
Scaffold(
appBar: GTKHeaderBar(
middle: Text('example title'),
),
)
带有溢出汉堡菜单的基本标题
import 'package:gtk_window/gtk_window.dart';
Scaffold(
appBar: GTKHeaderBar(
middle: Text('example title'),
trailing: ElevatedButton(
child: Icon(Icons.menu)
),
),
)
带搜索底部的基本标题
import 'package:gtk_window/gtk_window.dart';
Scaffold(
appBar: GTKHeaderBar(
middle: Text('Settings'),
trailing: GTKButton(
child: Icon(Icons.menu)
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(48),
child: TextField()
)
),
)
复杂分割视图带底部搜索栏
查看此分支
当前限制
- 操作系统不原生处理appbar,因此在使用窗口管理器时无法隐藏。
- 目前右键点击标题栏没有任何效果。
为什么不直接使用flutter提供的原生标题栏?
由于flutter团队支持的平台种类繁多,他们必须捆绑功能以在尽可能多的平台上简化开发。因此,他们制作的GTK appbar版本与MacOS和Windows的相似,它们只是悬浮在内容上方,没有太多控制权。在GTK中,appbar可以包含许多小部件,而没有这个包,你将无法控制它们。
免责声明
我对GTK本身不是很熟练,欢迎任何建议。
使用的插件
实际上,我没有为此包编写任何特定于平台的代码。我依赖于handy_window和window_manager维护者的出色工作。
完整示例代码
import 'package:flutter/material.dart';
import 'package:gtk_window/gtk_window.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// 这个小部件是你的应用的根。这是你应用程序的状态。
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData.light()
.copyWith(scaffoldBackgroundColor: const Color(0xFFfafafa)),
darkTheme: ThemeData.dark()
.copyWith(scaffoldBackgroundColor: const Color(0xFF242424)),
home: const MyHomePage(title: 'GTK window Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// 这个小部件是你的应用的主页。它是有状态的,意味着它有一个状态对象(下面定义),该对象包含影响其外观的字段。
// 这个类是状态的配置。它保存了由父级(在这个例子中是App小部件)提供的值(在这种情况下是标题),并用于State的build方法。子类中的字段总是标记为"final"。
final String title;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// 这次对setState的调用告诉Flutter框架某些事情已经改变,这导致它重新运行下面的构建方法,以便显示可以反映更新值。如果我们不调用setState()改变_counter,那么构建方法就不会再次被调用,因此看起来什么都不会发生。
_counter++;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
// 这个方法每次调用setState时都会被重新运行,比如上面的_incrementCounter方法所做的那样。
//
// Flutter框架已经被优化为使重新运行构建方法变得快速,因此你可以重建任何需要更新的东西,而不是逐个更改小部件实例。
return Scaffold(
appBar: GTKHeaderBar(
leading: [
IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Menu button pressed"),
),
);
},
),
],
trailing: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Search button pressed"),
),
);
},
),
IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("More button pressed"),
),
);
},
),
],
// 这里我们从MyHomePage对象获取值,该对象是由App.build方法创建的,并使用它来设置我们的appbar标题。
middle: Text(widget.title),
),
body: Center(
// Center是一个布局小部件。它接受一个子元素并将其定位在父元素的中间。
child: Column(
// Column也是一个布局小部件。它接受一个小部件列表并垂直排列它们。默认情况下,它水平调整自身大小以适应其子元素,并尝试与其父元素一样高。
//
// 调试绘制(按控制台中的“p”,选择Android Studio中的Flutter Inspector中的“切换调试绘制”操作,或Visual Studio Code中的“切换调试绘制”命令)可以看到每个小部件的线框。
//
// Column有各种属性可以控制其大小和位置。在这里我们使用mainAxisAlignment来垂直居中子元素;主轴是垂直方向(因为Columns是垂直的,所以交叉轴是水平的)。
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Scaffold(
appBar: GTKHeaderBar(
middle: Text("Second page"),
),
body: Center(
child: Text("Second page"),
),
),
),
);
},
child: const Text('Go to Second Page'),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // 这个逗号使得自动格式化更美观
);
}
}
更多关于Flutter GTK窗口集成插件gtk_window的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter GTK窗口集成插件gtk_window的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,关于Flutter与GTK窗口集成的插件gtk_window
,这里提供一个简单的代码示例来展示如何在Flutter应用中嵌入并使用GTK窗口。请注意,由于这是一个相对特殊和高级的使用场景,你可能需要一些额外的配置和依赖。
首先,确保你已经安装了必要的依赖项,包括Flutter和GTK开发环境。然后,你可能需要创建一个Flutter插件或使用现有的插件来实现GTK窗口的集成。由于gtk_window
可能不是一个官方或广泛使用的插件名称,这里我们假设你有一个自定义的插件或者通过某种方式能够调用GTK功能。
以下是一个简化的示例,展示如何在Flutter中调用GTK窗口(注意:这只是一个概念性的示例,实际实现可能需要根据具体的插件或库进行调整):
Flutter 端代码
- 创建一个新的Flutter项目(如果还没有的话):
flutter create flutter_gtk_integration
cd flutter_gtk_integration
- 修改
pubspec.yaml
文件,添加对自定义GTK插件的依赖(假设插件名为gtk_window_plugin
):
dependencies:
flutter:
sdk: flutter
gtk_window_plugin:
path: ../path/to/your/gtk_window_plugin # 使用本地路径或发布到pub.dev的插件
- 在
lib/main.dart
中编写代码来调用GTK窗口:
import 'package:flutter/material.dart';
import 'package:gtk_window_plugin/gtk_window_plugin.dart'; // 假设这是你的GTK插件包
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter GTK Integration'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
// 调用GTK窗口插件的方法
await GtkWindowPlugin.showGtkWindow();
},
child: Text('Show GTK Window'),
),
),
),
),
}
}
GTK 插件端代码
这里假设你已经创建了一个Flutter插件来封装GTK功能。以下是一个简化的插件代码结构示例:
- 插件的
lib/gtk_window_plugin.dart
文件:
import 'dart:async';
class GtkWindowPlugin {
static const MethodChannel _channel = MethodChannel('your.channel.name/gtk_window');
static Future<void> showGtkWindow() async {
try {
await _channel.invokeMethod('showGtkWindow');
} on PlatformException catch (e) {
print("Failed to invoke: '${e.message}'.");
}
}
}
- 插件的
android/CMakeLists.txt
和ios/
等原生代码配置(这里主要关注Linux/GTK部分):
-
Linux平台配置:
在
linux/
目录下创建必要的CMake和配置文件来链接GTK库。CMakeLists.txt 示例:
cmake_minimum_required(VERSION 3.10) project(gtk_window_plugin) find_package(PkgConfig REQUIRED) pkg_check_modules(GTK3 REQUIRED gtk+-3.0) include_directories(${GTK3_INCLUDE_DIRS}) link_directories(${GTK3_LIBRARY_DIRS}) add_library(gtk_window_plugin SHARED gtk_window_plugin.c ) target_link_libraries(gtk_window_plugin ${GTK3_LIBRARIES})
gtk_window_plugin.c 示例(一个简单的GTK窗口实现):
#include <gtk/gtk.h> #include <flutter_linux_plugin_registrar.h> static void show_gtk_window(FlutterLinuxPluginRegistrar* registrar) { GtkWidget *window; gtk_init(NULL, NULL); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "GTK Window from Flutter"); gtk_window_set_default_size(GTK_WINDOW(window), 400, 300); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(window); gtk_main(); } void flutter_gtk_window_plugin_register_with_registrar( FlutterLinuxPluginRegistrar* registrar) { g_autoptr(GMethodCallback) show_window_callback = g_cclosure_new(G_CALLBACK(show_gtk_window), NULL, NULL); g_signal_connect(registrar->messenger, "handle-method-call", G_CALLBACK(FlutterLinuxPluginRegistrarHandleMethodCall), show_window_callback); flutter_method_channel_set_method_call_handler( flutter_linux_plugin_registrar_get_channel(registrar, "your.channel.name/gtk_window"), [](FlutterMethodChannel* channel, FlutterMethodCall* call, FlutterMethodResponse* response) -> void { const gchar* method = flutter_method_call_get_name(call); if (g_strcmp0(method, "showGtkWindow") == 0) { show_gtk_window(flutter_linux_plugin_registrar_get_registrar(channel)); flutter_method_response_success(response); } else { flutter_method_response_not_implemented(response); } }, NULL); }
请注意,上述代码是一个非常简化的示例,用于展示如何在Flutter中调用GTK窗口。实际实现中,你可能需要处理更多的细节,比如插件注册、错误处理、线程同步等。此外,由于GTK和Flutter的运行环境差异,确保在正确的线程上调用GTK函数是非常重要的。
希望这个示例能帮助你理解如何在Flutter中集成GTK窗口。如果你有更具体的需求或遇到问题,请提供更多的上下文,以便给出更详细的指导。