Added layout for Desktop and Tablets
This commit is contained in:
parent
12d23fb790
commit
f19a23cbc8
|
@ -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}"
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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")}"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue