java.lang.IllegalStateException: There are multiple DataStores active for the same file: /data/user/0/com.firstgoalkeeper.firstgoalkeeper/files/datastore/player_pref.preferences_pb. You should either maintain your DataStore as a singleton or confirm that there is no two DataStore's active on the same file (by confirming that the scope is cancelled).
class Constants {
companion object{
const val PLAYER_PREFERENCE = "player_pref"
val PLAYER_SELECTION_KEY = intPreferencesKey("player_selection")
}
}
abstract class PrefsDataStore(context: Context, fileName: String) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
fileName
)
val mdataStore: DataStore<Preferences> = context.dataStore
}
class PlayerSelectionDataStore(context: Context) : PrefsDataStore(context,
PLAYER_PREFERENCE) {
suspend fun storeIndex(index: Int) {
mdataStore.edit {
it[PLAYER_SELECTION_KEY] = index
}
}
val userSelectionFlow: Flow<Int> = mdataStore.data.map {
it[PLAYER_SELECTION_KEY] ?: 4
}
}
@Composable
fun PlayerSelection() {
val context = LocalContext.current
val playerSelectionDataStore = PlayerSelectionDataStore(context)
var index by remember {
mutableStateOf(4)
}
Log.d("index", "PlayerSelection: we are at index ${index} ")
Log.d("index", "PlayerSelection: we select ${allTeamsLists[index].name} ")
Row(
verticalAlignment = Alignment.CenterVertically, modifier = Modifier
.fillMaxSize()
.background(color = goalkeeperBackground)
) {
// ...
Box(
modifier = Modifier
.clickable {
GlobalScope.launch {
playerSelectionDataStore.storeIndex(index)
}
Toast
.makeText(
context,
"${allTeamsLists[index].name} player is Selected ",
Toast.LENGTH_SHORT
)
.show()
}
...
) {...}
What i did wrong and suggest best practice.
CodePudding user response:
Right not you're creating a new PlayerSelectionDataStore
object on each recomposition.
The least you can do is wrapping it with remember
:
val playerSelectionDataStore = remember(context) { PlayerSelectionDataStore(context) }
A more general solution:
@Composable
fun <T> rememberPreference(
key: Preferences.Key<T>,
defaultValue: T,
): MutableState<T> {
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val state = remember {
context.dataStore.data
.map {
it[key] ?: defaultValue
}
}.collectAsState(initial = defaultValue)
return remember {
object : MutableState<T> {
override var value: T
get() = state.value
set(value) {
coroutineScope.launch {
context.dataStore.edit {
it[key] = value
}
}
}
override fun component1() = value
override fun component2(): (T) -> Unit = { value = it }
}
}
}
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "preferences")
Usage:
var text by rememberPreference(stringPreferencesKey("key_text"), defaultValue = "World")
TextField(value = text, onValueChange = { text = it })