# Compose - GUI

a**New Application with defined application composable**

```
@Composable
fun SomeGuiApplication(){
    ComposeWorkshopsTheme {
            UserList()
        }
    }
}

@Composable
fun UserList(){
//at this point user repository is hardcoded
    val users=UsersRepository.select() 
    users.forEach{
        Text("User ${it.name} ")
    }
}
```

**change background with surface**

```
ComposeWorkshopsTheme {
        Surface(color = Color.Yellow) {
            UserList()
        }
    }
```

**format user list with column**

```
 Column(
        verticalArrangement = Arrangement.Center // just check possibilities in the code
    ) {
        users.forEach {
            Text(
                text = "User : ${it.name} ",
                modifier = Modifier.padding(1.dp),
            )
        }
    }
```

**make text clickable**

```
Modifier
                    .padding(1.dp)
                    .clickable { Toast.makeText(context, "user clicked ${it.name}",Toast.LENGTH_LONG).show() }
```

**modifiers order**

show difference between&#x20;

```
Modifier
                    .padding(30.dp)
                    .clickable { Toast.makeText(context, "user clicked ${it.name}",Toast.LENGTH_LONG).show() }
```

and:

```
     Modifier
                    .clickable { Toast.makeText(context, "user clicked ${it.name}",Toast.LENGTH_LONG).show() }
                    .padding(30.dp)
```

## Scaffold

**First attempt**

```
@Composable
fun SomeGuiApplication() {
    ComposeWorkshopsTheme {
        Surface(color = Color.Yellow) {
            AppBody()
        }
    }
}

@Composable
fun AppBody(){
    Scaffold {
        UserList()
    }
}
```

**Better composition structure**

```
@Composable
fun SomeGuiApplication() {
    ComposeWorkshopsTheme {
        Scaffold {
            AppBody()
        }
    }
}

@Composable
fun AppBody(){
    Surface(color = Color.Yellow) {
        UserList()
    }
}
```

**Top Bar**

```
@Composable
fun SomeGuiApplication() {
    ComposeWorkshopsTheme {
        Scaffold(
            topBar = { topBar() } // waith for kotlin 1.6
        ) {
            AppBody()
        }
    }
}
```

**Top Bar Compsable - first variant**&#x20;

```
@Composable
fun topBar(){
    TopAppBar() {
        Text(
            text = "Some AppTitle",
            style = MaterialTheme.typography.h3
        )
    }
}
```

**Top Bar Composable - Second variant**

* There is a second variant of *TopAppBar* function and you can not use function syntax with it!
* to make code more readable you may want to extract some function definitions
  * remember about @Composable annotation before function definitions
  * (to me: during workshops explain topBarActions syntax)

```
@Composable
fun topBar(){
    val appTitle= @Composable {
        Text(
            text = "Some AppTitle",
            style = MaterialTheme.typography.h3
        )
    }

    val topBarActions: @Composable RowScope.() -> Unit ={
            IconButton(onClick = { /* doSomething() */ }) {
                Icon(Icons.Filled.Email, contentDescription = null)
            }
    }

    TopAppBar(
        actions = topBarActions,
        title = appTitle
    )
}
```

**Floating Button**&#x20;

```
   val fap =@Composable {
        FloatingActionButton(onClick = { /* ... */ }) {
            Icon(Icons.Filled.AccountBox, contentDescription = null)
        }
    }
    ...
    Scaffold(
                topBar = { topBar() }, // waith for kotlin 1.6
                floatingActionButton = fap
        ) {
            AppBody()
        }
```

## Users List

Extract layout for single user

```
@Composable
private fun DisplayUser(context: Context,user: User) {
    Text(
        text = "User : ${user.name} ",
        modifier =
        Modifier
            .clickable {
                Toast
                    .makeText(context, "user clicked ${user.name}", Toast.LENGTH_LONG)
                    .show()
            }
            .padding(30.dp)
    )
}
```

**Bonus : FP and currying**

```
private fun displayUser(context: Context): @Composable (User) -> Unit = { user ->
...
}

val context = LocalContext.current
        val userComposable= displayUser(context)
        users.forEach{userComposable(it)}
```

**Change data source in "our very sophisticated DI framework"**

```
object DI{
//    val usersRepository:UsersRepository = InMemoryUsersRepository
    val usersRepository:UsersRepository = InMemorySourceOfALotOfUsersy
}
```

