Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions lib/domain/dashboard_preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class DashboardPreferences {
class DashboardPreferencesRepository {
static const _keyRangeMode = 'ringotrack.dashboard.heatmapRangeMode';
static const _keyGlassEffect = 'ringotrack.dashboard.enableGlassEffect';
// Windows 平台:待生效的玻璃效果设置,下次启动时应用
static const _keyGlassEffectPending = 'ringotrack.dashboard.enableGlassEffect.pending';

static const _keyWeekStartMode = 'ringotrack.dashboard.weekStartMode';

Future<DashboardPreferences> load() async {
Expand Down Expand Up @@ -74,4 +77,34 @@ class DashboardPreferencesRepository {
await sp.setBool(_keyGlassEffect, prefs.enableGlassEffect);
await sp.setString(_keyWeekStartMode, prefs.weekStartMode.name);
}

/// Windows 平台:设置待生效的玻璃效果值(下次启动时应用)
Future<void> setGlassEffectPending(bool enabled) async {
final sp = await SharedPreferences.getInstance();
await sp.setBool(_keyGlassEffectPending, enabled);
}

/// Windows 平台:获取待生效的玻璃效果值
/// 返回 null 表示没有待生效的设置
Future<bool?> getGlassEffectPending() async {
final sp = await SharedPreferences.getInstance();
if (!sp.containsKey(_keyGlassEffectPending)) return null;
return sp.getBool(_keyGlassEffectPending);
}

/// Windows 平台:启动时调用,将 pending 值应用到实际设置
/// 返回最终的 enableGlassEffect 值
Future<bool> applyPendingGlassEffect() async {
final sp = await SharedPreferences.getInstance();
final pending = sp.getBool(_keyGlassEffectPending);
if (pending != null) {
// 将 pending 值应用到实际设置
await sp.setBool(_keyGlassEffect, pending);
// 清除 pending
await sp.remove(_keyGlassEffectPending);
return pending;
}
// 没有 pending,返回当前值
return sp.getBool(_keyGlassEffect) ?? false;
}
}
22 changes: 22 additions & 0 deletions lib/domain/dashboard_preferences_controller.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:io' show Platform;

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ringotrack/domain/dashboard_preferences.dart';

Expand All @@ -20,6 +22,16 @@ class DashboardPreferencesController

Future<void> setEnableGlassEffect(bool enabled) async {
final current = state.value ?? const DashboardPreferences();

// Windows 平台:设置 pending 值,下次启动时生效。
// UI 状态不变,避免背景立即变透明但玻璃效果还没启用。
if (Platform.isWindows) {
await _repository.setGlassEffectPending(enabled);
// 不更新 state,让 UI 保持当前状态
return;
}

// macOS:直接更新,可以实时切换
final next = current.copyWith(enableGlassEffect: enabled);
state = AsyncData(next);
await _repository.save(next);
Expand All @@ -42,3 +54,13 @@ final dashboardPreferencesControllerProvider =
AsyncNotifierProvider<DashboardPreferencesController, DashboardPreferences>(
DashboardPreferencesController.new,
);

/// Windows 平台:获取玻璃效果的显示值(pending 值优先,否则用当前值)
/// 用于 UI 显示,让用户看到他们设置的值
final windowsGlassEffectDisplayProvider = FutureProvider<bool>((ref) async {
final repo = ref.watch(dashboardPreferencesRepositoryProvider);
final pending = await repo.getGlassEffectPending();
if (pending != null) return pending;
final prefs = await repo.load();
return prefs.enableGlassEffect;
});
22 changes: 21 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
import 'dart:io' show Platform;

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ringotrack/app.dart';
import 'package:ringotrack/domain/dashboard_preferences.dart';
import 'package:ringotrack/platform/glass_tint_controller.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();

// Windows 平台:在启动时一次性决定是否启用玻璃效果。
// 由于 Windows 上玻璃效果无法在运行时优雅地关闭,
// 所以仅在启动时根据用户偏好来决定是否启用。
if (Platform.isWindows) {
final repo = DashboardPreferencesRepository();
// 先应用 pending 值(如果有的话),这样 UI 层读取到的是最新的设置
final enableGlass = await repo.applyPendingGlassEffect();
if (enableGlass) {
await GlassTintController.instance.enableGlass();
}
// 如果 enableGlassEffect 为 false,则完全不启用玻璃效果,
// 窗口将保持默认的不透明背景。
}

void main() {
runApp(const ProviderScope(child: RingoTrackApp()));
}
35 changes: 19 additions & 16 deletions lib/pages/clock_page.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:io' show Platform;
import 'dart:math' as math;

import 'package:flutter/material.dart';
Expand Down Expand Up @@ -27,7 +28,7 @@ class _ClockPageState extends ConsumerState<ClockPage> {

@override
void dispose() {
// 离开页面时恢复默认白色 tint
// 离开页面时恢复默认白色 tint(macOS/Windows 都支持更新 tint 颜色)
GlassTintController.instance.resetTintColor();
super.dispose();
}
Expand Down Expand Up @@ -93,22 +94,24 @@ class _ClockPageState extends ConsumerState<ClockPage> {
final pinSupported = WindowPinController.instance.isSupported;
final useGlass = ref.watch(useGlassEffectProvider);

// 监听毛玻璃设置变化
ref.listen(useGlassEffectProvider, (previous, next) {
if (mounted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (next) {
// 开启毛玻璃:设置彩色 tint
_updateGlassTint();
} else {
// 关闭毛玻璃:恢复默认白色 tint
GlassTintController.instance.resetTintColor();
}
});
}
});
// 监听毛玻璃设置变化(仅 macOS,Windows 不支持实时开/关)
if (Platform.isMacOS) {
ref.listen(useGlassEffectProvider, (previous, next) {
if (mounted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (next) {
// 开启毛玻璃:设置彩色 tint
_updateGlassTint();
} else {
// 关闭毛玻璃:恢复默认白色 tint
GlassTintController.instance.resetTintColor();
}
});
}
});
}

// 监听主题变化,更新 tint(仅在毛玻璃模式下)
// 监听主题变化,更新 tint(仅在毛玻璃模式下,macOS/Windows 都支持
ref.listen(appThemeProvider, (previous, next) {
if (useGlass && mounted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Expand Down
1 change: 1 addition & 0 deletions lib/pages/dashboard_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class _DashboardPageState extends ConsumerState<DashboardPage> {
void initState() {
super.initState();
// 确保恢复默认白色 tint(从 ClockPage 返回时)
// macOS/Windows 都支持更新 tint 颜色
WidgetsBinding.instance.addPostFrameCallback((_) {
GlassTintController.instance.resetTintColor();
});
Expand Down
75 changes: 53 additions & 22 deletions lib/pages/settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
void initState() {
super.initState();
// 确保恢复默认白色 tint(从 ClockPage 返回时)
// macOS/Windows 都支持更新 tint 颜色
WidgetsBinding.instance.addPostFrameCallback((_) {
GlassTintController.instance.resetTintColor();
});
Expand Down Expand Up @@ -1040,11 +1041,6 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
}

Widget _appearanceSection(ThemeData theme) {
final dashboardPrefsAsync = ref.watch(
dashboardPreferencesControllerProvider,
);
final enableGlass = dashboardPrefsAsync.value?.enableGlassEffect ?? true;

final tiles = <Widget>[
_dataTile(
theme,
Expand All @@ -1055,19 +1051,54 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
];

if (Platform.isMacOS || Platform.isWindows) {
tiles.add(
_dataTile(
theme,
title: '毛玻璃效果(实验性)',
helper: '启用后窗口背景将呈现半透明模糊效果。',
child: Row(
tiles.add(_buildGlassEffectTile(theme));
}

return _sectionCard(
theme,
title: '外观',
icon: Icons.palette_outlined,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _withDividers(tiles, theme),
),
);
}

Widget _buildGlassEffectTile(ThemeData theme) {
// Windows 平台:显示 pending 值(用户设置的值,待重启生效)
// macOS 平台:显示当前生效的值
final bool enableGlass;
if (Platform.isWindows) {
final displayAsync = ref.watch(windowsGlassEffectDisplayProvider);
enableGlass = displayAsync.value ?? false;
} else {
final dashboardPrefsAsync = ref.watch(
dashboardPreferencesControllerProvider,
);
enableGlass = dashboardPrefsAsync.value?.enableGlassEffect ?? false;
}

return _dataTile(
theme,
title: '毛玻璃效果(实验性)',
helper: '启用后窗口背景将呈现半透明模糊效果。',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Switch(
value: enableGlass,
onChanged: (value) {
ref
.read(dashboardPreferencesControllerProvider.notifier)
.setEnableGlassEffect(value);
// Windows 平台:刷新 display provider 并提示用户需要重启
if (Platform.isWindows) {
ref.invalidate(windowsGlassEffectDisplayProvider);
_showSnack('设置已保存,重启应用后生效');
}
},
),
SizedBox(width: 8.w),
Expand All @@ -1077,17 +1108,17 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
),
],
),
),
);
}

return _sectionCard(
theme,
title: '外观',
icon: Icons.palette_outlined,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _withDividers(tiles, theme),
// Windows 平台显示重启提示
if (Platform.isWindows) ...[
SizedBox(height: 6.h),
Text(
'⚠️ 需要重启应用才能生效',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
],
),
);
}
Expand Down
Loading