Added layout for Desktop and Tablets
This commit is contained in:
parent
12d23fb790
commit
f19a23cbc8
|
@ -1,12 +1,29 @@
|
||||||
package net.codinux.banking.ui
|
package net.codinux.banking.ui
|
||||||
|
|
||||||
import android.os.Build
|
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.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
actual val Dispatchers.IOorDefault: CoroutineDispatcher
|
actual val Dispatchers.IOorDefault: CoroutineDispatcher
|
||||||
get() = Dispatchers.IO
|
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 {
|
class AndroidPlatform : Platform {
|
||||||
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,15 @@ fun App() {
|
||||||
// the same values as in BottomBar, but LocalContentColor.current and LocalContentAlpha.current have a different value there
|
// 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 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
|
// 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
|
val uiState = DI.uiState
|
||||||
|
|
||||||
|
@ -46,17 +53,15 @@ fun App() {
|
||||||
|
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
var isInitialized by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
coroutineScope.launch {
|
val coroutineScope = rememberCoroutineScope()
|
||||||
DI.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MaterialTheme(colors = colors, typography = typography) {
|
MaterialTheme(colors = colors, typography = typography) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
scaffoldState = rememberScaffoldState(drawerState, snackbarHostState),
|
scaffoldState = rememberScaffoldState(drawerState, snackbarHostState),
|
||||||
bottomBar = { BottomBar() },
|
bottomBar = { if (isMobile) BottomBar() else null },
|
||||||
backgroundColor = Colors.Zinc100,
|
backgroundColor = Colors.Zinc100,
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
|
@ -67,14 +72,15 @@ fun App() {
|
||||||
Icon(Icons.Filled.Add, contentDescription = "Add a bank account")
|
Icon(Icons.Filled.Add, contentDescription = "Add a bank account")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
drawerContent = { SideMenuContent() },
|
drawerContent = { if (isMobile) SideMenuContent() else null },
|
||||||
drawerBackgroundColor = Colors.DrawerContentBackground,
|
drawerBackgroundColor = Colors.DrawerContentBackground,
|
||||||
snackbarHost = { snackbarHostState ->
|
snackbarHost = { snackbarHostState ->
|
||||||
SnackbarHost(
|
SnackbarHost(
|
||||||
hostState = snackbarHostState
|
hostState = snackbarHostState
|
||||||
) { data ->
|
) { data ->
|
||||||
Snackbar(
|
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 {
|
action = { if (data.actionLabel == null) null else {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = { data.performAction() },
|
onClick = { data.performAction() },
|
||||||
|
@ -88,7 +94,36 @@ fun App() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { scaffoldPadding ->
|
) { 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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,37 @@
|
||||||
package net.codinux.banking.ui
|
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.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
|
||||||
expect val Dispatchers.IOorDefault: CoroutineDispatcher
|
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
|
expect fun getPlatform(): Platform
|
||||||
|
|
||||||
interface Platform {
|
interface Platform {
|
||||||
|
|
|
@ -15,11 +15,12 @@ fun AppContent(
|
||||||
scaffoldPadding: PaddingValues,
|
scaffoldPadding: PaddingValues,
|
||||||
uiState: UiState,
|
uiState: UiState,
|
||||||
uiSettings: UiSettings,
|
uiSettings: UiSettings,
|
||||||
snackbarHostState: SnackbarHostState
|
snackbarHostState: SnackbarHostState,
|
||||||
|
isMobile: Boolean
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Column(Modifier.fillMaxSize().padding(scaffoldPadding), horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(Modifier.fillMaxSize().padding(scaffoldPadding), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
TransactionsList(uiState, uiSettings)
|
TransactionsList(uiState, uiSettings, isMobile)
|
||||||
|
|
||||||
StateHandler(uiState, snackbarHostState)
|
StateHandler(uiState, snackbarHostState)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ private val uiState = DI.uiState
|
||||||
private val IconWidth = 48.dp
|
private val IconWidth = 48.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BottomBar() {
|
fun BottomBar(showMenuDrawer: Boolean = true) {
|
||||||
val userAccounts by uiState.userAccounts.collectAsState()
|
val userAccounts by uiState.userAccounts.collectAsState()
|
||||||
|
|
||||||
val transactionsFilter by uiState.transactionsFilter.collectAsState()
|
val transactionsFilter by uiState.transactionsFilter.collectAsState()
|
||||||
|
@ -50,14 +50,8 @@ fun BottomBar() {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
|
||||||
val configuration = LocalViewConfiguration.current
|
BottomAppBar {
|
||||||
println("config: $configuration")
|
if (showMenuDrawer) {
|
||||||
|
|
||||||
val windowInfo = LocalWindowInfo.current
|
|
||||||
println("windowInfo: $windowInfo")
|
|
||||||
|
|
||||||
|
|
||||||
BottomAppBar(modifier = Modifier.background(Colors.Accent)) {
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { coroutineScope.launch {
|
onClick = { coroutineScope.launch {
|
||||||
uiState.drawerState.value.toggle()
|
uiState.drawerState.value.toggle()
|
||||||
|
@ -65,6 +59,9 @@ fun BottomBar() {
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Filled.Menu, contentDescription = "Open Navigation Drawer with sidebar menu")
|
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
|
Row(Modifier.fillMaxWidth().padding(end = 64.dp)) { // 72.dp = leave space for Floating Action Button
|
||||||
val color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
|
val color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
|
||||||
|
|
|
@ -57,7 +57,7 @@ fun SideMenuContent() {
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
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)) {
|
Column(Modifier.fillMaxWidth().height(HeaderHeight.dp).background(HeaderBackground).padding(16.dp)) {
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ private val calculator = DI.calculator
|
||||||
private val formatUtil = DI.formatUtil
|
private val formatUtil = DI.formatUtil
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TransactionsList(uiState: UiState, uiSettings: UiSettings) {
|
fun TransactionsList(uiState: UiState, uiSettings: UiSettings, isMobile: Boolean = true) {
|
||||||
val userAccounts by uiState.userAccounts.collectAsState()
|
val userAccounts by uiState.userAccounts.collectAsState()
|
||||||
val userAccountsId by remember(userAccounts) {
|
val userAccountsId by remember(userAccounts) {
|
||||||
derivedStateOf { userAccounts.associateBy { it.id } }
|
derivedStateOf { userAccounts.associateBy { it.id } }
|
||||||
|
@ -46,7 +46,7 @@ fun TransactionsList(uiState: UiState, uiSettings: UiSettings) {
|
||||||
val transactionsListModifier = Modifier.fillMaxSize()
|
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) {
|
Row(Modifier.fillMaxWidth().height(32.dp).background(Colors.Zinc200), verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text("${transactionsToDisplay.size} Umsätze")
|
Text("${transactionsToDisplay.size} Umsätze")
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
|
|
@ -1,11 +1,30 @@
|
||||||
package net.codinux.banking.ui
|
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.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
actual val Dispatchers.IOorDefault: CoroutineDispatcher
|
actual val Dispatchers.IOorDefault: CoroutineDispatcher
|
||||||
get() = Dispatchers.IO
|
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 {
|
class JVMPlatform: Platform {
|
||||||
override val name: String = "Java ${System.getProperty("java.version")}"
|
override val name: String = "Java ${System.getProperty("java.version")}"
|
||||||
|
|
|
@ -24,7 +24,7 @@ fun main() = application {
|
||||||
onCloseRequest = ::exitApplication,
|
onCloseRequest = ::exitApplication,
|
||||||
title = "Bankmeister",
|
title = "Bankmeister",
|
||||||
icon = painterResource(Res.drawable.AppIcon_svg),
|
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()
|
File("data/db").mkdirs()
|
||||||
DI.setRepository(JdbcSqliteDriver("jdbc:sqlite:data/db/Bankmeister.db").apply {
|
DI.setRepository(JdbcSqliteDriver("jdbc:sqlite:data/db/Bankmeister.db").apply {
|
||||||
|
|
|
@ -1,11 +1,30 @@
|
||||||
package net.codinux.banking.ui
|
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.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
actual val Dispatchers.IOorDefault: CoroutineDispatcher
|
actual val Dispatchers.IOorDefault: CoroutineDispatcher
|
||||||
get() = Dispatchers.Default
|
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 {
|
class JsPlatform: Platform {
|
||||||
override val name: String = "Web with Kotlin/Js"
|
override val name: String = "Web with Kotlin/Js"
|
||||||
|
|
Loading…
Reference in New Issue