**Lazy Column - First attempt**

```
   LazyColumn(
        verticalArrangement = Arrangement.Center // just chek in the code
    ) {
        val context = LocalContext.current //we are not in Composable anymore!!!asdasdLa Column - second attemptzy
        val userComposable= displayUser(context)
        users.forEach{userComposable(it)}

    }
```

**Lazy Column - Second attempt**

```
LazyColumn(
            verticalArrangement = Arrangement.Center // just chek in the code
    ) {

        //srogie rozwiązanie - import androidx.compose.foundation.lazy.items
        items(users.toList()) {
            userComposable(it)
        }

    }
```

**Add state to View**

```
 //remember about : import androidx.compose.runtime.*
 var userClickedState:String? by remember {
        mutableStateOf(null)
    }
    if(userClickedState!=null) {
        Text(text = "clicked user $userClickedState")
    }

    val userOnClick:(User)->Unit={  //currying once again!!!
        userClickedState=it.name
        Toast
                .makeText(context, "user clicked ${it.name}", Toast.LENGTH_LONG)
                .show()
    }

  ....
      val userComposable = displayUser(userOnClick)
  ...
  private fun displayUser(onClick:(User)->Unit): @Composable (User) -> Unit = { user ->
    Text(
            text = "User : ${user.name} ",
            modifier =
            Modifier
                    .clickable {
                        onClick(user)
                    }
                    .padding(30.dp)
    )
}
```

(but it doesnt match theory)

{% embed url="<https://developer.android.com/codelabs/jetpack-compose-layouts?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fcompose%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fjetpack-compose-layouts#5>" %}
in theory scroll state should be stored
{% endembed %}

**Refactor state**

```
class UsersPageState {
    private var userClickedState: String? by mutableStateOf(null)

    val userInfo:String
        get() = userClickedState ?: throw RuntimeException("users state was reset")

   val updateClickedState: (User) -> Unit = {
        if (userClicked())
            userClickedState = "$userClickedState:${it.name}"
        else
            userClickedState = it.name
    }

    fun userClicked() = userClickedState != null
}

@Composable
fun UserList(usersPage: UsersPageState = UsersPageState(), usersRepository: UsersRepository = DI.usersRepository) {
    val users = usersRepository.select().toList()
    val userComposable = displayUser(onClick = usersPage.updateClickedState)
....
}


fun displayUser(onClick: (User) -> Unit): @Composable (User) -> Unit = { user ->

```

**Puzzler - when state is created?**

```
@Composable
fun UserList(usersPage: UsersPageState= UsersPageState(), usersRepository: UsersRepository = DI.usersRepository) {

    println("****************************RECOMPOSE*******************************")
    val users = usersRepository.select().toList()
    val userComposable = displayUser(onClick = usersPage.updateClickedState)
    
class UsersPageState {

    init {
        println("**************USER PAGE STATE INITIALIZED***************")
    }
```

still working

```
@Composable
fun AppBody() {
    Surface(color = Color.Yellow) {
        val state=UsersPageState()
        UserList(state)
    }
}
```

so compose actually saves stores default parameters outside "particular method compose"!

Following will not work:

```
@Composable
fun UserList(usersRepository: UsersRepository = DI.usersRepository) {
    println("****************************RECOMPOSE*******************************")
    val usersPage=UsersPageState()
```

**Survive Rotation**

First add saver

* saver doesnt play well with null values!!!!!

```
  companion object {
        private val stateKey: String = "StateKey"

        val saver = mapSaver(
                save = {
                    val value = it.userClickedState ?: ""
                    mapOf(stateKey to value)
                },
                restore = { storedMap ->
                    val stored=storedMap[stateKey] as String
                    val value=if(stored=="") null else stored
                    UsersPageState().apply { userClickedState =  value }
                }
        )
    }
```

then use it in Composable function

* remember that param name is stateSaver!!!!

```
@Composable
fun AppBody() {
    Surface(color = Color.Yellow) {
        val usersPageState by rememberSaveable(stateSaver = UsersPageState.saver) {
            mutableStateOf(UsersPageState())
        }

        UserList(usersPageState)
    }
}
```

**Fixing Width**

