A comprehensive interactive Android app demonstrating advanced Jetpack Compose concepts including side-effects management and performance optimization techniques.
This lesson covers essential Compose concepts for production-ready apps:
- LaunchedEffect - Managing coroutines in Compose
- DisposableEffect - Handling cleanup with side effects
- derivedStateOf - Optimizing derived calculations
- snapshotFlow - Converting Compose State to Flow
- Performance Optimization - Avoiding unnecessary recompositions
- Understand when and how to use different side-effect APIs
- Master coroutine management in Compose lifecycle
- Learn to optimize performance and prevent unnecessary recompositions
- Implement proper cleanup for resources and observers
- Use Flow operators with Compose state
What it demonstrates:
- How
LaunchedEffect
launches coroutines tied to composition lifecycle - Automatic restart when keys change
- Timer implementation with pause/resume functionality
- Interactive key switching (A, B, C) to see effect restart behavior
Key Concepts:
LaunchedEffect(key) {
// This block restarts when 'key' changes
while (isRunning) {
delay(1000)
counter++
}
}
What it demonstrates:
- Lifecycle observation with proper cleanup
onDispose
block for resource management- Toggle observer to see add/remove behavior
- Real-time lifecycle event logging
Key Concepts:
DisposableEffect(lifecycle) {
val observer = LifecycleEventObserver { _, event ->
// Handle lifecycle events
}
lifecycle.addObserver(observer)
onDispose {
lifecycle.removeObserver(observer) // Cleanup!
}
}
What it demonstrates:
- Optimizing derived calculations
- Preventing unnecessary recompositions
- Real-time recomposition counting
- Interactive form with first name, last name, and age inputs
Key Concepts:
val fullName by remember {
derivedStateOf { "$firstName $lastName" }
}
// Only recomposes when the RESULT changes, not on every keystroke
What it demonstrates:
- Converting Compose State to Kotlin Flow
- Scroll position tracking
- Flow operators (distinctUntilChanged, map, filter)
- Scroll-to-top FAB that appears when not at top
Key Concepts:
LaunchedEffect(listState) {
snapshotFlow { listState.firstVisibleItemIndex }
.distinctUntilChanged()
.collect { index ->
// React to scroll changes
}
}
What it demonstrates:
- Side-by-side comparison of optimized vs unoptimized code
- Real-time recomposition counting
- Impact of
remember()
with keys - Visual demonstration of performance differences
Key Concepts:
// β Bad: Recalculates every recomposition
val result = expensiveCalculation(input)
// β Good: Only recalculates when input changes
val result = remember(input) {
expensiveCalculation(input)
}
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.9.4")
implementation("androidx.activity:activity-compose:1.11.0")
implementation("androidx.compose.material3:material3")
- Min SDK: 26 (Android 8.0)
- Target SDK: 36
- Kotlin: 2.0.21
- Compose BOM: 2024.09.00
app/src/main/java/com/example/androidkotlinlesson9/
βββ MainActivity.kt # Entry point
βββ SideEffectsLesson.kt # All lesson composables
βββ ui/theme/ # Theme configuration
- Use for: API calls, timers, animations, one-time events
- Lifecycle: Cancels when leaving composition
- Key parameter: Restarts effect when key changes
- Use for: Observers, listeners, resource registration
- Cleanup: Always implement
onDispose
block - Remember: Cleanup is crucial to prevent memory leaks
- Use for: Computed properties, derived calculations
- Performance: Only recomposes when result changes
- Best practice: Wrap in
remember
for optimal performance
- Use for: Analytics, scroll tracking, state monitoring
- Power: Full Flow API with operators
- Integration: Bridges Compose State with coroutines
- remember(): Cache expensive calculations
- keys: Provide keys to control recalculation
- Stable parameters: Use immutable data classes
- Avoid: Lambda allocations in tight loops
- Clone the repository
- Open in Android Studio (Ladybug | 2024.2.1 or later)
- Sync Gradle dependencies
- Run on emulator or physical device (Android 8.0+)
- Explore each tab to learn interactively
- Experiment: Try changing keys in LaunchedEffect tab
- Observe: Watch recomposition counts in Performance tab
- Compare: Notice the difference between optimized and unoptimized code
- Read logs: Check Logcat for DisposableEffect and snapshotFlow events
- Modify: Change the code and see immediate results
- Add a new LaunchedEffect that fetches data from an API
- Create a DisposableEffect for keyboard visibility listener
- Implement derivedStateOf for filtering a list
- Use snapshotFlow to track text field changes
- Optimize a slow list with proper keys and stable parameters
- Don't: Use
LaunchedEffect(true)
- it never restarts - Don't: Forget
onDispose
inDisposableEffect
- Don't: Call expensive functions directly in Composables
- Don't: Use mutable state outside of
remember
- Do: Always provide meaningful keys to side effects
- Interactive Learning: Each concept has hands-on examples
- Visual Feedback: Real-time recomposition counters
- Real-world Examples: Practical use cases for each API
- Performance Comparison: See the impact of optimization
- Best Practices: Learn the right way from the start
The LaunchedEffect demo shows a production-ready timer that:
- Restarts when key changes
- Can be paused/resumed
- Properly cancels on composition exit
- Logs lifecycle events for debugging
The DisposableEffect demo demonstrates:
- Proper observer registration/unregistration
- Memory leak prevention
- Event logging for debugging
- Clean separation of concerns
The Performance demo uses SideEffect
to:
- Count recompositions per component
- Compare optimization strategies
- Visualize performance impact
- Teach optimization techniques
- Material 3 Design: Modern, clean interface
- Tab Navigation: Easy switching between lessons
- Color-coded Cards: Visual distinction of concepts
- Interactive Controls: Buttons, sliders, text fields
- Responsive Layout: Works on all screen sizes
- Smooth Animations: AnimatedVisibility for polish
The project uses:
- Kotlin DSL for build scripts
- Version Catalog for dependency management
- Compose Compiler Plugin for optimization
- ProGuard rules for release builds
After completing this lesson, you should be able to:
- β Choose the right side-effect API for your use case
- β Implement proper cleanup in your Composables
- β Optimize performance and reduce recompositions
- β Debug composition and recomposition issues
- β Write production-ready Compose code
- Practice: Build a feature using each side-effect API
- Experiment: Modify the examples to test edge cases
- Profile: Use Layout Inspector to see recompositions
- Optimize: Apply learnings to your existing projects
- Share: Teach others what you've learned
Found a bug? Have a suggestion? Feel free to:
- Open an issue
- Submit a pull request
- Share your improvements
Happy Coding! π
Made with β€οΈ using Jetpack Compose