Can't fetch the data of type list from RoomDB. I had used TypeConverter also

36 Views Asked by At

ABOUT THE PROJECT

I was making a app using Kotlin, Jetpack Compose and RoomDB in RoomDB. It is a very simple app, that consist of two screens. First-> Register screen -> Using OutlinedTextField it takes the name from the user, the "Add" button add that name to the list. And then if every desired name got entered, the "Final Add List to database" add that list to the dataBase.

Second-> Profile Screen -> It shows the list to the user. I had used MVVM architecture to make this project. I had used MutableLiveData() and LiveData() in the ViewModel class to follow a good practice, so that the variable which stores all list must only be updated in viewModel and I will show the LiveData version to the UI.

DETAILS ABOUT THE PROBLEM

When I took input from the user and saved it in database. When I visited the Profile screen to see the list, the screen was just blank nothing was written on the screen neither the fetched data nor the else statement which I had given for the condition if the list is empty or null "Can't fetch...."(You will get more detail about it in my ProfileScreen.kt file).

Short Summary

Since I am new to app development or coding in general. I asked ChatGPT to resolve this problem. But it had also confused me. If anybody of you have the answer to this problem, I humbly request you to explain me in very simple words.

Register.kt

package com.example.dataset2

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Button
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun RegisterScreen(
    viewModel: UserViewModel = viewModel(),
    onNavigateToProfileScreen: ()->Unit
){


    var name by remember {
        mutableStateOf("")
    }
    
    var nameList by remember {
        mutableStateOf(mutableListOf<String>())
    }
    
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ){
        
        item { 
            OutlinedTextField(
                value = name, 
                onValueChange = {
                    name = it
                },
                label = { Text(text = "Enter names:")}
            )
        }
        
        item {
            Spacer(modifier = Modifier.height(16.dp))
        }

        item {
            Column(
                modifier = Modifier.padding(16.dp)
            ) {
                if (nameList.isNotEmpty()){
                    nameList.forEach {
                        name_in_list ->
                        Text(text = name_in_list)
                    }
                }
            }
        }

        item {
            Spacer(modifier = Modifier.height(16.dp))

        }
        
        item {
            Button(onClick = {
                viewModel.addList(nameList)
                nameList = mutableListOf() // Clear the nameList
                name= ""
            }) {
                Text(text = "Add")
            }
        }

        item {
            Spacer(modifier = Modifier.height(16.dp))
        }

        item {
            Button(onClick = {
                viewModel.saveListsToDatabase()

                onNavigateToProfileScreen()


            }) {
                Text(text = "Final Add List to database")
            }
        }
    }
}

ProfileScreen.kt

package com.example.dataset2

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun ProfileScreen(viewModel: UserViewModel = viewModel()){
    val userDetail by viewModel.userDetail.observeAsState(initial = emptyList())

    LaunchedEffect(viewModel) {
        viewModel.fetchUserDetails()
    }
    
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        if (userDetail.isNotEmpty()){
            userDetail.forEach {
                user->
                user.list.forEach {
                    Text(text = it)
                }

            }
        }
        else{
            Text(text = "Can't fetch ....")
        }
    }
}

ViewModel

package com.example.dataset2

import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class UserViewModel(private val userRepository: UserRepository = Graph.userRepository) : ViewModel() {

    private val _userDetail = MutableLiveData<List<UserDetail>>()
    val userDetail: LiveData<List<UserDetail>> get() = _userDetail

    private val _allLists = mutableListOf<List<String>>()

    // Loading state
    private val _loading = mutableStateOf(false)
    val loading: State<Boolean> get() = _loading

    fun addList(list: List<String>) {
        _allLists.add(list)
    }

    fun saveListsToDatabase() {
        viewModelScope.launch {
            _loading.value = true // Set loading to true before saving
            for (list in _allLists) {
                userRepository.insertUser(UserDetail(list = list))
            }
            _allLists.clear()
            _loading.value = false // Set loading to false after saving
        }
    }

    fun fetchUserDetails() {
        viewModelScope.launch {
            _loading.value = true // Set loading to true before fetching
            userRepository.getUser().collect {
                _userDetail.value = it
                _loading.value = false // Set loading to false after fetching
            }
        }
    }
}

