Flutter 桌面端实现多窗口

5,926 阅读1分钟

尽管Flutter桌面端尚未实施多窗口,但一些社区插件已经可以做到。插件:Desktop_multi_window,它支持所有三个桌面平台并具有强大的API。

image.png

1:安装

flutter pub add desktop_multi_window

目前我使用的版本

dependencies:
  desktop_multi_window: ^0.2.0

官方例子:

import 'dart:convert';

import 'package:collection/collection.dart';
import 'package:desktop_lifecycle/desktop_lifecycle.dart';
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter_multi_window_example/event_widget.dart';

void main(List<String> args) {
  if (args.firstOrNull == 'multi_window') {
    final windowId = int.parse(args[1]);
    final argument = args[2].isEmpty
        ? const {}
        : jsonDecode(args[2]) as Map<String, dynamic>;
    runApp(_ExampleSubWindow(
      windowController: WindowController.fromWindowId(windowId),
      args: argument,
    ));
  } else {
    runApp(const _ExampleMainWindow());
  }
}

class _ExampleMainWindow extends StatefulWidget {
  const _ExampleMainWindow({Key? key}) : super(key: key);

  @override
  State<_ExampleMainWindow> createState() => _ExampleMainWindowState();
}

class _ExampleMainWindowState extends State<_ExampleMainWindow> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Column(
          children: [
            TextButton(
              onPressed: () async {
                final window =
                    await DesktopMultiWindow.createWindow(jsonEncode({
                  'args1': 'Sub window',
                  'args2': 100,
                  'args3': true,
                  'business': 'business_test',
                }));
                window
                  ..setFrame(const Offset(0, 0) & const Size(1280, 720))
                  ..center()
                  ..setTitle('Another window')
                  ..resizable(false)
                  ..show();
              },
              child: const Text('Create a new World!'),
            ),
            TextButton(
              child: const Text('Send event to all sub windows'),
              onPressed: () async {
                final subWindowIds =
                    await DesktopMultiWindow.getAllSubWindowIds();
                for (final windowId in subWindowIds) {
                  DesktopMultiWindow.invokeMethod(
                    windowId,
                    'broadcast',
                    'Broadcast from main window',
                  );
                }
              },
            ),
            Expanded(
              child: EventWidget(controller: WindowController.fromWindowId(0)),
            )
          ],
        ),
      ),
    );
  }
}

class _ExampleSubWindow extends StatelessWidget {
  const _ExampleSubWindow({
    Key? key,
    required this.windowController,
    required this.args,
  }) : super(key: key);

  final WindowController windowController;
  final Map? args;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Column(
          children: [
            if (args != null)
              Text(
                'Arguments: ${args.toString()}',
                style: const TextStyle(fontSize: 20),
              ),
            ValueListenableBuilder<bool>(
              valueListenable: DesktopLifecycle.instance.isActive,
              builder: (context, active, child) {
                if (active) {
                  return const Text('Window Active');
                } else {
                  return const Text('Window Inactive');
                }
              },
            ),
            TextButton(
              onPressed: () async {
                windowController.close();
              },
              child: const Text('Close this window'),
            ),
            Expanded(child: EventWidget(controller: windowController)),
          ],
        ),
      ),
    );
  }
}

2:注意 (问题来了)

很多小伙伴可能注意到了,官方提供的例子当中只能打开一个窗口,那么打开多个窗口可能就没什么头绪了。然并卵,我们在实际开发中不可能只打开一个窗口的,那么官方没有提供例子只有我自己研究了(其实很多api我都看不懂,有懂的小伙伴可以在评论区留言教导一下更好的方法)。

  • 那么我是怎么实现多窗口的呢?
  • 附上我的关键代码
import 'package:collection/collection.dart';
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:macos_ui/macos_ui.dart';

import 'windows/detail_window/detail_window.dart';

void main(List<String> args) {
  if (args.firstOrNull == 'multi_window') {
    print('执行了窗口:$args');
    final windowId = int.parse(args[1]);
    print('窗口ID$windowId');
    final arguments = args[2].isEmpty
        ? const {}
        : jsonDecode(args[2]) as Map<String, dynamic>;

    /// 通过打开窗口的参数来选择创建的窗口,这里类似咱们使用的路由,小伙伴们可以抽离出去
    Map windows = {
      "AboutWindow": AboutWindow(
        windowController: WindowController.fromWindowId(windowId),
        args: arguments,
      ),
      "DetailWindow": DetailWindow(
        windowController: WindowController.fromWindowId(windowId),
        args: arguments,
      ),
    };
    // print('窗口名:${arguments["name]}');

    runApp(windows[arguments["name"]]);
  } else {
    runApp(const App());
  }
}
  • 调用代码
final window = await DesktopMultiWindow.createWindow(jsonEncode(
  {
    'name': 'DetailWindow', // 这里的名称不能错误,类似路由跳转的 path
    'args2': 100,
    'args3': true,
  },
));
debugPrint('$window');
window
  ..setFrame(const Offset(100, 100) & const Size(850, 350))
  ..center()
  ..setTitle('Detail macos_ui_app')
  ..show();

3:大功告成

image.png

最后

小伙伴们有更好方案可以评论区留言,互相学习,共同成长。