From 441b76c7caf07f42766693c284557a476254160b Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Sun, 14 Jan 2024 02:42:09 +0800 Subject: [PATCH] feat: update demo --- .../flexiui/demo/bean/SimpleListBean.kt | 43 +++++ .../flexiui/demo/screen/LazyListScreen.kt | 167 +++++++++++++++--- .../flexiui/demo/screen/MainScreen.kt | 29 +-- .../flexiui/demo/screen/SecondaryScreen.kt | 33 ++-- 4 files changed, 223 insertions(+), 49 deletions(-) create mode 100644 samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/bean/SimpleListBean.kt diff --git a/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/bean/SimpleListBean.kt b/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/bean/SimpleListBean.kt new file mode 100644 index 0000000..68236c8 --- /dev/null +++ b/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/bean/SimpleListBean.kt @@ -0,0 +1,43 @@ +/* + * Flexi UI - A flexible and useful UI component library. + * Copyright (C) 2019-2024 HighCapable + * https://github.com/BetterAndroid/FlexiUI + * + * Apache License Version 2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is created by fankes on 2024/1/13. + */ +package com.highcapable.flexiui.demo.bean + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable + +@Immutable +data class SimpleListBean( + val index: Int, + val title: String, + val subtitle: String +) { + + companion object { + + @Stable + fun create(index: Int) = SimpleListBean( + index = index, + title = "Item $index", + subtitle = "This is a simple data of index $index" + ) + } +} \ No newline at end of file diff --git a/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/LazyListScreen.kt b/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/LazyListScreen.kt index 644abfd..245a13d 100644 --- a/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/LazyListScreen.kt +++ b/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/LazyListScreen.kt @@ -24,23 +24,46 @@ package com.highcapable.flexiui.demo.screen import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.rememberLazyGridState +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.unit.dp import com.highcapable.betterandroid.compose.multiplatform.backpress.BackHandler +import com.highcapable.flexiui.component.Button +import com.highcapable.flexiui.component.DropdownMenu +import com.highcapable.flexiui.component.DropdownMenuItem +import com.highcapable.flexiui.component.HorizontalItemBox import com.highcapable.flexiui.component.Icon import com.highcapable.flexiui.component.Scaffold import com.highcapable.flexiui.component.SecondaryAppBar import com.highcapable.flexiui.component.Tab import com.highcapable.flexiui.component.TabRow import com.highcapable.flexiui.component.Text +import com.highcapable.flexiui.component.window.FlexiDialog +import com.highcapable.flexiui.demo.Delete import com.highcapable.flexiui.demo.DeleteForever import com.highcapable.flexiui.demo.ListAdd +import com.highcapable.flexiui.demo.PrimarySpacer +import com.highcapable.flexiui.demo.bean.SimpleListBean import com.highcapable.flexiui.demo.rememberRouter import com.highcapable.flexiui.resources.FlexiIcons import kotlinx.coroutines.launch @@ -49,54 +72,154 @@ import kotlinx.coroutines.launch fun LazyListScreen() { val router = rememberRouter() val pageCount = 2 - val state = rememberPagerState(pageCount = { pageCount }) + val pagerState = rememberPagerState(pageCount = { pageCount }) + val lazyListState = rememberLazyListState() + val lazyGridState = rememberLazyGridState() val scope = rememberCoroutineScope() + val testListData = remember { + mutableStateListOf().apply { + for (i in 1..5) add(SimpleListBean.create(i)) + } + } Scaffold( appBar = { + var showRemoveAllDialog by remember { mutableStateOf(false) } + FlexiDialog( + visible = showRemoveAllDialog, + onDismissRequest = { showRemoveAllDialog = false }, + title = { Text("Remove All") }, + content = { Text("Are you sure you want to remove all data?") }, + confirmButton = { + Button( + onClick = { + showRemoveAllDialog = false + testListData.clear() + } + ) { Text("OK") } + }, + cancelButton = { + Button( + onClick = { showRemoveAllDialog = false } + ) { Text("Cancel") } + } + ) SecondaryAppBar( title = { Text("Lazy List Demo") }, - subtitle = { Text("0 items of list data", singleLine = true) }, + subtitle = { Text("${testListData.size} items of list data", singleLine = true) }, navigationIcon = { NavigationIconButton(onClick = { router.goHome() }) }, actions = { - ActionIconButton(onClick = { /* TODO */ }) { - Icon(FlexiIcons.ListAdd) - } - ActionIconButton(onClick = { /* TODO */ }) { - Icon(FlexiIcons.DeleteForever) - } + ActionIconButton( + onClick = { + val lastIndex = if (testListData.isNotEmpty()) + testListData[testListData.lastIndex].index + 1 + else 1 + testListData.add(SimpleListBean.create(lastIndex)) + scope.launch { + lazyListState.animateScrollToItem(testListData.lastIndex) + lazyGridState.animateScrollToItem(testListData.lastIndex) + } + } + ) { Icon(FlexiIcons.ListAdd) } + ActionIconButton( + onClick = { showRemoveAllDialog = testListData.isNotEmpty() } + ) { Icon(FlexiIcons.DeleteForever) } } ) + BackHandler { router.goHome() } }, tab = { TabRow( - selectedTabIndex = state.currentPage, + selectedTabIndex = pagerState.currentPage, indicator = { - TabIndicator(modifier = Modifier.pagerTabIndicatorOffset(state)) + TabIndicator(modifier = Modifier.pagerTabIndicatorOffset(pagerState)) } ) { Tab( - selected = state.currentPage == 0, - onClick = { scope.launch { state.animateScrollToPage(0) } } + selected = pagerState.currentPage == 0, + onClick = { scope.launch { pagerState.animateScrollToPage(0) } } ) { Text("Linear List") } Tab( - selected = state.currentPage == 1, - onClick = { scope.launch { state.animateScrollToPage(1) } } + selected = pagerState.currentPage == 1, + onClick = { scope.launch { pagerState.animateScrollToPage(1) } } ) { Text("Grid List") } } } - ) { + ) { innerPadding -> HorizontalPager( modifier = Modifier.fillMaxSize(), - state = state, + state = pagerState, + beyondBoundsPageCount = pageCount ) { index -> - // TODO: To be implemented. - Box( - modifier = Modifier.fillMaxSize(), + @Composable + fun LazyItem(modifier: Modifier, index: Int) { + Box(modifier = modifier) { + val hapticFeedback = LocalHapticFeedback.current + var showDropdownMenu by remember { mutableStateOf(false) } + HorizontalItemBox( + onLongClick = { + showDropdownMenu = true + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + }, + title = { Text(testListData[index].title) }, + subtitle = { Text(testListData[index].subtitle) }, + showArrowIcon = false + ) + DropdownMenu( + expanded = showDropdownMenu, + onDismissRequest = { showDropdownMenu = false } + ) { + DropdownMenuItem( + onClick = { testListData.removeAt(index) } + ) { + Icon(FlexiIcons.Delete) + PrimarySpacer() + Text("Remove this data") + } + } + } + } + if (testListData.isNotEmpty()) when (index) { + 0 -> LazyColumn( + modifier = Modifier.fillMaxSize().padding(innerPadding), + state = lazyListState, + verticalArrangement = ListSpacing + ) { + items( + count = testListData.size, + key = { testListData[it].index } + ) { index -> + LazyItem( + modifier = Modifier.animateItemPlacement(), + index = index + ) + } + } + 1 -> LazyVerticalGrid( + modifier = Modifier.fillMaxSize().padding(innerPadding), + state = lazyGridState, + columns = GridCells.Adaptive(GridMaxWidth), + verticalArrangement = ListSpacing, + horizontalArrangement = ListSpacing + ) { + items( + count = testListData.size, + key = { testListData[it].index } + ) { index -> + LazyItem( + modifier = Modifier.animateItemPlacement(), + index = index + ) + } + } + } else Box( + modifier = Modifier.fillMaxSize().padding(innerPadding), contentAlignment = Alignment.Center - ) { Text("Page ${index + 1}. To be implemented.") } + ) { Text("No data to show") } } - BackHandler { router.goHome() } } -} \ No newline at end of file +} + +private val ListSpacing = Arrangement.spacedBy(10.dp) +private val GridMaxWidth = 100.dp \ No newline at end of file diff --git a/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/MainScreen.kt b/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/MainScreen.kt index a3f223c..8722dfb 100644 --- a/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/MainScreen.kt +++ b/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/MainScreen.kt @@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState @@ -78,7 +79,7 @@ import kotlinx.coroutines.launch @Composable fun MainScreen() { val pageCount = 2 - val state = rememberPagerState(pageCount = { pageCount }) + val pagerState = rememberPagerState(pageCount = { pageCount }) val scope = rememberCoroutineScope() val uriHandler = LocalUriHandler.current Scaffold( @@ -117,36 +118,37 @@ fun MainScreen() { arrangement = Arrangement.SpaceAround ) { NavigationBarItem( - selected = state.currentPage == 0, - onClick = { scope.launch { state.animateScrollToPage(page = 0) } }, + selected = pagerState.currentPage == 0, + onClick = { scope.launch { pagerState.animateScrollToPage(page = 0) } }, icon = { Icon(FlexiIcons.Home, style = IconDefaults.style(size = 24.dp)) }, text = { Text("Home") } ) NavigationBarItem( - selected = state.currentPage == 1, - onClick = { scope.launch { state.animateScrollToPage(page = 1) } }, + selected = pagerState.currentPage == 1, + onClick = { scope.launch { pagerState.animateScrollToPage(page = 1) } }, icon = { Icon(FlexiIcons.Component, style = IconDefaults.style(size = 24.dp)) }, text = { Text("Component") } ) } } - ) { + ) { innerPadding -> HorizontalPager( modifier = Modifier.fillMaxSize(), - state = state, + state = pagerState, ) { index -> + val modifier = Modifier.padding(innerPadding) when (index) { - 0 -> MainHomePage() - 1 -> MainComponentPage() + 0 -> MainHomePage(modifier) + 1 -> MainComponentPage(modifier) } } } } @Composable -fun MainHomePage() { +fun MainHomePage(modifier: Modifier) { val scrollState = rememberScrollState() - Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { + Column(modifier = modifier.fillMaxSize().verticalScroll(scrollState)) { AreaBox(modifier = Modifier.fillMaxWidth()) { Text("Flexi UI is a flexible and useful UI component library.") } @@ -213,15 +215,14 @@ fun MainHomePage() { title = { Text("Lazy List Demo") }, subtitle = { Text("Open a lazy list page") } ) - PrimarySpacer() } } @Composable -fun MainComponentPage() { +fun MainComponentPage(modifier: Modifier) { // TODO: To be implemented. Box( - modifier = Modifier.fillMaxSize(), + modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text("To be implemented.") } } \ No newline at end of file diff --git a/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/SecondaryScreen.kt b/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/SecondaryScreen.kt index 271e973..6f88ac1 100644 --- a/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/SecondaryScreen.kt +++ b/samples/composeApp/src/commonMain/kotlin/com/highcapable/flexiui/demo/screen/SecondaryScreen.kt @@ -21,7 +21,9 @@ */ package com.highcapable.flexiui.demo.screen +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.em @@ -46,22 +48,27 @@ fun SecondaryScreen() { NavigationIconButton(onClick = { router.goHome() }) } ) + BackHandler { router.goHome() } } - ) { - AreaColumn(modifier = Modifier.fillMaxWidth()) { - Text( - """ + ) { innerPadding -> + Box( + modifier = Modifier.fillMaxWidth() + .padding(innerPadding) + ) { + AreaColumn(modifier = Modifier.fillMaxWidth()) { + Text( + """ Now, you open a separate secondary page. You can click the button below to back to the homepage. - """.trimIndent(), - style = FlexiTheme.typography.primary.copy(lineHeight = 2.em) - ) - PrimarySpacer() - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { router.goHome() } - ) { Text("Take Me Home") } + """.trimIndent(), + style = FlexiTheme.typography.primary.copy(lineHeight = 2.em) + ) + PrimarySpacer() + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { router.goHome() } + ) { Text("Take Me Home") } + } } - BackHandler { router.goHome() } } } \ No newline at end of file