Repository

package com.example.dataset2

import kotlinx.coroutines.flow.Flow
class UserRepository(private val userDetailDao: UserDetailDao) {

    suspend fun insertUser(userDetail: UserDetail) {
        userDetailDao.insertUser(userDetail)
    }

    fun getUser(): Flow<List<UserDetail>> {
        return userDetailDao.getUser()
    }
}

DataClass

package com.example.dataset2

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
data class UserDetail(
    @PrimaryKey(autoGenerate = true)
    val id:Long = 0L,
    val list: List<String> = emptyList()
)

Converter file, that converts list into string and then from string to list.

package com.example.dataset2

import androidx.room.TypeConverter

class Converters {

    @TypeConverter
    fun fromStringList(value: List<String>?):String?{
        return value?.joinToString { "," }
    }

    @TypeConverter
    fun toStringList(value: String?):List<String>?{
        return value?.split(",")?.map { it.trim() }
    }

}

dataBase

package com.example.dataset2

import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters

@Database(
    entities = [UserDetail::class],
    version = 1
)

@TypeConverters(Converters::class)
abstract class Database : RoomDatabase(){

    abstract fun userDetailDao(): UserDetailDao

}

Dao

package com.example.dataset2

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow

@Dao
interface UserDetailDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUser(userDetail: UserDetail)

    @Query("Select * from userdetail")
    fun getUser(): Flow<List<UserDetail>>

}

Singleton file that stores an instance of Repository and database

package com.example.dataset2

import android.content.Context
import androidx.room.Room

object Graph {

    lateinit var database: Database

    val userRepository by lazy {
        UserRepository(database.userDetailDao())
    }

    fun init(context: Context){
        database = Room.databaseBuilder(context,Database::class.java,"database.db").build()
    }

}

Navigation and MainActivity.kt

package com.example.dataset2

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.dataset2.ui.theme.Dataset2Theme

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Dataset2Theme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {

                    AppNavigation()

                }
            }
        }
    }
}


@Composable
fun AppNavigation(){
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "registerscreen"){

        composable("registerScreen"){
            RegisterScreen (onNavigateToProfileScreen = {navController.navigate("profilescreen")})
        }

        composable("profilescreen"){
            ProfileScreen()

        }

    }
}


Application file where I had given context to make database

package com.example.dataset2

import android.app.Application

class MyApp: Application() {
    override fun onCreate() {
        super.onCreate()
        Graph.init(this)
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application

        android:name=".MyApp"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Dataset2"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.Dataset2">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Used dependencies and plugins

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("kotlin-parcelize")
    id("kotlin-kapt")
}

android {
    namespace = "com.example.dataset2"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.dataset2"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.1"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {

    val room = "2.6.1"

    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0-alpha01")
    implementation("androidx.lifecycle:lifecycle-viewmodel:2.4.0-alpha01")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")


    implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.4.0")
    implementation ("androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.1")


    //Dependencies for RoomDB
    implementation("androidx.room:room-runtime:$room")
    implementation("androidx.room:room-ktx:$room")
    kapt("androidx.room:room-compiler:$room")

    // Add the latest version of Jetpack Compose
    implementation("androidx.compose.ui:ui:1.6.0")
    implementation("androidx.activity:activity-compose:1.8.2")
    implementation("androidx.compose.foundation:foundation:1.6.0")
    implementation("androidx.compose.material:material:1.6.0")
    implementation ("androidx.compose.material3:material3:1.1.2")


    implementation("androidx.compose.runtime:runtime-livedata:1.6.0")
    implementation("androidx.compose.runtime:runtime:1.6.0")


    //Compose ViewModel
//    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")

    //Network Call
    implementation("com.squareup.retrofit2:retrofit:2.9.0")

    //Json to Kotlin object mapping
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")

    //Image loading
    implementation("io.coil-kt:coil-compose:2.4.0")

    //Navigation
    implementation("androidx.navigation:navigation-compose:2.7.6")


    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
    implementation("androidx.activity:activity-compose:1.8.2")
    implementation(platform("androidx.compose:compose-bom:2023.08.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
}

I am using Android studio's hedgehog RC1. These are the things I had tried.

0

There are 0 best solutions below