feat: checkpoint API migration and dzikir UX updates

This commit is contained in:
Dwindi Ramadhana
2026-03-16 00:30:32 +07:00
parent c4696f2d9f
commit a049129a35
85 changed files with 4285 additions and 211 deletions

View File

@@ -244,6 +244,70 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
),
const SizedBox(height: 24),
// ── DZIKIR DISPLAY ──
_sectionLabel('TAMPILAN DZIKIR'),
const SizedBox(height: 12),
_buildSegmentSettingCard(
isDark,
title: 'Mode Tampilan Dzikir',
subtitle: 'Pilih daftar baris atau fokus per slide',
value: _settings.dzikirDisplayMode,
options: const {
'list': 'Daftar (Baris)',
'focus': 'Fokus (Slide)',
},
onChanged: (value) {
_settings.dzikirDisplayMode = value;
_saveSettings();
},
),
if (_settings.dzikirDisplayMode == 'focus') ...[
const SizedBox(height: 10),
_buildSegmentSettingCard(
isDark,
title: 'Posisi Tombol Hitung',
subtitle: 'Atur posisi tombol pada mode fokus',
value: _settings.dzikirCounterButtonPosition,
options: const {
'bottomPill': 'Pill Bawah',
'fabCircle': 'Bulat Kanan Bawah',
},
onChanged: (value) {
_settings.dzikirCounterButtonPosition = value;
_saveSettings();
},
),
const SizedBox(height: 10),
_settingRow(
isDark,
icon: LucideIcons.arrowRight,
iconColor: const Color(0xFF00B894),
title: 'Lanjut Otomatis Saat Target Tercapai',
trailing: IosToggle(
value: _settings.dzikirAutoAdvance,
onChanged: (v) {
_settings.dzikirAutoAdvance = v;
_saveSettings();
},
),
),
],
const SizedBox(height: 10),
_settingRow(
isDark,
icon: LucideIcons.vibrate,
iconColor: const Color(0xFF6C5CE7),
title: 'Getaran Saat Hitung',
trailing: IosToggle(
value: _settings.dzikirHapticOnCount,
onChanged: (v) {
_settings.dzikirHapticOnCount = v;
_saveSettings();
},
),
),
const SizedBox(height: 24),
// ── PRAYER SETTINGS ──
_sectionLabel('WAKTU SHOLAT'),
const SizedBox(height: 12),
@@ -438,6 +502,103 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
);
}
Widget _buildSegmentSettingCard(
bool isDark, {
required String title,
String? subtitle,
required String value,
required Map<String, String> options,
required ValueChanged<String> onChanged,
}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isDark
? AppColors.primary.withValues(alpha: 0.08)
: AppColors.cream,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
if (subtitle != null) ...[
const SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: isDark
? AppColors.textSecondaryDark
: AppColors.textSecondaryLight,
),
),
],
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: isDark
? AppColors.backgroundDark
: AppColors.backgroundLight,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isDark
? AppColors.primary.withValues(alpha: 0.08)
: AppColors.cream,
),
),
child: Row(
children: options.entries.map((entry) {
final selected = value == entry.key;
return Expanded(
child: GestureDetector(
onTap: () => onChanged(entry.key),
child: AnimatedContainer(
duration: const Duration(milliseconds: 160),
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 10,
),
decoration: BoxDecoration(
color: selected
? AppColors.primary
: Colors.transparent,
borderRadius: BorderRadius.circular(10),
),
child: Text(
entry.value,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: selected
? AppColors.onPrimary
: (isDark
? AppColors.textPrimaryDark
: AppColors.textPrimaryLight),
),
),
),
),
);
}).toList(),
),
),
],
),
);
}
void _showMethodDialog(BuildContext context) {
showDialog(
context: context,