Skip to content

[Flutter] Using bitsdojo_window to Customize Window Title Bar

Last Updated on 2024-08-20 by Clay

Recently, I started experimenting with Flutter to develop desktop applications. Since I’m using the Linux Gnome desktop, its default system window bar (the top strip) is completely black, which doesn't match the light and playful tone of the app I’m developing. So, I found a great tool: bitsdojo_window.

The strength of this package is that it supports Windows, MacOS, and Linux simultaneously. However, it’s a bit tricky as it requires some adjustments, not just a simple installation.


bitsdojo_window usage guide (mainly for Linux)

  1. Check the pub.dev page of bitsdojo_window to confirm the latest version and syntax
  2. Add the package to your pubspec.yaml file
  3. Modify linux/my_application.cc
  4. Test the sample code


Step 1. Check the pub.dev page of bitsdojo_window to confirm the latest version and syntax

On my end, the latest version of bitsdojo_window on pub.dev is 0.1.6, so I’ll be adding this version.


Step 2: Add the package to your pubspec.yaml file

dependencies:
  flutter:
    sdk: flutter
  bitsdojo_window: ^0.1.6


Then, get the package:

flutter pub get



Step 3: Modify linux/my_application.cc

When using bitsdojo_window on Linux, we need to modify the linux/my_application.cc file. Locate the following code:

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


And change it to:

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));


This allows the application to use a custom window frame instead of the system’s title bar.

Additionally, make sure that at the beginning of the file, you have included bitsdojo_window_plugin.h:

#include <bitsdojo_window_linux/bitsdojo_window_plugin.h>



Step 4: Test the sample code

Here, I’m running the official sample code directly:

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:

As you can see, we’ve successfully applied the custom window title bar!

This will be crucial for the art style I’m aiming for in my next attempts.


References


Read More

Leave a Reply