DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Proto DataStore in Android -Type-Safe Settings Management

Build type-safe persistent storage with Protocol Buffers!

Protobuf Plugin Setup

Add dependencies to build.gradle.kts:

plugins {
    id("com.google.protobuf") version "0.9.1"
}

dependencies {
    implementation("androidx.datastore:datastore-core:1.0.0")
    implementation("com.google.protobuf:protobuf-kotlin-lite:3.21.0")
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.21.0"
    }
    generateProtoTasks {
        all().forEach { task ->
            task.builtins {
                register("kotlin_lite") {
                    option("lite")
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

.proto Schema Definition

Define your data structure in app_settings.proto:

syntax = "proto3";

message AppSettings {
    string user_name = 1;
    int32 theme = 2;
    bool notifications_enabled = 3;
    repeated string favorite_items = 4;
}
Enter fullscreen mode Exit fullscreen mode

Serializer Implementation

Create a serializer for your protobuf message:

object AppSettingsSerializer : Serializer<AppSettings> {
    override val defaultValue: AppSettings = AppSettings.getDefaultInstance()

    override suspend fun readFrom(input: InputStream): AppSettings {
        return AppSettings.parseFrom(input)
    }

    override suspend fun writeTo(t: AppSettings, output: OutputStream) {
        t.writeTo(output)
    }
}
Enter fullscreen mode Exit fullscreen mode

DataStore Creation

Initialize DataStore with your serializer:

val Context.dataStore: DataStore<AppSettings> by dataStore(
    fileName = "app_settings.pb",
    serializer = AppSettingsSerializer
)
Enter fullscreen mode Exit fullscreen mode

ViewModel updateData with toBuilder

Update settings safely with toBuilder:

class SettingsViewModel(private val context: Context) : ViewModel() {
    val settings: Flow<AppSettings> = context.dataStore.data

    fun updateUserName(name: String) {
        viewModelScope.launch {
            context.dataStore.updateData { currentSettings ->
                currentSettings.toBuilder()
                    .setUserName(name)
                    .build()
            }
        }
    }

    fun setTheme(themeId: Int) {
        viewModelScope.launch {
            context.dataStore.updateData {
                it.toBuilder().setTheme(themeId).build()
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Compose UI Integration

Read and update settings from Compose:

@Composable
fun SettingsScreen(viewModel: SettingsViewModel) {
    val settings by viewModel.settings.collectAsState(AppSettings.getDefaultInstance())

    Column(modifier = Modifier.padding(16.dp)) {
        TextField(
            value = settings.userName,
            onValueChange = { viewModel.updateUserName(it) },
            label = { Text("User Name") }
        )

        Row {
            Text("Theme: ")
            RadioButton(
                selected = settings.theme == 0,
                onClick = { viewModel.setTheme(0) }
            )
            Text("Light")
        }

        Row {
            Text("Notifications: ")
            Switch(
                checked = settings.notificationsEnabled,
                onCheckedChange = { viewModel.toggleNotifications() }
            )
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Achieve type-safe, boilerplate-free settings management!


8 production-ready Android app templates on Gumroad.
Browse templatesGumroad

Top comments (0)