Added layout for Desktop and Tablets

This commit is contained in:
dankito 2024-09-05 05:25:42 +02:00
parent 12d23fb790
commit f19a23cbc8
10 changed files with 144 additions and 30 deletions

View File

@ -1,12 +1,29 @@
package net.codinux.banking.ui
import android.os.Build
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
actual val Dispatchers.IOorDefault: CoroutineDispatcher
get() = Dispatchers.IO
@Composable
actual fun rememberScreenSizeInfo(): ScreenSizeInfo {
val config = LocalConfiguration.current
return remember(config) {
ScreenSizeInfo(
heightDp = config.screenHeightDp.dp,
widthDp = config.screenWidthDp.dp
)
}
}
class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}"

View File

@ -35,8 +35,15 @@ fun App() {
// the same values as in BottomBar, but LocalContentColor.current and LocalContentAlpha.current have a different value there
val snackbarTextColor = MaterialTheme.colors.onPrimary.copy(alpha = 0.74f) // 0.74f = ContentAlpha.HighContrastContentAlpha.medium
val screenSizeInfo = rememberScreenSizeInfo()
val isMobile = derivedStateOf { screenSizeInfo.getUiType() == UiType.COMPACT }.value
// we want to place the FAB half size into the BottomBar so we have to add some top padding to it
val fabPositionAdjustment = 44.dp // FabSpacing = 16.dp + FAB height (= 56.dp) / 2
val fabPositionAdjustment = if (isMobile) 44.dp // FabSpacing = 16.dp + FAB height (= 56.dp) / 2
else (-10).dp
val desktopDrawerWidth = 350.dp
val uiState = DI.uiState
@ -46,17 +53,15 @@ fun App() {
val snackbarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
var isInitialized by remember { mutableStateOf(false) }
coroutineScope.launch {
DI.init()
}
val coroutineScope = rememberCoroutineScope()
MaterialTheme(colors = colors, typography = typography) {
Scaffold(
scaffoldState = rememberScaffoldState(drawerState, snackbarHostState),
bottomBar = { BottomBar() },
bottomBar = { if (isMobile) BottomBar() else null },
backgroundColor = Colors.Zinc100,
floatingActionButton = {
FloatingActionButton(
@ -67,14 +72,15 @@ fun App() {
Icon(Icons.Filled.Add, contentDescription = "Add a bank account")
}
},
drawerContent = { SideMenuContent() },
drawerContent = { if (isMobile) SideMenuContent() else null },
drawerBackgroundColor = Colors.DrawerContentBackground,
snackbarHost = { snackbarHostState ->
SnackbarHost(
hostState = snackbarHostState
) { data ->
Snackbar(
modifier = Modifier.offset(y = fabPositionAdjustment - 4.dp).padding(horizontal = 12.dp),
modifier = Modifier.offset(y = fabPositionAdjustment - 4.dp)
.let { if (isMobile) it.padding(horizontal = 12.dp) else it.padding(start = desktopDrawerWidth + 6.dp, end = 2.dp) },
action = { if (data.actionLabel == null) null else {
TextButton(
onClick = { data.performAction() },
@ -88,7 +94,36 @@ fun App() {
}
}
) { scaffoldPadding ->
AppContent(scaffoldPadding, uiState, uiSettings, snackbarHostState)
if (isMobile) {
AppContent(scaffoldPadding, uiState, uiSettings, snackbarHostState, isMobile)
} else {
Row(Modifier.fillMaxSize()) {
Column(Modifier.width(desktopDrawerWidth)) {
SideMenuContent()
}
Column(Modifier.fillMaxSize().weight(1f).padding(start = 6.dp)) {
Row(Modifier.fillMaxWidth().weight(1f)) {
AppContent(scaffoldPadding, uiState, uiSettings, snackbarHostState, isMobile)
}
Row(Modifier.fillMaxWidth()) {
BottomBar(false)
}
}
}
}
}
}
LaunchedEffect(isInitialized) {
if (isInitialized == false) {
isInitialized = true
coroutineScope.launch {
DI.init()
}
}
}
}

View File

@ -1,11 +1,37 @@
package net.codinux.banking.ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
expect val Dispatchers.IOorDefault: CoroutineDispatcher
@Composable
expect fun rememberScreenSizeInfo(): ScreenSizeInfo
data class ScreenSizeInfo(
val heightDp: Dp,
val widthDp: Dp
)
fun ScreenSizeInfo.getUiType(): UiType {
return when (widthDp){
in 0.dp..600.dp -> UiType.COMPACT
in 600.dp..840.dp -> UiType.MEDIUM
else -> UiType.EXPANDED
}
}
enum class UiType {
COMPACT,
MEDIUM,
EXPANDED
}
expect fun getPlatform(): Platform
interface Platform {

View File

@ -15,11 +15,12 @@ fun AppContent(
scaffoldPadding: PaddingValues,
uiState: UiState,
uiSettings: UiSettings,
snackbarHostState: SnackbarHostState
snackbarHostState: SnackbarHostState,
isMobile: Boolean
) {
Column(Modifier.fillMaxSize().padding(scaffoldPadding), horizontalAlignment = Alignment.CenterHorizontally) {
TransactionsList(uiState, uiSettings)
TransactionsList(uiState, uiSettings, isMobile)
StateHandler(uiState, snackbarHostState)
}

View File

@ -38,7 +38,7 @@ private val uiState = DI.uiState
private val IconWidth = 48.dp
@Composable
fun BottomBar() {
fun BottomBar(showMenuDrawer: Boolean = true) {
val userAccounts by uiState.userAccounts.collectAsState()
val transactionsFilter by uiState.transactionsFilter.collectAsState()
@ -50,20 +50,17 @@ fun BottomBar() {
val coroutineScope = rememberCoroutineScope()
val configuration = LocalViewConfiguration.current
println("config: $configuration")
val windowInfo = LocalWindowInfo.current
println("windowInfo: $windowInfo")
BottomAppBar(modifier = Modifier.background(Colors.Accent)) {
IconButton(
onClick = { coroutineScope.launch {
uiState.drawerState.value.toggle()
} }
) {
Icon(Icons.Filled.Menu, contentDescription = "Open Navigation Drawer with sidebar menu")
BottomAppBar {
if (showMenuDrawer) {
IconButton(
onClick = { coroutineScope.launch {
uiState.drawerState.value.toggle()
} }
) {
Icon(Icons.Filled.Menu, contentDescription = "Open Navigation Drawer with sidebar menu")
}
} else {
Spacer(Modifier.width(6.dp)) // or show app icon?
}
Row(Modifier.fillMaxWidth().padding(end = 64.dp)) { // 72.dp = leave space for Floating Action Button

View File

@ -57,7 +57,7 @@ fun SideMenuContent() {
val coroutineScope = rememberCoroutineScope()
Column(Modifier.verticalScroll(ScrollState(0), enabled = true)) {
Column(Modifier.background(Colors.DrawerContentBackground).verticalScroll(ScrollState(0), enabled = true)) {
Column(Modifier.fillMaxWidth().height(HeaderHeight.dp).background(HeaderBackground).padding(16.dp)) {
Spacer(Modifier.weight(1f))

View File

@ -23,7 +23,7 @@ private val calculator = DI.calculator
private val formatUtil = DI.formatUtil
@Composable
fun TransactionsList(uiState: UiState, uiSettings: UiSettings) {
fun TransactionsList(uiState: UiState, uiSettings: UiSettings, isMobile: Boolean = true) {
val userAccounts by uiState.userAccounts.collectAsState()
val userAccountsId by remember(userAccounts) {
derivedStateOf { userAccounts.associateBy { it.id } }
@ -46,7 +46,7 @@ fun TransactionsList(uiState: UiState, uiSettings: UiSettings) {
val transactionsListModifier = Modifier.fillMaxSize()
Column(Modifier.fillMaxSize().padding(horizontal = 8.dp)) {
Column(Modifier.fillMaxSize().let { if (isMobile) it.padding(horizontal = 8.dp) else it.padding(end = 2.dp) }) {
Row(Modifier.fillMaxWidth().height(32.dp).background(Colors.Zinc200), verticalAlignment = Alignment.CenterVertically) {
Text("${transactionsToDisplay.size} Umsätze")
Spacer(Modifier.weight(1f))

View File

@ -1,11 +1,30 @@
package net.codinux.banking.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
actual val Dispatchers.IOorDefault: CoroutineDispatcher
get() = Dispatchers.IO
@OptIn(ExperimentalComposeUiApi::class)
@Composable
actual fun rememberScreenSizeInfo(): ScreenSizeInfo {
val density = LocalDensity.current
val config = LocalWindowInfo.current.containerSize
return remember(density, config) {
ScreenSizeInfo(
heightDp = with(density) { config.height.toDp() },
widthDp = with(density) { config.width.toDp() }
)
}
}
class JVMPlatform: Platform {
override val name: String = "Java ${System.getProperty("java.version")}"

View File

@ -24,7 +24,7 @@ fun main() = application {
onCloseRequest = ::exitApplication,
title = "Bankmeister",
icon = painterResource(Res.drawable.AppIcon_svg),
state = WindowState(position = WindowPosition(Alignment.Center), size = DpSize(900.dp, 800.dp)),
state = WindowState(position = WindowPosition(Alignment.Center), size = DpSize(1000.dp, 800.dp)),
) {
File("data/db").mkdirs()
DI.setRepository(JdbcSqliteDriver("jdbc:sqlite:data/db/Bankmeister.db").apply {

View File

@ -1,11 +1,30 @@
package net.codinux.banking.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
actual val Dispatchers.IOorDefault: CoroutineDispatcher
get() = Dispatchers.Default
@OptIn(ExperimentalComposeUiApi::class)
@Composable
actual fun rememberScreenSizeInfo(): ScreenSizeInfo {
val density = LocalDensity.current
val config = LocalWindowInfo.current.containerSize
return remember(density, config) {
ScreenSizeInfo(
heightDp = with(density) { config.height.toDp() },
widthDp = with(density) { config.width.toDp() }
)
}
}
class JsPlatform: Platform {
override val name: String = "Web with Kotlin/Js"