Flutter Desktop Single-Instance Applications

May 13, 2025

Werner Scholtz

Most desktop applications are single-instance, meaning that if the user attempts to open the application again, the existing instance is typically brought to the foreground instead of launching a new one.

Flutter applications do not behave like this out of the box for all desktop platforms. Desktop Linux and Windows require some extra work.

Section 1: Using packages

Using packages to achieve single-instance applications in flutter is possible. Basic functionality to focus on an existing window when it is re-launched is supported. Using packages is a lot simpler than modifying platform specific code generated by flutter.

Recommended packages:

Example:

  1. flutter pub add window_manager
  2. flutter pub add windows_single_instance
  3. flutter pub add unix_single_instance
void main(List<String> args) async {
	WidgetsFlutterBinding.ensureInitialized();
  	// Initialize the window manager to control the app window
	await windowManager.ensureInitialized();

	 // Enforce single-instance behavior based on the platform
	if (Platform.isLinux || Platform.isMacOS) {
	  if (!await unixSingleInstance(args, _onSecondInstance)) exit(0);
	} else if (Platform.isWindows) {
	  await WindowsSingleInstance.ensureSingleInstance(
		  args,
		  "custom_identifier",
		  onSecondWindow: _onSecondInstance,
	  );
	}

  runApp(const MyApp());
}

// Handle what happens when a second instance is launched
void _onSecondInstance(List<dynamic> decodedArgs) async {
  windowManager.waitUntilReadyToShow(null, () async {
    await windowManager.show();
    await windowManager.focus();
  });
}

Section 2: Manual implementation

You can achieve the same, and in some cases more reliable results, by implementing single-instance enforcement in the files generated by flutter. Doing it this way allows you much finer control over the behavior. It does have some downsides as you need to maintain the code yourself.

Note:
It is possible to enforce single instances using dart's
RandomAccessFile as it provides a way to lock files. The first instance would lock a file the second instance would check if the file is locked and then not start.

Linux Desktop

Desktop Linux applications built with Flutter use the GTK framework under the hood. By default, a Flutter application on Linux may allow multiple instances to run simultaneously. To enforce single-instance behavior, you can make changes to the linux/my_application.cc file. Below are the steps and explanations for each modification.

Step 1: Remove the G_APPLICATION_NON_UNIQUE Flag

The G_APPLICATION_NON_UNIQUE flag in GTK allows multiple instances of an application to run. To enforce single-instance behavior, you need to remove this flag from the application initialization code.

MyApplication* my_application_new() {
  return MY_APPLICATION(
	g_object_new(
		my_application_get_type(), 
		"application-id", APPLICATION_ID,
		// "flags", G_APPLICATION_NON_UNIQUE,
    	nullptr)
	);
}

By removing the G_APPLICATION_NON_UNIQUE flag, the application becomes single-instance.

Step 2: Check for Existing Windows and Present Them

When the application is re-launched, the GApplication::activate signal is triggered. In this signal handler, you can check if a window is already open. If it exists, bring it to the foreground using the gtk_window_present() function. Otherwise, create a new window.

// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
    MyApplication* self = MY_APPLICATION(application);

    GList* list = gtk_application_get_windows(
GTK_APPLICATION(application));

    GtkWindow* existing_window = list ? GTK_WINDOW(list->data) : NULL;

    if (existing_window) {
        gtk_window_present(existing_window);
    } else {
        // ...
		// Generated flutter code
		// ...
    }
}

Note:
Linux distributions running wayland have strict focus behaviors and therefore the above solution is inadequate for focusing the window. Instead a request to show the window is made, and might appear as a notification.

Windows

By default, Windows desktop applications allow multiple instances to run simultaneously. To enforce single-instance behavior in a Flutter Windows app, you can make modifications to the windows/runner/main.cpp file.

Step 1: Create a Named Mutex

By creating a named mutex (short for "mutual exclusion") when starting a flutter application, we can use it to enforce single-instance behavior.

Add the following code to the beginning of the wWinMain() function:

#define APP_MUTEX_NAME L"Global\\MyUniqueFlutterAppMutex"

int APIENTRY wWinMain(...) {
	HANDLE hMutex = CreateMutex(NULL, TRUE, APP_MUTEX_NAME);
	if (GetLastError() == ERROR_ALREADY_EXISTS) {
    	// App is already running.
	}

	...
}

Step 2: Find and Present the Existing Window

When the application detects that another instance is running, it brings the existing app window to the foreground instead of launching a new one. If you use something like window_manager to change the name of the window the single instance will still be enforced however focusing on the existing window will not work.

Add the following code inside the if (GetLastError() == ERROR_ALREADY_EXISTS) block:

HWND hwnd = FindWindow(NULL, APP_WINDOW_NAME);
if (hwnd) {
    ShowWindow(hwnd, SW_RESTORE);
    SetForegroundWindow(hwnd);
}
return 0;

Step 3: Set a Unique Window Name

The window title serves as the identifier for FindWindow.

Update the Create call for the main window to use a unique name defined as APP_WINDOW_NAME:

#define APP_WINDOW_NAME L"unique_title"

if (!window.Create(APP_WINDOW_NAME, origin, size)) {
    return EXIT_FAILURE;
}

When the application exits or crashes, the mutex is automatically destroyed.

Add this code before the return EXIT_SUCCESS; statement:

if (hMutex) {
    ReleaseMutex(hMutex);
}

macOS

Flutter on macOS typically handles single-instance applications quite well on its own. When you launch a second instance of an application, the system usually brings the existing instance to the front. However you can ensure the behavior by modifying the macos/Runner/AppDelegate.swift file.

Step 1: Override the applicationShouldHandleReopen Function

This method ensures that if the app is already running and a new instance is opened the existing window is brought to the foreground.

Add this function to the AppDelegate class.

override func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
  if !flag {
      sender.windows.forEach { $0.makeKeyAndOrderFront(self) }
  }
  return true
}

Step 2: Override the applicationDidFinishLaunching Function

This function is called when the app finished launching, it checks if there are other instances of the app running. If it finds another instance it will activate the existing one and terminate the the new instance.

Add this function to the AppDelegate class.

override func applicationDidFinishLaunching(_ notification: Notification) {
  super.applicationDidFinishLaunching(notification)

  let appBundleIdentifier = Bundle.main.bundleIdentifier!

  let runningApps = NSRunningApplication.runningApplications(withBundleIdentifier: appBundleIdentifier)
  if runningApps.count > 1 {
      // App is already running, activate the existing instance
      runningApps.first?.activate(options: .activateIgnoringOtherApps)
      NSApp.terminate(nil)
  }
}

Summary

By implementing single-instance functionality, you can provide a polished and user-friendly experience for desktop applications. Whether you choose to use existing packages or dive into platform-specific code, Flutter makes it possible to achieve this seamlessly across platforms. The choice ultimately depends on your app’s requirements and your development preferences.

1 comment on "Flutter Desktop Single-Instance Applications"

Sravdar

May 14, 2025, 5:48 AM

Thanks for the article. Nice to see clear examples of how to handle single instance apps in Flutter desktop. It’s a niche topic but definitely useful for certain use cases.

Leave a Comment

Your Email address will not be published

KDAB is committed to ensuring that your privacy is protected.

  • Only the above data is collected about you when you fill out this form.
  • The data will be stored securely.
  • The data will only be used to contact you about possible business together.
  • If we do not engage in business within 3 years, your personal data will be erased from our systems.
  • If you wish for us to erase it earlier, email us at info@kdab.com.

For more information about our Privacy Policy, please read our privacy policy