PicaComic 多窗口通信机制:桌面端数据共享实现

引言

在桌面端应用中,多窗口通信是一个常见的需求,它允许不同窗口之间共享数据和状态,提升用户体验。PicaComic作为一款使用Flutter构建的漫画应用,在桌面端实现了高效的多窗口通信机制。本文将深入探讨PicaComic的多窗口通信实现方式,包括状态管理、窗口管理和数据共享等关键技术点。

状态管理基础

PicaComic采用了自定义的状态管理方案,核心类为StateController。该类提供了状态注册、查找、更新等功能,为多窗口通信奠定了基础。

StateController类

StateController是一个抽象类,提供了状态管理的核心功能。它通过静态方法putfind实现了状态的注册和查找,支持带标签的状态管理,方便区分不同实例。

abstract class StateController {
  static final _controllers = <StateControllerWrapped>[];

  static T put<T extends StateController>(T controller,
      {Object? tag, bool autoRemove = false}) {
    _controllers.add(StateControllerWrapped(controller, autoRemove, tag));
    return controller;
  }

  static T find<T extends StateController>({Object? tag}) {
    try {
      return _controllers
          .lastWhere((element) =>
              element.controller is T && (tag == null || tag == element.tag))
          .controller as T;
    } catch (e) {
      throw StateError("$T with tag $tag Not Found");
    }
  }

  // 其他方法...
}

源码路径:lib/foundation/state_controller.dart

StateBuilder组件

StateBuilder是一个泛型组件,用于构建依赖于StateController的UI。它会在初始化时查找或创建指定类型的StateController,并在状态更新时重建UI。

class StateBuilder<T extends StateController> extends StatefulWidget {
  const StateBuilder({
    super.key,
    this.init,
    this.dispose,
    this.initState,
    this.tag,
    required this.builder,
    this.id,
  });

  final Widget Function(T controller) builder;

  // 其他属性和方法...
}

源码路径:lib/foundation/state_controller.dart

窗口管理

PicaComic在桌面端使用了window_manager插件进行窗口管理,并通过自定义的WindowFrame组件实现了窗口标题栏和边框的定制。

App类中的平台判断

App类中定义了一系列静态属性,用于判断当前运行的平台,这对于区分桌面端和移动端的窗口行为非常重要。

class App {
  static bool get isAndroid => Platform.isAndroid;
  static bool get isIOS => Platform.isIOS;
  static bool get isWindows => Platform.isWindows;
  static bool get isLinux => Platform.isLinux;
  static bool get isMacOS => Platform.isMacOS;
  static bool get isDesktop =>
      Platform.isWindows || Platform.isLinux || Platform.isMacOS;
  static bool get isMobile => Platform.isAndroid || Platform.isIOS;

  // 其他属性和方法...
}

源码路径:lib/foundation/app.dart

WindowFrame组件

WindowFrame组件是PicaComic桌面端窗口的核心容器,它包含了自定义的标题栏、窗口控制按钮和侧边栏等元素。

class WindowFrame extends StatelessWidget {
  const WindowFrame(this.child, {super.key});

  final Widget child;

  @override
  Widget build(BuildContext context) {
    StateController.putIfNotExists<WindowFrameController>(
        WindowFrameController());
    if (App.isMobile) return child;
    return StateBuilder<WindowFrameController>(builder: (controller) {
      // 构建窗口框架...
    });
  }

  // 其他方法...
}

源码路径:lib/components/window_frame.dart

WindowFrameController

WindowFrameControllerWindowFrame组件的状态控制器,管理窗口的主题、边框显示等状态。

class WindowFrameController extends StateController {
  bool useDarkTheme = false;
  bool isHideWindowFrame = false;

  void setDarkTheme() {
    useDarkTheme = true;
    update();
  }

  void resetTheme() {
    useDarkTheme = false;
    update();
  }

  VoidCallback openSideBar = () {};

  void hideWindowFrame() {
    isHideWindowFrame = true;
    update();
  }

  void showWindowFrame() {
    isHideWindowFrame = false;
    update();
  }
}

源码路径:lib/components/window_frame.dart

多窗口通信实现

PicaComic的多窗口通信主要依赖于全局状态管理和窗口事件监听。通过将状态存储在全局的StateController中,不同窗口可以共享和更新同一份状态。

