Files
immich/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart
T
Santo Shakil 686da0656d fix(mobile): only toggle backup from the switch, not the whole row
tapping anywhere on the enable backup row flipped backup on or off, so it was easy to toggle by accident. now only the switch does it.
2026-06-21 00:29:28 +06:00

168 lines
6.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
class BackupToggleButton extends ConsumerStatefulWidget {
final VoidCallback onStart;
final VoidCallback onStop;
const BackupToggleButton({super.key, required this.onStart, required this.onStop});
@override
ConsumerState<BackupToggleButton> createState() => BackupToggleButtonState();
}
class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _gradientAnimation;
bool _isEnabled = false;
@override
void initState() {
super.initState();
_animationController = AnimationController(duration: const Duration(seconds: 8), vsync: this);
_gradientAnimation = Tween<double>(
begin: 0,
end: 1,
).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut));
_isEnabled = ref.read(appConfigProvider).backup.enabled;
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
Future<void> _onToggle(bool value) async {
await ref.read(settingsProvider).write(.backupEnabled, value);
setState(() {
_isEnabled = value;
});
if (value) {
widget.onStart.call();
} else {
widget.onStop.call();
}
}
@override
Widget build(BuildContext context) {
final uploadTasks = ref.watch(driftBackupProvider.select((state) => state.uploadItems));
final isSyncing = ref.watch(driftBackupProvider.select((state) => state.isSyncing));
final iCloudProgress = ref.watch(driftBackupProvider.select((state) => state.iCloudDownloadProgress));
final errorCount = ref.watch(driftBackupProvider.select((state) => state.errorCount));
final isProcessing = uploadTasks.isNotEmpty || isSyncing || iCloudProgress.isNotEmpty;
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
final gradientColors = [
Color.lerp(
context.primaryColor.withValues(alpha: 0.5),
context.primaryColor.withValues(alpha: 0.3),
_gradientAnimation.value,
)!,
Color.lerp(
context.primaryColor.withValues(alpha: 0.2),
context.primaryColor.withValues(alpha: 0.4),
_gradientAnimation.value,
)!,
Color.lerp(
context.primaryColor.withValues(alpha: 0.3),
context.primaryColor.withValues(alpha: 0.5),
_gradientAnimation.value,
)!,
];
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(20)),
gradient: LinearGradient(
colors: gradientColors,
stops: const [0.0, 0.5, 1.0],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(color: context.primaryColor.withValues(alpha: 0.1), blurRadius: 12, offset: const Offset(0, 2)),
],
),
child: Container(
margin: const EdgeInsets.all(1.5),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(18.5)),
color: context.colorScheme.surfaceContainerLow,
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
context.primaryColor.withValues(alpha: 0.2),
context.primaryColor.withValues(alpha: 0.1),
],
),
),
child: isProcessing
? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2))
: Icon(Icons.cloud_upload_outlined, color: context.primaryColor, size: 24),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(
child: Text(
"enable_backup".t(context: context),
style: context.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
color: context.primaryColor,
),
),
),
],
),
if (errorCount > 0)
Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
"upload_error_with_count".t(context: context, args: {'count': '$errorCount'}),
style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.error),
),
),
],
),
),
Switch.adaptive(value: _isEnabled, onChanged: (value) => _onToggle(value)),
],
),
),
),
);
},
);
}
}