使用flutter开发的Windows桌面壁纸软件

1,264 阅读10分钟

使用flutter开发的Windows桌面壁纸软件

1、项目背景介绍、以 Flutter 重构波奇壁纸软件:从臃肿到轻盈的蜕变之旅

在探索桌面应用开发的历程中,我曾使用 Electron 打造了一款 壁纸集 软件。项目初衷是为了熟悉 Electron 开发流程,同时满足自己对个性化壁纸管理的需求。Electron 的跨平台特性和与前端技术栈的无缝结合,让我能快速搭建起应用框架。在开发过程中,采用了 electron-vite 提升构建效率,搭配 vue3、pinia 和 ts 构建现代化前端架构,element plus 则丰富了 UI 组件库,使得界面设计更加美观实用。从首页展示,到多级分类菜单,再到不同壁纸源的分页加载、搜索功能等,都逐步实现和完善,最终成功开发出这款简易但功能较为全面的壁纸软件,并将其开源共享。

然而,当面对软件打包后的体积问题时,我陷入了沉思。90M 左右的打包体积,安装后膨胀至几百 M,这对于一款壁纸应用来说显然是难以接受的。过大的体积不仅增加了用户的下载负担,还可能导致用户设备存储压力骤增,影响使用体验,违背了开发一款轻量化、便捷工具的初衷。

痛定思痛,我决定另辟蹊径,采用 Flutter 对壁纸软件进行重构,打造出全新的波奇壁纸软件。Flutter 凭借其独特的架构和编译机制,在跨平台开发中展现出了卓越的性能优势和体积控制能力。它使用 Dart 语言开发,拥有高效的渲染引擎,能够将代码和资源进行高度优化整合。在 Flutter 的加持下,波奇壁纸软件的打包体积大幅缩减至 11M,安装后的体积也仅有 30M 左右,体积相较于 Electron 版本缩小了 10 倍不止。这不仅显著降低了软件对用户设备存储空间的占用,还让软件的分发和安装变得更加高效便捷,极大地提升了软件的可用性和竞争力。

通过这次从 Electron 到 Flutter 的重构之旅,我深刻体会到技术选型对于软件质量的深远影响。在追求功能实现的同时,更应注重软件性能和用户体验的平衡。Flutter 以其出色的性能表现和轻量化特性,为我提供了一种全新的开发思路和解决方案,助力我成功打造出了波奇壁纸软件这一优质的桌面壁纸管理工具。未来,我将继续探索 Flutter 开发的更多可能性,不断优化和拓展波奇壁纸软件的功能与体验,满足用户对壁纸应用的更高期待。

上面都是ai帮忙吹的,一句话就是Electron打包体积太大,使用flutter重构。

  1. 使用flutter开发的windows桌面端壁纸软件
  2. 支持收藏、壁纸下载、设为桌面壁纸等功能
  3. 不支持设置锁屏壁纸、不支持视频壁纸(这两个功能没写出来、相应的插件也没没有,没办法实现)
  4. 开源免费、所有api均来自网络
  5. 开源地址:https://gitee.com/zsnoin-can/windows_wallpaper
  6. 该项目仅包含windows,只需注意flutter版本不要低于开发使用的版本,copy过去随便就能跑起来的,不像Android项目,需要处理各种问题。
  7. 软件也内置了安卓下载地址,安卓的以前文章有介绍过,安卓要处理各种问题,要跑起来可能有点麻烦。

2、项目截图

对比其他花里胡哨的,更喜欢这种简约风格的

image.png image.png image.png image.png image.png image.png image.png image.png

3、插件和工具介绍

3.1、主题设置(优化)

主题和国际化以前文章介绍过,这里就不再做过多赘述了(该软件没有做国际化),主要是介绍一下优化点。

  1. 在Windows上面,不设置字体会出现字体大小不一致的情况,默认设置字体为 微软雅黑。
  2. 主题色提取出来,允许用户自定义主题色。
  3. 怎么用,以前文章详细介绍过,和以前一样,感兴趣的可以翻一下以前的文章,这里只快速贴一下优化后的代码。

lib/themes/theme.dart,定义亮色和暗色默认,字体统一设置为 "Microsoft YaHei"。

import 'dart:io';
import 'package:flutter/material.dart';

