Однонаправленный поток данных (unidirectional data flow) представляет подход, при котором состояние компонента не может напрямую изменяться дочерними, вложенными компонентами. Применение однонаправленного потока данных упрощает создание модульных, независимых друг от друга компонентов и, как следствие, упрощает разработку приложения и его тестирование. Например, возьмем следующий пример:
package com.example.helloapp import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Counter() } } } @Composable fun Counter(){ val clicksState = remember{mutableStateOf(0)} val onClicksChange = { value : Int -> clicksState.value = value } Column{ Text(text = "Clicks: ${clicksState.value}", fontSize = 28.sp ) Increment(clicksState.value, onClicksChange) } } @Composable fun Increment(clicks: Int, onClicksChange : (Int) -> Unit){ Text( text = "+", fontSize = 36.sp, modifier = Modifier.clickable( onClick = {onClicksChange(clicks+1)}) .border(1.dp, Color.DarkGray) .padding(10.dp, 0.dp) ) }
Здесь у нас есть компонент Counter. Этот компонент определяет состояние clicksState, которое хранит количество нажатий, и функицю onClicksChange, которая изменяет это состояние:
val clicksState = remember{mutableStateOf(0)} val onClicksChange = { value : Int -> clicksState.value = value }
onClicksChange
представлет функцию, которая принимает один параметр типа Int - новое значение и передает его в состояние clicksState.
Внутри Counter для вывода состояния и его изменения определены два компонента:
Column{ Text(text = "Clicks: ${clicksState.value}", fontSize = 28.sp ) Increment(clicksState.value, onClicksChange) }
Первый компонент - Text просто выводит текущее состояние на экран устройства.
Второй компонент - Increment призван изменять значение состояния. В него как раз передаются текущее значение состояния и функция изменения состояния. Посмотрим на его определение:
@Composable fun Increment(clicks: Int, onClicksChange : (Int) -> Unit){ Text( text = "+", fontSize = 36.sp, modifier = Modifier.clickable( onClick = {onClicksChange(clicks+1)}) .border(1.dp, Color.DarkGray) .padding(10.dp, 0.dp) ) }
Данный компонент в качестве параметров принимает текущее количество нажатий (по сути текущее значение состояния) и функцию для изменения. Внутри этого компонента также определен компонент Text с
примитивной стилизацией под кнопку увеличения. По нажатию на этот компонент будет вызываться переданная функция onClicksChange()
, в которую передается новое значение нажатий.
Таким образом, при нажатии на компонент Increment произойдет вызов функции onClicksChange и изменение состояния"
Таким образом, мы можем отдельно развивать компоненты Counter и Increment. При этом, хотя и Increment вызывает изменение состояния, но реальное изменение - определение функции onClicksChange все равно происходит в Counter. Increment напрямую не изменяет состояние компонента Counter.