198 lines
5.9 KiB
Dart
198 lines
5.9 KiB
Dart
/// 时间: 2026-04-02
|
|
/// 功能: 主页设置和控制组件
|
|
/// 介绍: 包含收起/恢复悬浮按钮的管理器和组件
|
|
/// 最新变化: 2026-04-02 初始创建
|
|
|
|
import 'dart:async';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import '../../../../constants/app_constants.dart';
|
|
|
|
/// 悬浮按钮收起管理器
|
|
class FloatingButtonsVisibilityManager {
|
|
static const String _key = 'floating_buttons_visible';
|
|
|
|
static FloatingButtonsVisibilityManager? _instance;
|
|
bool _isVisible = true;
|
|
bool _isFlashing = false;
|
|
int _flashCount = 0;
|
|
final ValueNotifier<bool> _visibleNotifier = ValueNotifier<bool>(true);
|
|
final ValueNotifier<bool> _flashingNotifier = ValueNotifier<bool>(false);
|
|
Timer? _hideTimer;
|
|
Timer? _flashTimer;
|
|
|
|
FloatingButtonsVisibilityManager._internal();
|
|
|
|
factory FloatingButtonsVisibilityManager() {
|
|
_instance ??= FloatingButtonsVisibilityManager._internal();
|
|
return _instance!;
|
|
}
|
|
|
|
Future<void> init() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final visible = prefs.getBool(_key) ?? true;
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_isVisible = visible;
|
|
_visibleNotifier.value = visible;
|
|
});
|
|
}
|
|
|
|
bool get isVisible => _isVisible;
|
|
bool get isFlashing => _isFlashing;
|
|
ValueNotifier<bool> get visibleNotifier => _visibleNotifier;
|
|
ValueNotifier<bool> get flashingNotifier => _flashingNotifier;
|
|
|
|
Future<void> toggle() async {
|
|
if (_isFlashing) {
|
|
await _restoreFromFlashing();
|
|
return;
|
|
}
|
|
|
|
_isVisible = !_isVisible;
|
|
_visibleNotifier.value = _isVisible;
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setBool(_key, _isVisible);
|
|
|
|
if (!_isVisible) {
|
|
_startHideTimer();
|
|
} else {
|
|
_cancelAllTimers();
|
|
}
|
|
}
|
|
|
|
Future<void> _restoreFromFlashing() async {
|
|
_cancelAllTimers();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_isFlashing = false;
|
|
_flashingNotifier.value = false;
|
|
_isVisible = true;
|
|
_visibleNotifier.value = true;
|
|
});
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setBool(_key, true);
|
|
}
|
|
|
|
void _startHideTimer() {
|
|
_cancelAllTimers();
|
|
_hideTimer = Timer(const Duration(seconds: 5), () {
|
|
_startFlashing();
|
|
});
|
|
}
|
|
|
|
void _startFlashing() {
|
|
_isFlashing = true;
|
|
_flashingNotifier.value = true;
|
|
_flashCount = 0;
|
|
_flashTimer = Timer.periodic(const Duration(seconds: 1), (timer) async {
|
|
_flashCount++;
|
|
_flashingNotifier.value = !_flashingNotifier.value;
|
|
|
|
if (_flashCount >= 6) {
|
|
timer.cancel();
|
|
_isFlashing = false;
|
|
_flashingNotifier.value = false;
|
|
_isVisible = false;
|
|
_visibleNotifier.value = false;
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setBool(_key, false);
|
|
}
|
|
});
|
|
}
|
|
|
|
void _cancelAllTimers() {
|
|
if (_hideTimer != null) {
|
|
_hideTimer!.cancel();
|
|
_hideTimer = null;
|
|
}
|
|
if (_flashTimer != null) {
|
|
_flashTimer!.cancel();
|
|
_flashTimer = null;
|
|
}
|
|
}
|
|
|
|
void dispose() {
|
|
_cancelAllTimers();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_visibleNotifier.value = true;
|
|
_flashingNotifier.value = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
/// 收起/恢复悬浮按钮组件
|
|
class FloatingButtonsToggleButton extends StatelessWidget {
|
|
final FloatingButtonsVisibilityManager manager;
|
|
final bool isDark;
|
|
|
|
const FloatingButtonsToggleButton({
|
|
super.key,
|
|
required this.manager,
|
|
required this.isDark,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ValueListenableBuilder<bool>(
|
|
valueListenable: manager.flashingNotifier,
|
|
builder: (context, isFlashing, child) {
|
|
return ValueListenableBuilder<bool>(
|
|
valueListenable: manager.visibleNotifier,
|
|
builder: (context, isVisible, child) {
|
|
final shouldShow = isFlashing ? !isFlashing : isVisible;
|
|
|
|
return SizedBox(
|
|
width: 44,
|
|
height: 44,
|
|
child: shouldShow || isFlashing
|
|
? Container(
|
|
decoration: BoxDecoration(
|
|
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
|
|
shape: BoxShape.circle,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withAlpha(isDark ? 40 : 20),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
borderRadius: BorderRadius.circular(22),
|
|
onTap: () {
|
|
manager.toggle();
|
|
},
|
|
child: Center(
|
|
child: Icon(
|
|
isFlashing
|
|
? Icons.visibility
|
|
: Icons.visibility_off,
|
|
color: isFlashing
|
|
? AppConstants.primaryColor
|
|
: (isDark
|
|
? Colors.grey[300]
|
|
: AppConstants.primaryColor),
|
|
size: 24,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
)
|
|
: Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
borderRadius: BorderRadius.circular(22),
|
|
onTap: () {
|
|
manager.toggle();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|