ThemeData createThemeData(Brightness brightness, Color primaryColor) {
  return ThemeData(
    brightness: brightness,
    colorScheme: brightness == Brightness.light
        ? ColorScheme.light(
            surface: Colors.grey.shade200,
            onSurface: Colors.grey.shade900,
            primary: primaryColor,
            primaryContainer: Colors.white,
            secondary: Colors.grey.shade300,
            inversePrimary: Colors.grey.shade800,
            shadow: const Color.fromARGB(112, 80, 80, 80),
          )
        : ColorScheme.dark(
            surface: Colors.grey.shade900,
            onSurface: Colors.grey.shade100,
            primary: primaryColor,
            primaryContainer: const Color.fromARGB(255, 23, 23, 23),
            secondary: const Color.fromARGB(107, 66, 66, 66),
            inversePrimary: Colors.grey.shade200,
            shadow: const Color.fromARGB(62, 75, 75, 75),
          ),
    fontFamily: Platform.isWindows ? "Microsoft YaHei" : null,
  );
}

ThemeData lightMode = createThemeData(Brightness.light, Colors.blueAccent);
ThemeData darkMode = createThemeData(Brightness.dark, Colors.blueAccent);

lib/themes/theme_provider.dart,各种设置主题的方法,注释已经很详细了。

// ignore_for_file: deprecated_member_use

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wallpaper_app/themes/theme.dart';
import 'dart:async';

class ThemeProvider extends ChangeNotifier {
  ThemeData _themeData = lightMode;
  bool _followSystem = false;
  bool _isInitialized = false;
  bool _isDarkMode = false;
  Color _customPrimaryColor = Colors.blueAccent; // 默认自定义颜色
  VoidCallback? _brightnessListener;

  ThemeData get themeData => _themeData;
  bool get isDarkMode => _isDarkMode;
  bool get followSystem => _followSystem;

  ThemeProvider() {
    _initTheme();
  }

  // 初始化主题(异步)
  Future<void> _initTheme() async {
    if (_isInitialized) return;

    final prefs = await SharedPreferences.getInstance();
    _followSystem = prefs.getBool('followSystem') ?? false;
    _customPrimaryColor =
        Color(prefs.getInt('customPrimaryColor') ?? Colors.blueAccent.value);
    _isDarkMode = prefs.getBool('isDark') ?? false;

    if (_followSystem) {
      _themeData = _getSystemTheme();
    } else {
      final isDark = prefs.getBool('isDark') ?? false;
      _themeData = isDark
          ? createThemeData(Brightness.dark, _customPrimaryColor)
          : createThemeData(Brightness.light, _customPrimaryColor);
    }

    _addSystemThemeListener();
    _isInitialized = true;
    notifyListeners();
  }

  // 系统主题监听
  void _addSystemThemeListener() {
    _brightnessListener = () {
      if (_followSystem) {
        _themeData = _getSystemTheme();
        notifyListeners();
      }
    };

    WidgetsBinding.instance.platformDispatcher.onPlatformBrightnessChanged =
        _brightnessListener;
  }

  // 清理资源
  @override
  void dispose() {
    WidgetsBinding.instance.platformDispatcher.onPlatformBrightnessChanged =
        null;
    super.dispose();
  }

