feat: Multi-window presenter mode
Description
It would be awesome to have a simple multi window plugin for the presenter mode!
When starting the app on desktop (not web), it would simply open a second window in presenter mode.
This package package might help: desktop_multi_window
@aloisdeniel Hey, yeah, makes total sense. I was trying to postpone using any plugin for this, considering that native Flutter multi-window support was coming for desktop soon, but... I said this more than a year ago already, I suppose 😄 For now, flutter_deck supports this out of the box for Web only.
In case someone wants a macOS presenter window here is a quick implementation:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
// ignore: depend_on_referenced_packages
import 'package:flutter_deck_client/flutter_deck_client.dart';
class PresenterClient implements FlutterDeckClient {
PresenterClient(this.windowId);
final int windowId;
final StreamController<FlutterDeckState> _stateController =
StreamController<FlutterDeckState>.broadcast();
AppLifecycleListener? _appLifecycleListener;
static Future<({bool isPresenter, int windowId})> open(
List<String> args,
) async {
if (args.firstOrNull == 'multi_window') {
return (isPresenter: true, windowId: 0);
}
final window = await DesktopMultiWindow.createWindow(jsonEncode({}));
window
..setFrame(const Offset(0, 0) & const Size(880, 520))
..center()
..setTitle('Presenter')
..show();
return (isPresenter: false, windowId: window.windowId);
}
@override
Stream<FlutterDeckState> get flutterDeckStateStream =>
_stateController.stream;
@override
void dispose() {
_stateController.close();
DesktopMultiWindow.setMethodHandler(null);
_appLifecycleListener?.dispose();
_appLifecycleListener = null;
}
@override
void init([FlutterDeckState? state]) async {
DesktopMultiWindow.setMethodHandler(_handleMethodCallback);
// Workaround: https://github.com/MixinNetwork/flutter-plugins/issues/319
if (windowId != 0 && Platform.isMacOS) {
_appLifecycleListener = AppLifecycleListener(
onHide:
// ignore: invalid_use_of_protected_member
() => SchedulerBinding.instance.handleAppLifecycleStateChanged(
AppLifecycleState.inactive,
),
);
}
if (state == null) {
return;
}
updateState(state);
}
Future<dynamic> _handleMethodCallback(
MethodCall call,
int fromWindowId,
) async {
if (call.method == 'updateState') {
_stateController.add(
FlutterDeckState.fromJson(jsonDecode(call.arguments)),
);
}
}
@override
void updateState(FlutterDeckState state) {
DesktopMultiWindow.invokeMethod(
windowId,
'updateState',
jsonEncode(state.toJson()),
);
}
}
And usage:
void main(List<String> args) async {
final presenter = await PresenterClient.open(args);
return FlutterDeckApp(
isPresenterView: widget.isPresenter,
client: PresenterClient(widget.windowId),
// ...