全局状态共享

PicaComic使用StateController的静态方法putfind来实现全局状态的注册和查找。例如,在应用初始化时注册一个全局的WindowFrameController

StateController.putIfNotExists<WindowFrameController>(WindowFrameController());

然后在其他窗口中通过find方法获取该控制器:

var controller = StateController.find<WindowFrameController>();

这样,不同窗口就可以通过同一个WindowFrameController实例来共享状态和触发更新。

窗口事件监听

PicaComic使用window_manager插件监听窗口事件,如最大化、最小化、关闭等,并在事件发生时更新状态或执行相应操作。

class _WindowButtonsState extends State<WindowButtons> with WindowListener {
  bool isMaximized = false;

  @override
  void initState() {
    windowManager.addListener(this);
    windowManager.isMaximized().then((value) {
      if (value) {
        setState(() {
          isMaximized = true;
        });
      }
    });
    super.initState();
  }

  @override
  void onWindowMaximize() {
    setState(() {
      isMaximized = true;
    });
    super.onWindowMaximize();
  }

  @override
  void onWindowUnmaximize() {
    setState(() {
      isMaximized = false;
    });
    super.onWindowUnmaximize();
  }

  // 其他事件处理方法...
}

源码路径:lib/components/window_frame.dart

数据持久化

为了在窗口关闭后保留状态,PicaComic实现了窗口位置和大小的持久化存储。

class WindowPlacement {
  final Rect rect;
  final bool isMaximized;

  // 其他属性和方法...

  Future<void> writeToFile() async {
    var file = File("${App.dataPath}/window_placement");
    await file.writeAsString(jsonEncode({
      'width': rect.width,
      'height': rect.height,
      'x': rect.topLeft.dx,
      'y': rect.topLeft.dy,
      'isMaximized': isMaximized
    }));
  }

  static Future<WindowPlacement> loadFromFile() async {
    try {
      var file = File("${App.dataPath}/window_placement");
      if (!file.existsSync()) {
        return defaultPlacement;
      }
      var json = jsonDecode(await file.readAsString());
      var rect =
          Rect.fromLTWH(json['x'], json['y'], json['width'], json['height']);
      return WindowPlacement(rect, json['isMaximized']);
    } catch (e) {
      return defaultPlacement;
    }
  }
}

源码路径:lib/components/window_frame.dart

实际应用场景

多窗口漫画阅读

在PicaComic中,用户可以同时打开多个窗口阅读不同的漫画。通过全局状态管理,这些窗口可以共享用户的阅读历史、收藏列表等数据。

主题同步

当用户在一个窗口中切换主题时,WindowFrameController会更新全局状态,其他窗口会通过状态监听自动应用新的主题设置。

void setDarkTheme() {
  useDarkTheme = true;
  update();
}

void resetTheme() {
  useDarkTheme = false;
  update();
}

源码路径:lib/components/window_frame.dart

窗口位置记忆

PicaComic会记住每个窗口的位置和大小,当用户重新打开窗口时,会恢复到上次关闭时的状态。

static Future<WindowPlacement> loadFromFile() async {
  try {
    var file = File("${App.dataPath}/window_placement");
    if (!file.existsSync()) {
      return defaultPlacement;
    }
    var json = jsonDecode(await file.readAsString());
    var rect =
        Rect.fromLTWH(json['x'], json['y'], json['width'], json['height']);
    return WindowPlacement(rect, json['isMaximized']);
  } catch (e) {
    return defaultPlacement;
  }
}

源码路径:lib/components/window_frame.dart

总结

PicaComic通过自定义的StateController实现了全局状态管理,结合window_manager插件提供的窗口操作能力,构建了一个高效的多窗口通信机制。这种机制不仅满足了漫画阅读场景下的多窗口需求,也为其他桌面端Flutter应用提供了参考。

核心实现包括:

  1. 使用StateController进行全局状态管理
  2. 通过WindowFrameWindowFrameController管理窗口状态
  3. 利用文件存储持久化窗口位置和大小
  4. 通过状态更新实现多窗口数据同步

这种设计既充分利用了Flutter的跨平台特性,又针对桌面端进行了专门的优化,为用户提供了流畅的多窗口体验。

参考资料

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