Putting it together
- Kotlin course
- Intermediate
- Lesson 116/116
- 6 min
Combine all layers into a working app.
Putting it together: Flow stream
In the final lesson you will combine all parts and add a Flow method to Library. fun bookFlow(): Flow<Book> = listAll().asFlow() presents the books as a stream; you can consume each book one by one with collect. This brings file saving and stream-based consumption together.
fun bookFlow(): Flow<Book> = listAll().asFlow()
fun main() = runBlocking {
lib.bookFlow().collect { println(it.format()) }
}Here asFlow() turns a list into a flow; each element is emitted in turn. collect prints each book's format output. runBlocking bridges a coroutine in main. Flow is ideal for lazy consumption of large collections; the whole list is not loaded into memory.
Combining all parts
The final app combines these parts: data class Book (data model), sealed class Category (OOP hierarchy), class Library (collection management), save/load (file I/O) and bookFlow (async stream). Each part was prepared in earlier lessons; now they all work together.
fun main() = runBlocking {
val lib = Library()
lib.add(Book("Crime and Punishment", "Dostoyevsky", 1866, Fiction))
lib.save("library.txt")
lib.bookFlow().collect { println(it.format()) }
}Here a book is added, saved to a file, then consumed via a Flow. This is an app that combines all core intermediate Kotlin concepts (OOP, collections, file I/O, Flow). Later, search, filtering or a UI can be added to this structure. The design allows each part to be tested independently.
Practice Add bookFlow and run the full app
All definitions are ready (Book, Category, Library, save/load). Add fun bookFlow(): Flow<Book> = listAll().asFlow() to the Library class. Write main as fun main() = runBlocking { ... }. Inside it create val lib = Library(); add two books; call lib.save("library.txt"); print each book with lib.bookFlow().collect { println(it.format()) }. Required imports (java.io.File, kotlinx.coroutines.flow.*, kotlinx.coroutines.runBlocking) are in the ready.
Starter code
import java.io.File
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
data class Book(val title: String, val author: String, val year: Int, val category: Category) {
fun format(): String = "$title - $author ($year) [${category.label()}]"
}
sealed class Category {
abstract fun label(): String
}
object Fiction : Category() {
override fun label() = "Fiction"
}
object NonFiction : Category() {
override fun label() = "Non-fiction"
}
class Library {
private val books = mutableListOf<Book>()
fun add(b: Book) { books.add(b) }
fun listAll(): List<Book> = books.toList()
fun save(path: String) {
val content = books.joinToString("\n") { it.format() }
File(path).writeText(content)
}
fun load(path: String): String {
return File(path).readText()
}
}
// Add bookFlow(): Flow<Book> to Library: return listAll as a stream
// main: add two books, call save("library.txt")
// inside runBlocking, collect the bookFlow stream and print each format output
fun main() {
// write here
}Sample solution
import java.io.File
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
data class Book(val title: String, val author: String, val year: Int, val category: Category) {
fun format(): String = "$title - $author ($year) [${category.label()}]"
}
sealed class Category {
abstract fun label(): String
}
object Fiction : Category() {
override fun label() = "Fiction"
}
object NonFiction : Category() {
override fun label() = "Non-fiction"
}
class Library {
private val books = mutableListOf<Book>()
fun add(b: Book) { books.add(b) }
fun listAll(): List<Book> = books.toList()
fun save(path: String) {
val content = books.joinToString("\n") { it.format() }
File(path).writeText(content)
}
fun load(path: String): String {
return File(path).readText()
}
fun bookFlow(): Flow<Book> = listAll().asFlow()
}
fun main() = runBlocking {
val lib = Library()
lib.add(Book("Crime and Punishment", "Dostoyevsky", 1866, Fiction))
lib.add(Book("Sapiens", "Harari", 2011, NonFiction))
lib.save("library.txt")
lib.bookFlow().collect { println(it.format()) }
}Expected output
Crime and Punishment - Dostoyevsky (1866) [Fiction] Sapiens - Harari (2011) [Non-fiction]
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 What is Flow used for in this app?
- For file compression
- For database connections
- To present books as a stream Correct answer
- For drawing the UI
Explanation: Flow is used to consume books as a stream.
2 Match the app parts
| Term | Meaning |
|---|---|
data class Book |
Data model |
sealed class Category |
OOP hierarchy |
class Library |
Collection management |
fun bookFlow |
Async stream |
3 Turn a list into a flow
fun bookFlow(): Flow<Book> = listAll().____()
fun bookFlow(): Flow<Book> = listAll().asFlow()
Explanation: asFlow() turns the list into a Flow.