  // 统一保存配置(异步)
  Future<void> _savePreferences() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('isDark', isDarkMode);
    await prefs.setBool('followSystem', _followSystem);
    await prefs.setInt('customPrimaryColor', _customPrimaryColor.value);
  }

  // 切换主题
  void toggleTheme() {
    _followSystem = false;
    _themeData = _isDarkMode
        ? createThemeData(Brightness.light, _customPrimaryColor)
        : createThemeData(Brightness.dark, _customPrimaryColor);
    _isDarkMode = !_isDarkMode;
    _scheduleSavePreferences(); // 异步保存设置
    notifyListeners();
  }

  // 设置浅色主题
  void setLightTheme() {
    _followSystem = false;
    _isDarkMode = false;
    _themeData = createThemeData(Brightness.light, _customPrimaryColor);
    _scheduleSavePreferences(); // 异步保存设置
    notifyListeners();
  }

  // 设置深色主题
  void setDarkTheme() {
    _followSystem = false;
    _isDarkMode = true;
    _themeData = createThemeData(Brightness.dark, _customPrimaryColor);
    _scheduleSavePreferences(); // 异步保存设置
    notifyListeners();
  }

  // 跟随系统主题
  void setFollowSystem() {
    _followSystem = true;
    _themeData = _getSystemTheme();
    _scheduleSavePreferences(); // 异步保存设置
    notifyListeners();
  }

  // 设置自定义主题颜色
  void setCustomTheme(Color primaryColor) {
    if (_followSystem) {
      _customPrimaryColor = primaryColor;
      _themeData = _getSystemTheme();
    } else {
      _customPrimaryColor = primaryColor;
      _themeData = isDarkMode
          ? createThemeData(Brightness.dark, primaryColor)
          : createThemeData(Brightness.light, primaryColor);
    }
    _scheduleSavePreferences(); // 异步保存设置
    notifyListeners();
  }

  // 获取系统主题
  ThemeData _getSystemTheme() {
    final brightness =
        WidgetsBinding.instance.platformDispatcher.platformBrightness;
    return brightness == Brightness.dark
        ? createThemeData(Brightness.dark, _customPrimaryColor)
        : createThemeData(Brightness.light, _customPrimaryColor);
  }

  // 异步保存设置(不会阻塞主线程)
  void _scheduleSavePreferences() {
    Future.microtask(() async {
      await _savePreferences();
    });
  }
}

3.2、图片缓存插件

  1. 缓存管理插件 flutter_cache_manager
  2. 图片缓存插件 cached_network_image。为什么桌面端我使用的是cached_network_image插件而不是 extended_image,因为这两个插件默认缓存文件都是在C盘下面的,但是cached_network_image能更方便的设置缓存管理器,允许自定义缓存目录,对我来说C盘空间还是非常珍贵的,所以选择cached_network_image插件实现缓存。自定义缓存目录,什么时候清理缓存等等你都能自定义,自由度更高一点,我设置的比较简单,缓存文件超过2G,下次打开软件时会自动清理缓存。
实现:先定义一个缓存管理器,lib/tools/custom_image_cache.dart。
import 'dart:io';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:path/path.dart' as path;

class CustomCacheManager extends CacheManager {
  static const key = 'custom-cache-key';
  static final String _cachePath = path.join('D:', 'custom_image_cache');
  static final CustomCacheManager _instance = CustomCacheManager._();

  factory CustomCacheManager() => _instance;

  CustomCacheManager._()
      : super(Config(
          key,
          stalePeriod: const Duration(days: 7),
          maxNrOfCacheObjects: 100,
          fileService: HttpFileService(),
          fileSystem: IOFileSystem(_cachePath),
        ));

  // 直接在构造函数中使用IOFileSystem指定了缓存路径
  // 确保D盘缓存目录存在
  static void ensureCacheDirExists() {
    final cacheDir = Directory(_cachePath);
    if (!cacheDir.existsSync()) {
      cacheDir.createSync(recursive: true);
    }
  }

  static void deleteCacheDir() {
    final cacheDir = Directory(_cachePath);
    if (cacheDir.existsSync()) {
      // 判断目录的大小,超过100M时才删除
      final size = cacheDir.listSync().fold<int>(
            0,
            (previousValue, element) => previousValue + element.statSync().size,
          );
      if (size < 100 * 1024 * 1024) {
        return;
      }
      cacheDir.deleteSync(recursive: true);
    }
  }

  static void clearCache() {
    final cacheDir = Directory(_cachePath);
    if (cacheDir.existsSync()) {
      cacheDir.deleteSync(recursive: true);
    }
  }

  static Future<double> fileSize() {
    final cacheDir = Directory(_cachePath);
    double filesize = 0;
    if (cacheDir.existsSync()) {
      // 判断目录的大小,超过100M时才删除
      int size = cacheDir.listSync().fold<int>(
            0,
            (previousValue, element) => previousValue + element.statSync().size,
          );
      filesize = size / 1024 / 1024;
    } else {
      filesize = 0;
    }
    // 保留两位小数
    filesize = double.parse(filesize.toStringAsFixed(2));
    return Future.value(filesize);
  }
}

