Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ kotlin {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.material3)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app.academy.utils

import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp

@Composable
actual fun getWindowSize(): DpSize {
val screen = LocalConfiguration.current
return DpSize(screen.screenWidthDp.dp, screen.screenHeightDp.dp)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package app.academy.components

import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SearchBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.academy.model.Note
import app.academy.utils.getWindowSize

@OptIn(ExperimentalMaterial3Api::class)
@ExperimentalFoundationApi
@Composable
fun AnimatedSearchBar(
query: String,
isSearchBarActive: Boolean,
onQueryChange: (String) -> Unit,
onClearSearchQueryButtonClick: () -> Unit,
onActiveChange: (Boolean) -> Unit,
onBackButtonClick: () -> Unit,
onNoteDismissed: (Note) -> Unit,
onNoteItemClick: (Note) -> Unit,
modifier: Modifier = Modifier,
suggestionsForQuery: List<Note>
) {
val leadingIcon = @Composable {
AnimatedContent(targetState = isSearchBarActive, label = "") { isActive ->
if (isActive) {
IconButton(onClick = onBackButtonClick) {
Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = null)
}
} else {
Icon(imageVector = Icons.Filled.Search, contentDescription = null)
}
}
}

val trailingIcon = @Composable {
AnimatedContent(targetState = isSearchBarActive, label = "") { isActive ->
if (isActive) {
IconButton(
onClick = onClearSearchQueryButtonClick,
content = { Icon(imageVector = Icons.Filled.Close, contentDescription = null) }
)
}
}
}
val windowSizeWithoutInsets = getWindowSize()
Column(modifier = modifier) {
SearchBar(
modifier = Modifier
.align(Alignment.CenterHorizontally)
.sizeIn(
// need sizeIn modifier to prevent exception being thrown about size being set to infinity when search bar is expanded
maxWidth = windowSizeWithoutInsets.width,
maxHeight = windowSizeWithoutInsets.height
),
query = query,
onQueryChange = onQueryChange,
onSearch = {
// no need for this callback because this app uses instant search
},
active = isSearchBarActive,
onActiveChange = onActiveChange,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
placeholder = { Text(text = "Search notes") },
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(top = 8.dp)
) {
NoteItems(
notes = suggestionsForQuery,
onClick = onNoteItemClick,
onDismissed = onNoteDismissed
)
}
}
}
}
45 changes: 44 additions & 1 deletion composeApp/src/commonMain/kotlin/app/academy/ui/HomeScreen.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,60 @@
package app.academy.ui

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.academy.components.AnimatedSearchBar
import app.academy.components.NoteItems
import app.academy.model.dummyNotes
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow

object HomeScreen : Screen {
@OptIn(ExperimentalFoundationApi::class)
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
var currentSearchQuery by remember { mutableStateOf("") }
var isSearchBarActive by remember { mutableStateOf(false) }
LazyColumn(Modifier.fillMaxSize()) {
item {
// Do not add status bars padding to AnimatedSearchBar.
// If the padding is added, then the search bar wouldn't
// fill the entire screen when it is expanded.
AnimatedSearchBar(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp),
query = currentSearchQuery,
isSearchBarActive = isSearchBarActive,
onQueryChange = {
currentSearchQuery = it
// TODO call search
},
onBackButtonClick = { isSearchBarActive = false },
onActiveChange = { isSearchBarActive = it },
onClearSearchQueryButtonClick = {
currentSearchQuery = ""
// TODO call search
},
suggestionsForQuery = emptyList(),// TODO fetch via search ui state
onNoteDismissed = {
//TODO note dismiss
},
onNoteItemClick = {
//TODO on note click
}
)
}
NoteItems(emptyList(), onClick = {}) {
//onDismissed
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package app.academy.utils

import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.DpSize

@Composable
expect fun getWindowSize(): DpSize
20 changes: 20 additions & 0 deletions composeApp/src/iosMain/kotlin/app/academy/utils/WindowSize.ios.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package app.academy.utils

import androidx.compose.runtime.Composable
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import kotlinx.cinterop.ExperimentalForeignApi
import platform.UIKit.UIScreen

@Composable
@OptIn(ExperimentalComposeUiApi::class)
actual fun getWindowSize(): DpSize {
val screen = LocalWindowInfo.current.containerSize
val width = screen.width
val height = screen.height
// Convert points to dp
val density = UIScreen.mainScreen.scale
return DpSize((width / density).dp, (height / density).dp)
}
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ androidx-constraintlayout = "2.1.4"
androidx-core-ktx = "1.13.1"
androidx-espresso-core = "3.5.1"
androidx-material = "1.12.0"
material3 = "1.0.1"
androidx-test-junit = "1.1.5"
compose-plugin = "1.6.10"
core = "1.5.0"
Expand All @@ -28,7 +29,6 @@ runtime = "2.0.0"
sqlDelight = "2.0.1"
material3Android = "1.2.1"
lifecycleViewmodelCompose = "2.7.0"
material3 = "1.2.1"
kotlinVersion = "2.0.0"
voyager = "1.0.0"

Expand All @@ -52,7 +52,7 @@ logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor" }
ktor-server-tests = { module = "io.ktor:ktor-server-tests-jvm", version.ref = "ktor" }
androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" }
androidx-material3-android = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqlDelight" }
koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
Expand Down