iT邦幫忙

2025 iThome 鐵人賽

DAY 25
0

這是一個新的頁面,所以和之前一樣,從 use case 開始建立

AI 寫扣經過

  1. 資料層建立 UserPreferencesRepository,用來記錄震動和聲音開關
    @Singleton
    class UserPreferencesRepository @Inject constructor(
        @ApplicationContext private val context: Context
    ) {
        private object Keys {
            val isSoundEnabled = booleanPreferencesKey("is_sound_enabled")
            val isVibrationEnabled = booleanPreferencesKey("is_vibration_enabled")
        }
    
        val isSoundEnabled: Flow<Boolean> = context.dataStore.data
            .map { preferences ->
                preferences[Keys.isSoundEnabled] ?: true // Default to true
            }
    
        val isVibrationEnabled: Flow<Boolean> = context.dataStore.data
            .map { preferences ->
                preferences[Keys.isVibrationEnabled] ?: true // Default to true
            }
    
        suspend fun setSoundEnabled(isEnabled: Boolean) {
            context.dataStore.edit { preferences ->
                preferences[Keys.isSoundEnabled] = isEnabled
            }
        }
    
        suspend fun setVibrationEnabled(isEnabled: Boolean) {
            context.dataStore.edit { preferences ->
                preferences[Keys.isVibrationEnabled] = isEnabled
            }
        }
    }
    
  2. 領域層建立設定 Use Cases
    data class SettingsData(
        val isSoundEnabled: Boolean,
        val isVibrationEnabled: Boolean
    )
    
    class GetSettingsUseCase @Inject constructor(
        private val repository: UserPreferencesRepository
    ) {
        operator fun invoke(): Flow<SettingsData> {
            return combine(
                repository.isSoundEnabled,
                repository.isVibrationEnabled
            ) { sound, vibration ->
                SettingsData(
                    isSoundEnabled = sound,
                    isVibrationEnabled = vibration
                )
            }
        }
    }
    
  3. 領域層建立設定更新 Use Cases
    class UpdateSoundSettingUseCase @Inject constructor(
        private val repository: UserPreferencesRepository
    ) {
        suspend operator fun invoke(isEnabled: Boolean) {
            repository.setSoundEnabled(isEnabled)
        }
    }
    
    class UpdateVibrationSettingUseCase @Inject constructor(
        private val repository: UserPreferencesRepository
    ) {
        suspend operator fun invoke(isEnabled: Boolean) {
            repository.setVibrationEnabled(isEnabled)
        }
    }
    
  4. 建立設定頁 SettingsViewModel
    data class SettingsUiState(
        val settings: SettingsData = SettingsData(isSoundEnabled = true, isVibrationEnabled = true),
        val isLoading: Boolean = true
    )
    @HiltViewModel
    class SettingsViewModel @Inject constructor(
        private val getSettingsUseCase: GetSettingsUseCase,
        private val updateSoundSettingUseCase: UpdateSoundSettingUseCase,
        private val updateVibrationSettingUseCase: UpdateVibrationSettingUseCase
    ) : ViewModel() {
        val uiState: StateFlow<SettingsUiState> = getSettingsUseCase()
            .map { SettingsUiState(settings = it, isLoading = false) }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5000),
                initialValue = SettingsUiState(isLoading = true)
            )
        fun onSoundToggled(isEnabled: Boolean) {
            viewModelScope.launch {
                updateSoundSettingUseCase(isEnabled)
            }
        }
        fun onVibrationToggled(isEnabled: Boolean) {
            viewModelScope.launch {
                updateVibrationSettingUseCase(isEnabled)
            }
        }
    }
    
  5. 更新 UI
    @Composable
    fun SettingsScreen(
        modifier: Modifier = Modifier,
        viewModel: SettingsViewModel = hiltViewModel()
    ) {
        val uiState by viewModel.uiState.collectAsState()
    
        SettingsScreenContent(
            modifier = modifier,
            uiState = uiState,
            onSoundToggled = viewModel::onSoundToggled,
            onVibrationToggled = viewModel::onVibrationToggled
        )
    }
    
    @Composable
    private fun SettingsScreenContent(
        modifier: Modifier = Modifier,
        uiState: SettingsUiState,
        onSoundToggled: (Boolean) -> Unit,
        onVibrationToggled: (Boolean) -> Unit
    ) {
        Box(
            modifier = modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            if (uiState.isLoading) {
                CircularProgressIndicator()
            } else {
                Column(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(16.dp),
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.spacedBy(16.dp)
                ) {
                    Text(
                        text = "設定",
                        style = MaterialTheme.typography.headlineLarge,
                        color = MaterialTheme.colorScheme.primary
                    )
    
                    SettingSwitchItem(
                        title = "按鈕音效",
                        isChecked = uiState.settings.isSoundEnabled,
                        onCheckedChange = onSoundToggled
                    )
    
                    SettingSwitchItem(
                        title = "按鈕震動",
                        isChecked = uiState.settings.isVibrationEnabled,
                        onCheckedChange = onVibrationToggled
                    )
                }
            }
        }
    }
    
    @Composable
    private fun SettingSwitchItem(
        title: String,
        isChecked: Boolean,
        onCheckedChange: (Boolean) -> Unit
    ) {
        Row(
            modifier = Modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Text(text = title, style = MaterialTheme.typography.bodyLarge)
            Switch(checked = isChecked, onCheckedChange = onCheckedChange)
        }
    }
    
    @Preview(showBackground = true)
    @Composable
    private fun SettingsScreenPreview() {
        AndroidTemplateTheme {
            SettingsScreenContent(
                uiState = SettingsUiState(isLoading = false),
                onSoundToggled = {},
                onVibrationToggled = {}
            )
        }
    }
    

上一篇
114/24 - Vibe Coding 新增和載入筆記
系列文
看見筆記捲土重來,試著用 Vibe Coding 完成一款 App 吧!25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言