♻️ Replace bitsdojo_window with window_manager

This commit is contained in:
2025-09-26 23:26:40 +08:00
parent affba29c04
commit e79f857feb
15 changed files with 216 additions and 155 deletions

View File

@@ -8,7 +8,6 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -17,7 +16,7 @@ import 'package:island/firebase_options.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/pods/theme.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:island/pods/userinfo.dart';
import 'package:island/pods/websocket.dart';
import 'package:island/route.dart';
@@ -30,6 +29,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:window_manager/window_manager.dart';
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
@@ -89,31 +89,39 @@ void main() async {
final prefs = await SharedPreferences.getInstance();
if (!kIsWeb && (Platform.isMacOS || Platform.isLinux || Platform.isWindows)) {
doWhenWindowReady(() {
const defaultSize = Size(360, 640);
await windowManager.ensureInitialized();
// Get saved window size from preferences
final savedSizeString = prefs.getString(kAppWindowSize);
Size initialSize = defaultSize;
const defaultSize = Size(360, 640);
if (savedSizeString != null) {
try {
final parts = savedSizeString.split(',');
if (parts.length == 2) {
final width = double.parse(parts[0]);
final height = double.parse(parts[1]);
initialSize = Size(width, height);
}
} catch (e) {
log("[SplashScreen] Failed to parse saved window size: $e");
initialSize = defaultSize;
// Get saved window size from preferences
final savedSizeString = prefs.getString(kAppWindowSize);
Size initialSize = defaultSize;
if (savedSizeString != null) {
try {
final parts = savedSizeString.split(',');
if (parts.length == 2) {
final width = double.parse(parts[0]);
final height = double.parse(parts[1]);
initialSize = Size(width, height);
}
} catch (e) {
log("[SplashScreen] Failed to parse saved window size: $e");
initialSize = defaultSize;
}
}
appWindow.minSize = defaultSize;
appWindow.size = initialSize;
appWindow.alignment = Alignment.center;
appWindow.show();
WindowOptions windowOptions = WindowOptions(
size: initialSize,
center: true,
backgroundColor: Colors.transparent,
skipTaskbar: false,
titleBarStyle: TitleBarStyle.hidden,
);
windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.setMinimumSize(defaultSize);
await windowManager.show();
await windowManager.focus();
log(
"[SplashScreen] Desktop window is ready with size: ${initialSize.width}x${initialSize.height}",
);

View File

@@ -1,8 +1,7 @@
import 'dart:io';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/foundation.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
class TrayService {
TrayService._();
@@ -48,15 +47,10 @@ class TrayService {
void handleAction(MenuItem item) {
switch (item.key) {
case 'show_window':
() async {
appWindow.show();
appWindow.restore();
await Future.delayed(const Duration(milliseconds: 32));
appWindow.show();
}();
windowManager.show();
break;
case 'exit_app':
appWindow.close();
windowManager.destroy();
break;
}
}

View File

@@ -1,6 +1,5 @@
import 'dart:io';
import 'dart:ui';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -15,6 +14,7 @@ import 'package:island/services/responsive.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:path_provider/path_provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:window_manager/window_manager.dart';
class AppScrollBehavior extends MaterialScrollBehavior {
@override
@@ -36,11 +36,12 @@ class WindowScaffold extends HookConsumerWidget {
if (!kIsWeb &&
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
void saveWindowSize() {
final size = appWindow.size;
final settingsNotifier = ref.read(
appSettingsNotifierProvider.notifier,
);
settingsNotifier.setWindowSize(size);
windowManager.getBounds().then((bounds) {
final settingsNotifier = ref.read(
appSettingsNotifierProvider.notifier,
);
settingsNotifier.setWindowSize(bounds.size);
});
}
// Save window size when app is about to close
@@ -61,13 +62,6 @@ class WindowScaffold extends HookConsumerWidget {
if (!kIsWeb &&
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
final windowButtonColor = WindowButtonColors(
iconNormal: Theme.of(context).colorScheme.primary,
mouseOver: Theme.of(context).colorScheme.primaryContainer,
mouseDown: Theme.of(context).colorScheme.onPrimaryContainer,
iconMouseOver: Theme.of(context).colorScheme.primary,
iconMouseDown: Theme.of(context).colorScheme.primary,
);
return Material(
child: Stack(
@@ -75,44 +69,66 @@ class WindowScaffold extends HookConsumerWidget {
children: [
Column(
children: [
WindowTitleBarBox(
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).dividerColor,
width: 1 / devicePixelRatio,
),
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).dividerColor,
width: 1 / devicePixelRatio,
),
),
child: MoveWindow(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment:
Platform.isMacOS
? MainAxisAlignment.center
: MainAxisAlignment.start,
children: [
Expanded(
child: Text(
'Solar Network',
textAlign:
Platform.isMacOS
? TextAlign.center
: TextAlign.start,
).padding(horizontal: 12, vertical: 5),
),
child: DragToMoveArea(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment:
Platform.isMacOS
? MainAxisAlignment.center
: MainAxisAlignment.start,
children: [
Expanded(
child: Text(
'Solar Network',
textAlign:
Platform.isMacOS
? TextAlign.center
: TextAlign.start,
).padding(horizontal: 12, vertical: 5),
),
if (!Platform.isMacOS)
IconButton(
icon: Icon(Symbols.minimize),
onPressed: () => windowManager.minimize(),
iconSize: 16,
padding: EdgeInsets.all(8),
constraints: BoxConstraints(),
color: Theme.of(context).iconTheme.color,
),
if (!Platform.isMacOS)
MinimizeWindowButton(colors: windowButtonColor),
if (!Platform.isMacOS)
MaximizeWindowButton(colors: windowButtonColor),
if (!Platform.isMacOS)
CloseWindowButton(
colors: windowButtonColor,
onPressed: () => appWindow.hide(),
),
],
),
if (!Platform.isMacOS)
IconButton(
icon: Icon(Symbols.maximize),
onPressed: () async {
if (await windowManager.isMaximized()) {
windowManager.restore();
} else {
windowManager.maximize();
}
},
iconSize: 16,
padding: EdgeInsets.all(8),
constraints: BoxConstraints(),
color: Theme.of(context).iconTheme.color,
),
if (!Platform.isMacOS)
IconButton(
icon: Icon(Symbols.close),
onPressed: () => windowManager.close(),
iconSize: 16,
padding: EdgeInsets.all(8),
constraints: BoxConstraints(),
color: Theme.of(context).iconTheme.color,
),
],
),
),
),

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -12,6 +11,7 @@ import 'package:island/services/update_service.dart';
import 'package:island/widgets/content/network_status_sheet.dart';
import 'package:island/widgets/tour/tour.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
class AppWrapper extends HookConsumerWidget with TrayListener {
final Widget child;
@@ -67,11 +67,7 @@ class AppWrapper extends HookConsumerWidget with TrayListener {
}
void _trayIconPrimaryAction() {
if (appWindow.isVisible) {
appWindow.restore();
} else {
appWindow.show();
}
windowManager.show();
}
void _trayIconSecondaryAction() {