Skip to content
Go back

Flow vs Suspend in Room DAO - When to Use Which in Jetpack Compose

Flow vs Suspend in Room DAO – When to Use Which?

When working with Room Database in Jetpack Compose, choosing between Flow<T> and suspend functions in your DAO can significantly impact your app’s performance and reactivity. This guide explains the key differences and when to use each approach.

The Core Difference

The choice between Flow and suspend in Room DAO comes down to data observation vs one-time operations:


When to Use Flow

Use Flow for Real-Time Updates

Flow is perfect when you need your UI to automatically reflect database changes. When the underlying data changes, Flow automatically emits new values to all collectors.

Ideal use cases:

Example:

@Dao
interface SubjectDao {
    @Query("SELECT * FROM Subject")
    fun getAllSubjects(): Flow<List<Subject>>  // Observes database changes
}

Using Flow in ViewModel:

class SubjectViewModel(private val dao: SubjectDao) : ViewModel() {
    // Automatically updates UI when database changes
    val subjects: StateFlow<List<Subject>> = dao.getAllSubjects()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )
}

Using Flow in Composable:

@Composable
fun SubjectListScreen(viewModel: SubjectViewModel) {
    val subjects by viewModel.subjects.collectAsState()
    
    LazyColumn {
        items(subjects) { subject ->
            SubjectItem(subject)
        }
    }
    // UI automatically updates when data changes!
}

When to Use suspend

Use suspend for One-Time Operations

suspend functions are designed for operations that execute once and don’t need to observe changes. They run asynchronously in coroutines, preventing UI thread blocking.

Ideal use cases:

Example:

@Dao
interface SubjectDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertSubject(subject: Subject)  // Single insert
}

Using suspend in ViewModel:

class SubjectViewModel(private val dao: SubjectDao) : ViewModel() {
    
    fun addSubject(name: String) {
        viewModelScope.launch {
            val subject = Subject(name = name, isActive = true)
            dao.insertSubject(subject)  // Executes once
        }
    }
}

Quick Comparison Table

FeatureFlowsuspend
PurposeObserve data changesOne-time operation
EmissionMultiple values over timeSingle result
UI UpdatesAutomatic when data changesManual trigger needed
Best forSELECT queries (lists)INSERT, UPDATE, DELETE
LifecycleContinuous streamCompletes after execution
Thread SafetyBuilt-inCoroutine context

Common Patterns

Pattern 1: List Screen with CRUD Operations

@Dao
interface SubjectDao {
    // Flow for displaying the list (auto-updates)
    @Query("SELECT * FROM Subject ORDER BY name ASC")
    fun getAllSubjects(): Flow<List<Subject>>
    
    // suspend for modifying data (one-time operations)
    @Insert
    suspend fun insertSubject(subject: Subject)
    
    @Update
    suspend fun updateSubject(subject: Subject)
    
    @Delete
    suspend fun deleteSubject(subject: Subject)
}

Pattern 2: Detail Screen

@Dao
interface SubjectDao {
    // Flow for observing single item changes
    @Query("SELECT * FROM Subject WHERE subjectId = :id")
    fun getSubjectByIdFlow(id: Int): Flow<Subject?>
    
    // suspend for one-time fetch (e.g., navigation argument)
    @Query("SELECT * FROM Subject WHERE subjectId = :id")
    suspend fun getSubjectById(id: Int): Subject?
}

Best Practices

DO:

DON’T:


Performance Considerations

Flow Advantages:

suspend Advantages:


Share this post on:

Previous Post
How to Remove Voicemail Menu Options in FusionPBX - Disable Options 5, 8, 9
Next Post
Install Piper TTS on Debian Linux(Offline Neural Text-to-Speech Guide)