```
  modifier = Modifier.width(IntrinsicSize.Max),
  
  gives 
  
  Process: com.wlodar.jug.compose, PID: 7170
    java.lang.IllegalStateException: Asking for intrinsic measurements of SubcomposeLayout layouts is not supported. This includes components that are built on top of SubcomposeLayout, such as lazy lists, BoxWithConstraints, TabRow, etc. To mitigate this:
    - if intrinsic measurements are used to achieve 'match parent' sizing,, consider replacing the parent of the component with a custom layout which controls the order in which children are measured, making intrinsic measurement not needed
```

but following is ok:

```
@Composable
fun UserList(usersPage: UsersPageState, usersRepository: UsersRepository = DI.usersRepository) {

    @Composable
    fun displayHeaders(usersPage: UsersPageState) {
       ...
    }

    @Composable
    fun displayUsersColumn() {
       ...
    }

    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        displayHeaders(usersPage)
        displayUsersColumn()
    }
}
```

**User "Animation"**

This is an example of "a state" which can be stored in composable - view state!

```
fun displayUser(onClick: (User) -> Unit): @Composable (User) -> Unit = { user ->
    var isExpanded by remember { mutableStateOf(false) } // our animation state

    val paddingValue by animateDpAsState( //animation "logic"
            if (isExpanded) 48.dp else 20.dp
    )

    Surface(
            color = Color.Blue,
            modifier = Modifier.padding(vertical = 1.dp)
    ) {
        Text(
                text = "User : ${user.name} ",
                color=Color.White,
                modifier = Modifier
                        .clickable {
                            isExpanded = !isExpanded
                            onClick(user)
                        }
                        .padding(paddingValue)  //animation styling
        )
    }

}
```

**Column manual navigation**

```
@Composable
fun displayColumnNavigation(scrollState: LazyListState, numberOfUsers: Int) {
    //coroutine scope 
    val coroutineScope = rememberCoroutineScope()

//for better readability declare "clicks" function outside composable definitions
    val scrolUp: () -> Unit = {
        coroutineScope.launch {
            scrollState.animateScrollToItem(0)
        }
    }
    val scrolDown: () -> Unit = {
        coroutineScope.launch {
        //scroll state is described below
            scrollState.animateScrollToItem(numberOfUsers)
        }
    }


    Row(
            modifier = Modifier.padding(vertical = 10.dp),
    )
    {
        Button(onClick = scrolUp, modifier = Modifier.padding(end=5.dp)){
            Text("Scroll Up")
        }
        Button(onClick = scrolDown){
            Text("Scroll Down")
        }
    }
}
```

To make it work we need to have control over lazy list scroll state

```
 Column(
            modifier = Modifier.fillMaxWidth()
    ) {
        val users = usersRepository.select().toList()
        val scrollState = rememberLazyListState()
        displayColumnNavigation(scrollState,users.size)
        displayHeaders(usersPage)
        displayUsersColumn(scrollState,users)
    }
    
    ....
    
      @Composable
    fun displayUsersColumn(scrollState: LazyListState, users: List<User>) {
     ......
        LazyColumn(
                state = scrollState,  //<---HERE
                verticalArrangement = Arrangement.Center // just chek in the code
        ) {
        ...
```

## Application theme

Lets return to this one:

```
@Composable
fun SomeGuiApplication() {
    ....
ComposeWorkshopsTheme {  /// <- This

(...)

//it was auto generated
@Composable
fun ComposeWorkshopsTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }

    MaterialTheme(
```

at the top of this file you have color pallete definitionns:&#x20;

```
private val LightColorPalette = lightColors(
    primary = Purple500,
    primaryVariant = Purple700,
    secondary = Teal200

)
```

and aprticular colors are defined in Color.kt

```
import androidx.compose.ui.graphics.Color

val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)
```

now using site such like this one : <https://www.w3schools.com/colors/colors_palettes.asp>&#x20;

we can define here some general colors

```
//remember top add FF at the begining which is (most likely) transparency value FF- visible
val backgroundColor = Color(0xFFFEB236)
val controlsColor = Color(0xFF6b5b95)
```

now you can define second "preview annotation" and define colors inside pallette

```
val yellow = Color(0xFFFEB236)
val purple = Color(0xFF6b5b95)
val pink = Color(0xFFd64161)
val orange = Color(0xFFff7b25)

private val LightColorPalette = lightColors(
    primary = yellow,
    primaryVariant = orange,
    secondary = purple
    )
```

and use those colors directly in the composables

```
Surface(
            color=MaterialTheme.colors.primary,
            modifier = Modifier.padding(vertical = 1.dp)
    ) 
```
