Search code examples
androidkotlinandroid-jetpack-composeandroid-room

LazyColumn is not updated when the data is changed


I just started learning Android development. I want to find a case, using Viewmodel+Room+Flow+collectAsStateWithLifecycle(), unfortunately I didn't find such a case. So I wrote the following code referring to the documentation and some information on the Internet.

Dao

@Dao
interface IArticleDao {

    @Query("select * from article")
    fun getAll(): List<Article>

    @Insert
    suspend fun add(article: Article): Long

}

Repository


private const val TAG = "ArticleRepository"

class ArticleRepository @Inject constructor(
    private val articleDao: IArticleDao
) {

    private val ioDispatcher = IO

    fun add(article: Article): Long {
        var id = 0L
        CoroutineScope(ioDispatcher).launch {
            try {
                id = articleDao.add(article)
            }catch (e: Exception){
               Log.e(TAG, "There is an exception in the new article")
            }
        }
        return id
    }

    fun getAll(): Flow<List<Article>> =
        flow {
            emit(
                articleDao.getAll()
            )
        }.flowOn(ioDispatcher)

}

ViewModel

@HiltViewModel
class ArticleViewModel @Inject constructor(private val articleRepository: ArticleRepository) : ViewModel() {

    val articles = articleRepository.getAll().stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5_000),
        initialValue = emptyList()
    )

    fun add(article: Article): Long{
        return articleRepository.add(article)
    }

}

Activity

Scaffold(
    content = { innerPadding ->
        val articles by articleViewModel.articles.collectAsStateWithLifecycle()
        Content(
            articles = articles, articleViewModel
        )
    }
)

@Composable
fun Content(
    articles:
    List<Article>, viewModel: ArticleViewModel
) {
    Column {
        Row {
            Button(onClick = {
                viewModel.add(Article(title = "this is a test", url = "http://www.test4.com/"))
            }) {
                Text("add")
            }
        }
        Row {
            LazyColumn(
                modifier = Modifier.fillMaxWidth(),
                contentPadding = PaddingValues(16.dp)
            ) {
                items(items = articles, key = { article -> article.id }) { article ->
                    Row {
                        Text(text = article.title)
                    }
                }
            }
        }
    }
}

This creates a problem. When a new piece of data is added, a new piece of data is added to the database, but the UI interface is not updated accordingly. Only when the Home button is clicked and the application is opened again can the UI interface be updated.

I know I must be missing something, but I don't know what. Possibly the code in the Repository?


Solution

  • Instead of returning a list in your DAO, try returning a Flow<List<Article>>.

    To do that, don't forget to add the following dependency implementation("androidx.room:room-ktx:$room_version")

    @Dao
    interface IArticleDao {
    
        @Query("select * from article")
        fun getAll(): Flow<List<Article>>
    
        @Insert
        suspend fun add(article: Article): Long
    
    }
    
    
    private const val TAG = "ArticleRepository"
    
    class ArticleRepository @Inject constructor(
        private val articleDao: IArticleDao
    ) {
    
        private val ioDispatcher = IO
    
        fun add(article: Article): Long {
            var id = 0L
            CoroutineScope(ioDispatcher).launch {
                try {
                    id = articleDao.add(article)
                }catch (e: Exception){
                   Log.e(TAG, "There is an exception in the new article")
                }
            }
            return id
        }
    
        fun getAll(): Flow<List<Article>> = articleDao.getAll().flowOn(ioDispatcher)
           
    }
    

    Using a flow, you will listen the database changes instead of just querying one time the datas.