使用,lib/components/Images/image_load.dart
  1. 使用前,先调 CustomCacheManager.ensureCacheDirExists() 函数,确保缓存文件夹存在。
  2. 然后把自定义的缓存文件管理器传递给 CachedNetworkImage 即可。
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:wallpaper_app/components/AlertDialog/my_loading.dart';
import 'package:wallpaper_app/tools/custom_image_cache.dart';

class ImageLoad extends StatelessWidget {
  final String imageUrl;
  final BoxFit fit;
  final double size;
  const ImageLoad({
    super.key,
    required this.imageUrl,
    this.fit = BoxFit.cover,
    this.size = 50,
  });

  @override
  Widget build(BuildContext context) {
    // 确保缓存目录存在
    CustomCacheManager.ensureCacheDirExists();
    return CachedNetworkImage(
      imageUrl: imageUrl,
      fit: BoxFit.cover,
      cacheManager: CustomCacheManager(),
      placeholder: (context, url) => Center(
        child: MyLoading(type: 1, size: size),
      ),
      errorWidget: (context, url, error) => Icon(Icons.error),
    );
  }
}

可以检查图片缓存是否在D盘下面custom_image_cache文件夹中出现,同时检查C盘下面的临时缓存文件夹中是否还会继续缓存图片。检查后发现缓存已经移至D盘,不会在C盘下面缓存了,同时缓存功能正常,ok了,大功告成。

3.3、图片查看插件

easy_image_viewer,支持弹窗的模式查看图片,支持双指缩放,支持滚动缩放,支持多图。主要是多端支持,而且能通过函数直接预览,图片预览非常方便。
简单示例,详细用法见官网,我这先从缓存中读取数据在预览。

······
   Tooltip(
       message: '查看',
       child: IconButton(
           icon: Icon(
             Icons.swipe_vertical,
             color: Colors.white,
           ),
           color: Theme.of(context)
               .colorScheme
               .primary,
           onPressed: () async {
             final cacheFile =
                 await CustomCacheManager()
                     .getSingleFile(
               widget
                   .images[activeIndex].largePath,
             );
             if (cacheFile.path.isEmpty) return;
             final imageProvider =
                 Image.file(cacheFile).image;
             showImageViewer(
                 context, imageProvider);
  }))
 ······

3.4、侧边栏插件

flutter_side_menu Flutter的完全可自定义侧边菜单已用作 Related Pages、Navigation Items、Filter side 等的目录。官网教程通俗易懂,直接前往官网查看即可。这里贴一下我自己优化后的侧边栏代码。

左侧布局,lib/components/SideBar/side_menu_nav.dart
// ignore_for_file: camel_case_types
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter_side_menu/flutter_side_menu.dart';
import 'package:package_info_plus/package_info_plus.dart';

class SizeMenuNav extends StatefulWidget {
  final void Function(int index) onTapSide;
  const SizeMenuNav({super.key, required this.onTapSide});

  @override
  State<SizeMenuNav> createState() => _SizeMenuNavState();
}

class sideItem {
  final String title;
  final IconData icon;
  sideItem(this.title, this.icon);
}

class _SizeMenuNavState extends State<SizeMenuNav> {
  int activeIndex = 0;
  double sidebarWidth = 180;
  String version = '';

  void getVersion() async {
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    setState(() {
      version = packageInfo.version;
    });
  }

  @override
  void initState() {
    super.initState();
    getVersion();
  }

