You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Windows端如何通过按钮点击唤起第二个应用窗口

Flutter Windows 端唤起独立子窗口实现方案

直接用维护成熟的desktop_multi_window库实现即可,不需要编写大量原生Win32代码,全流程实操步骤如下:

  • 第一步:添加项目依赖
    在项目根目录的pubspec.yaml中添加依赖,版本可按需替换为pub仓库上的最新稳定版:
dependencies:
  flutter:
    sdk: flutter
  desktop_multi_window: ^0.2.0

添加完成后执行flutter pub get拉取依赖。

  • 第二步:配置Windows原生入口
    打开项目路径下的windows/runner/main.cpp文件,替换原有入口逻辑,增加多窗口分发判断,否则子窗口无法正常拉起:
#include "flutter_window.h"
#include "utils.h"
#include <desktop_multi_window/desktop_multi_window_plugin.h>

int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
                      _In_ wchar_t* command_line, _In_ int show_command) {
  // 拦截子窗口启动请求,走子窗口独立渲染逻辑
  if (desktop_multi_window::HandleWindowStart(instance, show_command)) {
    return EXIT_SUCCESS;
  }
  // 保留原有主窗口初始化逻辑即可
  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
    CreateAndAttachConsole();
  }
  INITCOMMONCONTROLSEX iccex = {.dwSize = sizeof(iccex), .dwICC = ICC_STANDARD_CLASSES};
  ::InitCommonControlsEx(&iccex);
  flutter::DartProject project(L"data");
  std::vector<std::string> command_line_arguments = GetCommandLineArguments();
  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
  FlutterWindow window(project);
  Win32Window::Point origin(10, 10);
  Win32Window::Size size(1280, 720);
  if (!window.Create(L"相册主窗口", origin, size)) {
    return EXIT_FAILURE;
  }
  window.SetQuitOnClose(true);
  ::MSG msg;
  while (::GetMessage(&msg, nullptr, 0, 0)) {
    ::TranslateMessage(&msg);
    ::DispatchMessage(&msg);
  }
  ::CoUninitialize();
  return EXIT_SUCCESS;
}
  • 第三步:编写Dart层多窗口分发逻辑
    修改lib/main.dart入口文件,启动时先判断当前实例属于主窗口还是子窗口,分别加载对应页面:
import 'dart:convert';
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/material.dart';
// 以下两个页面替换为你自己业务中的实际文件路径即可
import 'pages/slideshow_page.dart';
import 'pages/home_page.dart';

void main(List<String> args) async {
  WidgetsFlutterBinding.ensureInitialized();
  // 识别子窗口启动请求
  if (args.isNotEmpty) {
    try {
      final params = jsonDecode(args.first);
      if (params['type'] == 'slideshow') {
        final windowId = params['windowId'];
        final controller = WindowController.fromWindowId(windowId);
        // 配置幻灯片窗口属性,可按需调整为全屏、置顶等模式
        controller
          ..setTitle("幻灯片放映")
          ..setFullscreen(true)
          ..show();
        // 子窗口直接加载幻灯片页面,传入照片列表、起始索引等业务参数
        runApp(MaterialApp(
          debugShowCheckedModeBanner: false,
          home: SlideshowPage(
            photoPaths: List<String>.from(params['photoList']),
            startIndex: params['startIndex'] as int,
          ),
        ));
        return;
      }
    } catch (_) {}
  }
  // 主窗口加载正常相册首页
  runApp(const MaterialApp(
    debugShowCheckedModeBanner: false,
    home: HomePage(),
  ));
}
  • 第四步:主窗口按钮点击唤起幻灯片窗口
    在主页面的幻灯片放映按钮点击回调中,调用窗口创建接口,传入需要的业务参数即可拉起独立子窗口:
// 幻灯片按钮点击逻辑
void _handleStartSlideshow() async {
  // 组装传给子窗口的参数
  final windowParams = {
    'type': 'slideshow',
    'photoList': photoPathList, // 待放映的所有照片本地路径列表
    'startIndex': currentPhotoIndex, // 从当前选中的照片开始播放
  };
  // 创建并展示独立窗口
  final window = await DesktopMultiWindow.createWindow(jsonEncode(windowParams));
  window.show();
}
  • 补充说明
    • 主、子窗口是独立的Flutter渲染实例,互不阻塞,跨窗口通信可以用DesktopMultiWindow.invokeMethod实现,比如主窗口控制幻灯片切页、幻灯片播放结束通知主窗口都可以用这个接口
    • 子窗口关闭不会影响主窗口运行,主窗口退出时会自动回收所有子窗口,符合桌面应用的常规交互逻辑
    • 如果需要自定义窗口样式(比如无边框、透明、置顶),直接通过WindowController的对应接口设置即可,不需要额外编写原生代码

实现效果参考:
双窗口运行效果示意图


内容的提问来源于stack exchange,提问作者Polla A. Fattah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 12:21:22