simple screen with lazylist, pull-to-refresh and blur with Haze

This commit is contained in:
2024-08-05 04:56:19 +03:00
parent 2c91f6bb62
commit 3b65d44f2f
8 changed files with 252 additions and 25 deletions
@@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.FadeTransition
import dev.meloda.overseerr.screens.url.presentation.UrlScreen
import dev.meloda.overseerr.screens.main.MainScreen
import dev.meloda.overseerr.theme.AppTheme
import org.koin.compose.KoinContext
@@ -14,7 +14,7 @@ import org.koin.compose.KoinContext
internal fun App() = KoinContext {
AppTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Navigator(UrlScreen()) { navigator ->
Navigator(MainScreen()) { navigator ->
FadeTransition(navigator)
}
}
@@ -0,0 +1,58 @@
package dev.meloda.overseerr.screens.main
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import dev.meloda.overseerr.screens.login.presentation.LoginScreen
import dev.meloda.overseerr.screens.requests.RequestsScreen
import dev.meloda.overseerr.screens.url.presentation.UrlScreen
class MainScreen : Screen {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
Scaffold(
topBar = {
TopAppBar(title = { Text(text = "Main screen") })
}
) { padding ->
Row(
modifier = Modifier
.padding(padding)
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
Button(
onClick = { navigator.push(UrlScreen()) },
modifier = Modifier.weight(0.3f)
) {
Text(text = "Url")
}
Button(
onClick = { navigator.push(LoginScreen()) },
modifier = Modifier.weight(0.3f)
) {
Text(text = "Login")
}
Button(
onClick = { navigator.push(RequestsScreen()) },
modifier = Modifier.weight(0.3f)
) {
Text(text = "Requests")
}
}
}
}
}
@@ -0,0 +1,143 @@
package dev.meloda.overseerr.screens.requests
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material3.*
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator
import androidx.compose.material3.pulltorefresh.pullToRefresh
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.haze
import dev.chrisbanes.haze.hazeChild
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds
class RequestsScreen : Screen {
@OptIn(
ExperimentalMaterial3Api::class,
ExperimentalHazeMaterialsApi::class
)
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
var isRefreshing by remember {
mutableStateOf(false)
}
val hazeState = remember { HazeState() }
val hazeStyle = HazeMaterials.ultraThin()
val refreshState = rememberPullToRefreshState()
var isNeedToRefresh by remember {
mutableStateOf(false)
}
LaunchedEffect(isNeedToRefresh) {
if (isNeedToRefresh) {
isRefreshing = true
delay(2.seconds)
isRefreshing = false
isNeedToRefresh = false
}
}
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = { Text(text = "Requests") },
navigationIcon = {
IconButton(onClick = navigator::pop) {
Icon(
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
contentDescription = null
)
}
},
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
modifier = Modifier
.hazeChild(
state = hazeState,
style = hazeStyle
).fillMaxWidth()
)
}
) { padding ->
val bottomPadding = padding.calculateBottomPadding()
Box(
modifier = Modifier
.fillMaxSize()
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
.padding(end = padding.calculateEndPadding(LayoutDirection.Ltr))
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.haze(
state = hazeState,
style = hazeStyle
)
.pullToRefresh(
isRefreshing = isRefreshing,
state = refreshState,
onRefresh = { isNeedToRefresh = true }
)
) {
item {
Spacer(modifier = Modifier.height(padding.calculateTopPadding()))
}
items(count = 1000) { index ->
Text(
text = "Text #${index + 1}",
style = MaterialTheme.typography.headlineLarge,
modifier = Modifier.background(Color.Red)
)
Spacer(modifier = Modifier.height(64.dp))
}
item {
Spacer(modifier = Modifier.height(bottomPadding))
}
}
if (bottomPadding.value > 0) {
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.hazeChild(
state = hazeState,
style = hazeStyle
)
.background(Color.Transparent)
.height(bottomPadding)
.fillMaxWidth()
)
}
Indicator(
state = refreshState,
isRefreshing = isRefreshing,
modifier = Modifier.align(Alignment.TopCenter)
.padding(top = padding.calculateTopPadding())
)
}
}
}
}
@@ -2,5 +2,10 @@ package dev.meloda.overseerr.screens.url
import androidx.lifecycle.ViewModel
class UrlViewModel : ViewModel() {
interface UrlViewModel {
}
class UrlViewModelImpl : ViewModel(), UrlViewModel {
}
@@ -1,37 +1,54 @@
package dev.meloda.overseerr.screens.url.presentation
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.LayoutDirection
import androidx.lifecycle.viewmodel.compose.viewModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import dev.meloda.overseerr.screens.login.presentation.LoginScreen
import dev.meloda.overseerr.screens.url.UrlViewModel
import dev.meloda.overseerr.screens.url.UrlViewModelImpl
class UrlScreen : Screen {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val viewModel: UrlViewModel = viewModel { UrlViewModel() }
val viewModel: UrlViewModel = viewModel { UrlViewModelImpl() }
Scaffold(modifier = Modifier.fillMaxSize()) { padding ->
Column(
modifier = Modifier.fillMaxSize()
.padding(padding)
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = { Text(text = "Url") },
navigationIcon = {
IconButton(onClick = navigator::pop) {
Icon(
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
contentDescription = null
)
}
}
)
}
) { padding ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
.padding(end = padding.calculateEndPadding(LayoutDirection.Ltr))
.padding(bottom = padding.calculateBottomPadding())
) {
Text(text = "Input url screen")
Button(
onClick = {
navigator.push(LoginScreen())
}
onClick = { navigator.push(LoginScreen()) }
) {
Text(text = "Next")
}
+1 -1
View File
@@ -15,7 +15,7 @@ fun main() = application {
Window(
title = "Overseerr",
state = rememberWindowState(width = 800.dp, height = 600.dp),
onCloseRequest = ::exitApplication,
onCloseRequest = ::exitApplication
) {
window.minimumSize = Dimension(350, 600)
App()