  @override
  Widget build(BuildContext context) {
    List<sideItem> sideItems = [
      sideItem('首页', Icons.home),
      sideItem('最新', Icons.new_label),
      sideItem('Loveanimer壁纸', Icons.real_estate_agent),
      sideItem('萌虎壁纸', Icons.support_agent),
      sideItem('ACG壁纸', Icons.discord),
      sideItem('你的名字', Icons.handshake),
      sideItem('JK', Icons.pregnant_woman),
      sideItem('个人中心', Icons.account_box),
    ];

    return SideMenu(
      mode: SideMenuMode.open,
      maxWidth: sidebarWidth,
      backgroundColor: Theme.of(context).colorScheme.primaryContainer,
      hasResizer: true,
      hasResizerToggle: true,
      resizerToggleData: ResizerToggleData(
        iconColor: Theme.of(context).colorScheme.primary,
        opacity: 1,
        iconSize: 25,
      ),
      builder: (data) => SideMenuData(
        header: Column(
          spacing: 10,
          children: [
            SizedBox(height: 30, child: MoveWindow()),
            FittedBox(
              fit: BoxFit.scaleDown,
              child: Padding(
                padding: const EdgeInsets.only(left: 10, right: 10),
                child: Image.asset(
                  'lib/assets/images/icon.png',
                  width: 80,
                  height: 80,
                ),
              ),
            ),
            FittedBox(
              fit: BoxFit.scaleDown,
              child: Padding(
                padding: const EdgeInsets.only(left: 10, right: 10),
                child: Text(
                  '波奇壁纸',
                  style: TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
            ),
            // 分割线
            const Divider(
              color: Colors.grey,
              height: 10,
              thickness: 1,
              indent: 10,
              endIndent: 10,
            ),
          ],
        ),
        items: [
          for (var item in sideItems)
            SideMenuItemDataTile(
              hasSelectedLine: true,
              borderRadius: BorderRadius.all(Radius.circular(5)),
              isSelected: activeIndex == sideItems.indexOf(item),
              tooltip:
                  data.currentWidth <= data.minWidth + 30 ? item.title : null,
              titleStyle: TextStyle(
                fontSize: 15,
              ),
              selectedTitleStyle: TextStyle(
                fontSize: 15,
                color: Theme.of(context).colorScheme.primary,
              ),
              onTap: () {
                setState(() {
                  activeIndex = sideItems.indexOf(item);
                });
                widget.onTapSide(sideItems.indexOf(item));
              },
              title: item.title,
              icon: Icon(item.icon),
            ),
        ],
        footer: FittedBox(
          fit: BoxFit.scaleDown,
          child: Padding(
            padding: const EdgeInsets.all(5),
            child: Text(
              'v$version',
              style: TextStyle(
                fontSize: 13,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

其它功能就没啥难点了,可自行查看。

使用,和官网用法一致即可,lib/pages/main_page.dart
import 'package:flutter/material.dart';
import 'package:wallpaper_app/pages/4k/image_list_new_360.dart';
import 'package:wallpaper_app/pages/setting/personal_center.dart';
import 'package:wallpaper_app/tools/custom_image_cache.dart';
import 'package:wallpaper_app/components/SideBar/side_menu_nav.dart';
import 'package:wallpaper_app/components/windows/window_title_bar.dart';
import 'package:wallpaper_app/pages/home_page.dart';
import 'package:wallpaper_app/pages/mohu/mohu_page.dart';
import 'package:wallpaper_app/pages/suyan/random_image_pc.dart';
import 'package:wallpaper_app/pages/tuhui/acg_list.dart';
import 'package:wallpaper_app/pages/tuhui/jk_list.dart';
import 'package:wallpaper_app/pages/tuhui/your_name.dart';
import 'package:wallpaper_app/tools/update_apk.dart';

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  PageController pageController = PageController();
  void cacheCleaning() async {
    double size = await CustomCacheManager.fileSize();
    // 大于2G 清除缓存
    if (size > 2048) {
      CustomCacheManager.deleteCacheDir();
    }
  }

  @override
  void initState() {
    super.initState();
    cacheCleaning();
    UpdateApk().updateApk(context);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: SizedBox(
      width: double.infinity,
      height: double.infinity,
      child: Row(
        children: [
          // 侧边栏
          // SidebarPage(onTapSide: (index) {
          //   pageController.jumpToPage(index);
          // }),
          SizeMenuNav(onTapSide: (index) {
            pageController.jumpToPage(index);
          }),
          Expanded(
              child: Column(
            children: [
              WindowTitleBar(),
              Expanded(
                child: PageView(
                  controller: pageController,
                  children: [
                    HomePage(),
                    ImageListNew360(),
                    RandomImagePc(),
                    MohuPage(),
                    AcgList(zd: 'pc'),
                    YourName(),
                    JkList(zd: 'pc'),
                    PersonalCenter(),
                  ],
                ),
              )
            ],
          ))
        ],
      ),
    ));
  }
}

最终效果
image.png image.png

4、壁纸设置功能

4.1 大致设置流程如下:

graph LR
点击设置按钮 --> 读取临时缓存中的文件 --> 保存到本地,防止被清理 --> 调用函数设置壁纸

4.2 读取临时缓存中的图片:

  • CustomCacheManager() 就是 3.2 自定义的缓存管理器,继承自 CacheManager
  • 具体用法见 lib/components/Images/image_view.dart
final cacheFile = await CustomCacheManager().getSingleFile(url);
print(cacheFile.path);

4.3 把临时文件保存到本地

  • lib/tools/save_file.dart
  • saveImageFile()图片保存到本地,saveFileWallPaper()设置壁纸时调用,功能一模一样,只有命名方式不同,用于标记当前桌面壁纸。
  • 保存成功后会返回图片新地址,传递给 4.4 的设置壁纸函数即可。
import 'dart:io';

class SaveFile {
  // 保存文件本地文件复制到 D盘 BoQiDown 文件夹
  static Future<String> saveImageFile(String path) async {
    try {
      // 确保 D盘 BoQiDown 文件夹存在
      Directory directory = Directory('D:\\BoQiDown\\image');
      if (!directory.existsSync()) {
        directory.createSync();
      }
      // 复制文件到 D盘 BoQiDown 文件夹
      File file = File(path);
      String newPath = 'D:\\BoQiDown\\image\\${file.path.split('\\').last}';
      await file.copy(newPath);
      return newPath; // 返回新的路径
    } catch (e) {
      throw Exception('保存文件失败: $e');
    }
  }


  static Future<String> saveFileWallPaper(String path) async {
    try {
      // 确保 D盘 BoQiDown 文件夹存在
      Directory directory = Directory('D:\\BoQiDown\\image');
      if (!directory.existsSync()) {
        directory.createSync();
      }
      // 复制文件到 D盘 BoQiDown 文件夹
      File file = File(path);
      String newPath = 'D:\\BoQiDown\\image\\desktop.jpg';
      await file.copy(newPath);
      return newPath; // 返回新的路径
    } catch (e) {
      throw Exception('保存文件失败: $e');
    }
  }
}

4.4 设置壁纸

  • lib/tools/wallpaper_service.dart
  • 设置壁纸功能会阻塞主线程,导致动画等卡顿,可以把设置壁纸这个动作放到独立线程 Isolate 中运行,在需要的地方监听 Isolate 即可。
  • 同样的具体用法见 lib/components/Images/image_view.dart
  • 设置壁纸功能是AI写的,我只是单纯扩展来一些额外功能。
// ignore_for_file: unused_local_variable, constant_identifier_names, depend_on_referenced_packages

import 'dart:async';
import 'dart:isolate';
import 'dart:io';
import 'dart:ui';
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';

const int SPIF_UPDATEINIFILE = 0x01;
const int SPIF_SENDCHANGE = 0x02;

class WallpaperService {
  // 设置Windows桌面壁纸
  // [imagePath] 图片的完整路径
  static Future<bool> setWallpaperInIsolate(String imagePath) async {
    final receivePort = ReceivePort();
    Isolate.spawn(_setWallpaper, imagePath).then((isolate) {
      return true;
    }).catchError((error) {
      return false;
    });
    return true;
  }

  static void _setWallpaper(String imagePath) {
    try {
      final file = File(imagePath);
      if (!file.existsSync()) {
        return;
      }

      final pathPointer = imagePath.toNativeUtf16();

      final result = SystemParametersInfo(
        SPI_SETDESKWALLPAPER,
        0,
        pathPointer,
        SPIF_UPDATEINIFILE | SPIF_SENDCHANGE,
      );

      free(pathPointer);

      if (result != 0) {
        // 通过 SendPort 发送消息到主 UI 线程
        final sendPort = IsolateNameServer.lookupPortByName('mainPort');
        sendPort?.send({'code': 200, 'message': '设置成功'});
      } else {
        final sendPort = IsolateNameServer.lookupPortByName('mainPort');
        sendPort?.send({'code': 400, 'message': '设置失败'});
      }
    } catch (e) {
      final sendPort = IsolateNameServer.lookupPortByName('mainPort');
      sendPort?.send({'code': 400, 'message': '设置失败: $e'});
    }
  }
}

5、地址

  1. 开源地址:https://gitee.com/zsnoin-can/windows_wallpaper
  2. 安装包地址:https://gitee.com/zsnoin-can/desktop_exe