Skip to content

[Flutter] 使用 bitsdojo_window 來自訂客製化視窗標題欄

Last Updated on 2024-08-20 by Clay

最近開始嘗試使用 Flutter 來實作桌面的應用程式,由於我是使用 Linux 的 Gnome 桌面,其預設的系統視窗欄(最上面的橫條)是純黑的,與我開發的 APP 其輕鬆活潑的調性不符合,所以這才找了個不錯的套件工具:bitsdojo_window

這個套件的強大之處在於,它同時支援 Windows、MacOS 和 Linux;但有些麻煩的是,它並不是安裝即可,也需要稍微做一些調整。


bitsdojo_window 的使用方式(主要是 Linux)

  1. 檢查 bitsdojo_window 的 pub.dev 確認最新版本與語法
  2. 在 pubspec.yaml 檔案中添加套件
  3. 修改 linux/my_application.cc
  4. 測試範例程式碼


Step 1. 檢查 bitsdojo_window 的 pub.dev 確認最新版本與語法

在我這裡,pub.dev 上的 bitsdojo_window 頁面最新版本是 0.1.6,所以我等等就是添加此版本。


Step 2: 在 pubspec.yaml 檔案中添加套件

dependencies:
  flutter:
    sdk: flutter
  bitsdojo_window: ^0.1.6


接著取得套件:

flutter pub get



Step 3: 修改 linux/my_application.cc

在 Linux 上使用 bitsdojo_window 時,我們需要修改 linux/my_application.cc 文件,找到以下程式碼:

gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));


並將其更改為:

auto bdw = bitsdojo_window_from(window);
bdw->setCustomFrame(true);
// gtk_window_set_default_size(window, 1280, 720); // <-- Comment this line
gtk_widget_show(GTK_WIDGET(window));


這樣可以讓應用程式使用自定義的視窗邊框,而不是系統的標題欄。

除此之外,也別忘了要在這份文件的開頭,確認有無引入 bitsdojo_window_plugin.h

#include <bitsdojo_window_linux/bitsdojo_window_plugin.h>



Step 4: 測試範例程式碼

在這裡我直接執行官方的範例程式碼:

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

void main() {
  runApp(const MyApp());
  doWhenWindowReady(() {
    final win = appWindow;
    const initialSize = Size(600, 450);
    win.minSize = initialSize;
    win.size = initialSize;
    win.alignment = Alignment.center;
    win.title = "Custom window with Flutter";
    win.show();
  });
}

const borderColor = Color(0xFF805306);

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: WindowBorder(
          color: borderColor,
          width: 1,
          child: Row(
            children: const [LeftSide(), RightSide()],
          ),
        ),
      ),
    );
  }
}

const sidebarColor = Color(0xFFF6A00C);

class LeftSide extends StatelessWidget {
  const LeftSide({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return SizedBox(
        width: 200,
        child: Container(
            color: sidebarColor,
            child: Column(
              children: [
                WindowTitleBarBox(child: MoveWindow()),
                Expanded(child: Container())
              ],
            )));
  }
}

const backgroundStartColor = Color(0xFFFFD500);
const backgroundEndColor = Color(0xFFF6A00C);

class RightSide extends StatelessWidget {
  const RightSide({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: [backgroundStartColor, backgroundEndColor],
              stops: [0.0, 1.0]),
        ),
        child: Column(children: [
          WindowTitleBarBox(
            child: Row(
              children: [Expanded(child: MoveWindow()), const WindowButtons()],
            ),
          )
        ]),
      ),
    );
  }
}

final buttonColors = WindowButtonColors(
    iconNormal: const Color(0xFF805306),
    mouseOver: const Color(0xFFF6A00C),
    mouseDown: const Color(0xFF805306),
    iconMouseOver: const Color(0xFF805306),
    iconMouseDown: const Color(0xFFFFD500));

final closeButtonColors = WindowButtonColors(
    mouseOver: const Color(0xFFD32F2F),
    mouseDown: const Color(0xFFB71C1C),
    iconNormal: const Color(0xFF805306),
    iconMouseOver: Colors.white);

class WindowButtons extends StatelessWidget {
  const WindowButtons({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        MinimizeWindowButton(colors: buttonColors),
        MaximizeWindowButton(colors: buttonColors),
        CloseWindowButton(colors: closeButtonColors),
      ],
    );
  }
}


Output:

可以看到,我們已經用上了自定義的視窗標題欄了!

這對於我接下來要嘗試的美術風格至關重要呢。


References


Read More

Leave a Reply