# android-patterns examples

Short, generic excerpts of what each rule looks like. Names are illustrative;
judge the shape, not the names.

## PAT-3 — UserAction in, StateFlow out, Effect via Channel

Good

```kotlin
private val _effects = Channel<AssistantEffect>(Channel.BUFFERED)

/** One-time instructions to the Fragment. Collected once; never replayed on rotation. */
val effects: Flow<AssistantEffect> = _effects.receiveAsFlow()

/** Everything the user does to this screen comes through here. */
fun onAction(action: AssistantUserAction) {
    when (action) {
        is AssistantUserAction.Offline -> onOfflineAction(action)
        AssistantUserAction.MoveToSidebar -> moveToSidebar(thenAsk = null)
        is AssistantUserAction.TapIcon -> askAboutTarget(action.onDevice)
        // … one branch per user action
    }
}
```

Not this:

```kotlin
val answer = MutableLiveData<String>()          // new LiveData
val showToast = MutableStateFlow<String?>(null) // one-off event stored as state, replays on rotation
fun onAskClicked(q: String) { … }               // one public fun per button
fun onCloseClicked() { … }
```

## PAT-4 — independent elements, independent flows

Good

```kotlin
private val _floatingCard = MutableStateFlow<FloatingCard>(FloatingCard.Closed)
val floatingCard: StateFlow<FloatingCard> = _floatingCard.asStateFlow()

/** The floating button. Derived, never written. */
val floatingIcon: StateFlow<FloatingIcon> =
    combine(_target, iconSuppressed, anchorRect, contentTransforming) { target, suppressed, rect, transforming ->
        if (target != null && !suppressed && rect != null && !transforming) {
            FloatingIcon.Shown(target, rect)
        } else {
            FloatingIcon.Hidden
        }
    }.stateIn(viewModelScope, SharingStarted.Eagerly, FloatingIcon.Hidden)

private val _cardPosition = MutableStateFlow<CardPosition>(CardPosition.Centered)
val cardPosition: StateFlow<CardPosition> = _cardPosition.asStateFlow()
```

Not this:

```kotlin
data class ScreenUiState(val card: …, val icon: …, val cardPosition: …)  // a drag recomposes the whole card
```

## SIMP-3 — "maybe" as a named case, not null

Good: the public flows above are never nullable: `FloatingCard.Closed`,
`FloatingIcon.Hidden`, `CardPosition.Centered`. Nullable inputs
(`_target`, `anchorRect`) stay **private** and are combined into a named
public state (`floatingIcon`), so no caller ever writes `?.let`.

Not this:

```kotlin
val cardPosition = MutableStateFlow<PointF?>(null)   // every collector: cardPosition?.let { … } ?: center()
```
