Ocideck/third_party/desktop_multi_window
Brenno de Winter 2c4a6f7358
Some checks failed
CI / Format · Analyze · Test (push) Has been cancelled
CI / Format · Analyze · Test (pull_request) Has been cancelled
Sync presenter annotations live, keep styling out of saved .md
Presentation fixes:
- Mirror the in-progress pen/highlighter stroke to the audience window
  live (new 'inkLive' channel) so highlights appear as they are drawn,
  not only after the pen lifts.
- Cover the macOS menu bar on the beamer: raise the audience window
  above .mainMenu level so the Apple/Wi-Fi strip no longer shows during
  a presentation.

Styling no longer lives in the file:
- generateDeck no longer embeds the ThemeProfile; a saved .md holds only
  content. The profile is inlined only for the transient audience-window
  payload (inlineStyleProfile: true), never to disk.
- On open, the app applies the active style profile (FileService.openDeck
  / activeProfileFor, DeckNotifier.loadDeck); applyMarkdown preserves the
  current profile.

Quality pass / tests green:
- Complete the consent-screen translations (English plus 7 missing
  strings per other language).
- Pass the consent gate in widget/ui-scale tests by seeding the consent
  key, so the app shell renders.
- Update markdown round-trip tests for the new default and add coverage
  for live stroke streaming and styling-free saves.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 19:25:05 +02:00
..
lib Add dual-screen presenter mode (slide on beamer, notes on laptop) 2026-06-06 21:25:34 +02:00
linux Extend dual-screen presenter to Windows and Linux 2026-06-06 22:03:56 +02:00
macos Sync presenter annotations live, keep styling out of saved .md 2026-06-11 19:25:05 +02:00
windows Extend dual-screen presenter to Windows and Linux 2026-06-06 22:03:56 +02:00
CHANGELOG.md Add dual-screen presenter mode (slide on beamer, notes on laptop) 2026-06-06 21:25:34 +02:00
LICENSE Add dual-screen presenter mode (slide on beamer, notes on laptop) 2026-06-06 21:25:34 +02:00
pubspec.yaml Add dual-screen presenter mode (slide on beamer, notes on laptop) 2026-06-06 21:25:34 +02:00
README.md Add dual-screen presenter mode (slide on beamer, notes on laptop) 2026-06-06 21:25:34 +02:00

desktop_multi_window

Pub

A Flutter plugin to create and manage multiple windows on desktop platforms.

Windows
Linux
macOS

Installation

Add desktop_multi_window to your pubspec.yaml:

dependencies:
  desktop_multi_window: ^latest_version

Getting Started

1. Initialize Multi-Window Support

In your main() function, initialize multi-window support before running your app:

import 'package:desktop_multi_window/desktop_multi_window.dart';

Future<void> main(List<String> args) async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Get the current window controller
  final windowController = await WindowController.fromCurrentEngine();
  
  // Parse window arguments to determine which window to show
  final arguments = parseArguments(windowController.arguments);
  
  // Run different apps based on the window type
  switch (arguments.type) {
    case YourArgumentDefinitions.main:
      runApp(const MainWindow());
    case YourArgumentDefinitions.sample:
      runApp(const SampleWindow());
    // Add more window types as needed
  }
}

2. Create New Windows

Use WindowController.create() to create and manage new windows:

// Create a new window
final controller = await WindowController.create(
  WindowConfiguration(
    hiddenAtLaunch: true,
    arguments: 'YOUR_WINDOW_ARGUMENTS_HERE',
  ),
);

// Show the window (if hidden at launch)
await controller.show();

3. Manage Existing Windows

Get all window controllers and manage them:

// Get all windows
final controllers = await WindowController.getAll();

// Find a specific window by business ID
for (var controller in controllers) {
  final args = parseArguments(controller.arguments);
  // Check window type
  if (args.type == YourArgumentDefinitions.sample) {
    await controller.center();
    await controller.show();
    return;
  }
}

// Listen to window changes
onWindowsChanged.listen((_) {
  // Handle window changes
});

4. Communication Between Windows

Use WindowMethodChannel for bidirectional communication between windows:

// In the target window, set up a method call handler
const channel = WindowMethodChannel('my_channel');
channel.setMethodCallHandler((call) async {
  switch (call.method) {
    case 'play':
      // Handle the method call
      return 'success';
    default:
      throw MissingPluginException('Not implemented: ${call.method}');
  }
});

// From another window, invoke methods
const channel = WindowMethodChannel('my_channel');
final result = await channel.invokeMethod('play');

5. Extend WindowController with Custom Methods

Create an extension to add custom functionality:

import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:window_manager/window_manager.dart';

extension WindowControllerExtension on WindowController {
  Future<void> doCustomInitialize() async {
    return await setWindowMethodHandler((call) async {
      switch (call.method) {
        case 'window_center':
          return await windowManager.center();
        case 'window_close':
          return await windowManager.close();
        default:
          throw MissingPluginException('Not implemented: ${call.method}');
      }
    });
  }
  
  Future<void> center() {
    return invokeMethod('window_center');
  }
  
  Future<void> close() {
    return invokeMethod('window_close');
  }
}

And now, you can center or close the window in the other window:

final controller = await WindowController.fromWindowId(other_window_id);

// Center the window
await controller.center();

// Close the window
await controller.close();

Working with Plugins in Sub-Windows

Each window created by this plugin has its own dedicated Flutter engine. Method channels cannot be shared between engines, so plugins must be manually registered for each new window.

Platform-Specific Plugin Registration

Windows

Edit windows/runner/flutter_window.cpp:

  1. Add the include at the top of the file:
 #include "flutter_window.h"
 
 #include <optional>
 
 #include "flutter/generated_plugin_registrant.h"
+#include "desktop_multi_window/desktop_multi_window_plugin.h"
  1. Register the callback in the OnCreate() method:
   RegisterPlugins(flutter_controller_->engine());
+  DesktopMultiWindowSetWindowCreatedCallback([](void *controller) {
+    auto *flutter_view_controller =
+        reinterpret_cast<flutter::FlutterViewController *>(controller);
+    auto *registry = flutter_view_controller->engine();
+    RegisterPlugins(registry);
+  });
   SetChildContent(flutter_controller_->view()->GetNativeWindow());

The RegisterPlugins function will automatically register all plugins for each new window.

macOS

Edit macos/Runner/MainFlutterWindow.swift:

  1. Add the import at the top of the file:
 import Cocoa
 import FlutterMacOS
+import desktop_multi_window
  1. Register the callback in the awakeFromNib() method:
     RegisterGeneratedPlugins(registry: flutterViewController)
     
+    FlutterMultiWindowPlugin.setOnWindowCreatedCallback { controller in
+      // Register the plugin which you want access from other isolate.
+      RegisterGeneratedPlugins(registry: controller)
+    }
+
     super.awakeFromNib()

The RegisterGeneratedPlugins function will automatically register all plugins for each new window.

Linux

Edit linux/my_application.cc:

  1. Add the include at the top of the file:
 #include "my_application.h"
 
 #include <flutter_linux/flutter_linux.h>
 #ifdef GDK_WINDOWING_X11
 #include <gdk/gdkx.h>
 #endif
 
 #include "flutter/generated_plugin_registrant.h"
 
+#include "desktop_multi_window/desktop_multi_window_plugin.h"
  1. Register the callback in the my_application_activate() function:
   fl_register_plugins(FL_PLUGIN_REGISTRY(view));
 
+  desktop_multi_window_plugin_set_window_created_callback([](FlPluginRegistry* registry){
+    fl_register_plugins(registry);
+  });
+
   gtk_widget_grab_focus(GTK_WIDGET(view));

The fl_register_plugins function will automatically register all plugins for each new window.

Integration with window_manager

This plugin works great with window_manager to control window properties:

by now, you should this fork version with a bit fix

  window_manager:
    git:
      url: https://github.com/boyan01/window_manager.git
      path: packages/window_manager
      ref: 6fae92d21b4c80ce1b8f71c1190d7970cf722bd4
import 'package:window_manager/window_manager.dart';

// Configure window options
WindowOptions windowOptions = const WindowOptions(
  size: Size(800, 600),
  center: true,
  backgroundColor: Colors.transparent,
  skipTaskbar: false,
  titleBarStyle: TitleBarStyle.hidden,
);

windowManager.waitUntilReadyToShow(windowOptions, () async {
  await windowManager.show();
  await windowManager.focus();
});

// Prevent window from closing immediately
windowManager.setPreventClose(true);
windowManager.addListener(this); // Must implement WindowListener

Example

Check out the example directory for a complete working application that demonstrates:

  • Creating multiple window types
  • Single instance vs multi-instance windows
  • Communication between windows
  • Custom window extensions
  • Plugin registration for video playback
  • Window lifecycle management

License

MIT