Giter Site home page Giter Site logo

maxkeppeler / sheets-compose-dialogs Goto Github PK

View Code? Open in Web Editor NEW
744.0 5.0 31.0 266.09 MB

✨ Enhancing Android UIs: A Jetpack Compose Library supporting a wide range of common use-cases with Material Design 3 Dialogs, Popups, and Bottom Sheets. ✨

Home Page: https://maxkeppeler.github.io/sheets-compose-dialogs/

License: Apache License 2.0

Kotlin 99.59% Shell 0.05% Python 0.36%
jetpack-compose-dialogs material-dialogs android clock-picker color-pickers compose dialogs material-you material3 picker-component

sheets-compose-dialogs's Introduction

Sheets-Compose-Dialogs

Sheets Library

An Android library that offers dialogs & views for various use cases - build with Jetpack Compose. All of the dialogs & views are easy and quick to implement. Views can be used for PopUps, BottomSheets and other containers.

Version of Sheets library Codacy code quality of Sheets library Codacy code quality of Sheets library Give this library a star Fork this library Follow me on GitHub Share this library on Twitter Follow Maximilian Keppeler on Twitter

sheets Library

Get started

The library contains various use-cases. Each module represents one use-case and contains a *Dialog and *View. You can use the *Dialog directly while you can use the *View for bottom sheets, popups and other non-scrollable containers.

Play around with the samples via APK.

In your top-level build.gradle

repositories {
  ...
  mavenCentral()
}

In your app build.gradle file:

dependencies {
  ...
  // Implementing the `core` module is mandatory for using other use cases.
  implementation 'com.maxkeppeler.sheets-compose-dialogs:core:<version>' 
  implementation 'com.maxkeppeler.sheets-compose-dialogs:<module>:<version>'
}

Replace <version> with the (latest or preferred) version of the library.
Replace <module> with the module you want to use.
Modules: core | info | color | calendar | rating | clock | duration | date-time | option | list | input | emoji | state

📖 Get a better insight into the API ‏‏‎ ‎‣‏‏‎ ‎ API Documentation
✨ General documentation, resources and more ‏‏‎ ‎‣‏‏‎ ‎ Documentation

Showcase

Take a look at the use-cases presented as dialogs. These same use-cases can also be displayed within a popup, bottom sheet, or any other container view.

Calendar

Module calendar ‎‎ | ‎‎ API Documentation

Light Dark
CalendarSample1
CalendarSample1
CalendarSample2
CalendarSample2
CalendarSample3
CalendarSample3
CalendarSample4
CalendarSample4
CalendarSample5
CalendarSample5

Clock

Module clock ‎‎ | ‎‎ API Documentation

Light Dark
ClockSample1
ClockSample1
ClockSample2
ClockSample2

Color

Module color ‎‎ | ‎‎ API Documentation

Light Dark
ColorSample1
ColorSample1
ColorSample2
ColorSample2
ColorSample3
ColorSample3

Core

Module core ‎‎ | ‎‎ API Documentation

Light Dark
CoreSample1
CoreSample1

Date-Time

Module date-time ‎‎ | ‎‎ API Documentation

Light Dark
DateTimeSample1
DateTimeSample1
DateTimeSample2
DateTimeSample2
DateTimeSample3
DateTimeSample3

Duration

Module duration ‎‎ | ‎‎ API Documentation

Light Dark
DurationSample1
DurationSample1
DurationSample2
DurationSample2

Emoji

Module emoji ‎‎ | ‎‎ API Documentation

Light Dark
EmojiSample1
EmojiSample1
EmojiSample2
EmojiSample2

Info

Module info ‎‎ | ‎‎ API Documentation

Light Dark
InfoSample1
InfoSample1

Input

Module input ‎‎ | ‎‎ API Documentation

Light Dark
InputSample1
InputSample1
InputSample2
InputSample2
InputSample3
InputSample3
InputSample4
InputSample4

List

Module list ‎‎ | ‎‎ API Documentation

Light Dark
ListSample1
ListSample1
ListSample2
ListSample2
ListSample3
ListSample3
ListSample4
ListSample4

Option

Module option ‎‎ | ‎‎ API Documentation

Light Dark
OptionSample1
OptionSample1
OptionSample2
OptionSample2
OptionSample3
OptionSample3

Rating

Module rating ‎‎ | ‎‎ API Documentation

Light Dark
RatingSample1
RatingSample1
RatingSample2
RatingSample2
RatingSample3
RatingSample3
RatingSample4
RatingSample4

State

Module state ‎‎ | ‎‎ API Documentation

Light Dark
StateSample1
StateSample1
StateSample2
StateSample2
StateSample3
StateSample3
StateSample4
StateSample4
StateSample5
StateSample5
StateSample6
StateSample6
StateSample7
StateSample7

Donate

Show your appreciation by donating me a coffee. Thank you very much!

Buy Me a Coffee at ko-fi.com Buy Me A Coffee Donate on PaPal

Showcase

Check out some apps which are using this library.

License

Copyright 2022-2023 Maximilian Keppeler https://maxkeppeler.com

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

   http://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.

sheets-compose-dialogs's People

Contributors

imgbot[bot] avatar maxkeppeler avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

sheets-compose-dialogs's Issues

[API] DEFAULT_RANGE is internal and cannot be used when creating composable functions

Constants.DEFAULT_RANGE is internal, as such, it cannot be used to create composables which wrap the CalendarDialog.

Sample:


@Composable
fun calendarDialog(
    title: String?,
    style: CalendarStyle,
    dateRange: ClosedRange<LocalDate>? = null,
    onSelectDate: (LocalDate) -> Unit,
    initialDate: LocalDate = LocalDate.now(TimeZone.currentSystemDefault()),
): UseCaseState {
    val state = rememberUseCaseState()

    CalendarDialog(
        state = state,
        selection = remember(initialDate) {
            CalendarSelection.Date(
                withButtonView = true,
                selectedDate = initialDate.toJavaLocalDate(),
            ) { onSelectDate(it.toKotlinLocalDate()) }
        },
        config = remember(style, dateRange) {
            CalendarConfig(
                style = style,
                monthSelection = true,
                yearSelection = true,
                boundary = dateRange ?: DEFAULT_RANGE,
                icons = LibIcons.Rounded,
            )
        },
        header = remember(title) { title?.let { Header.Default(title = it) } },
        properties = DialogProperties(),
    )
    return state
}

InputTextField NoSuchMethodError Crash

Seems like using InputTextField as one of the input options for Input Dialog throws NoSuchMethodError.

java.lang.NoSuchMethodError: No static method TextField(Ljava/lang/String;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;ZZLandroidx/compose/ui/text/TextStyle;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ZLandroidx/compose/ui/text/input/VisualTransformation;Landroidx/compose/foundation/text/KeyboardOptions;Landroidx/compose/foundation/text/KeyboardActions;ZILandroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/material3/TextFieldColors;Landroidx/compose/runtime/Composer;III)V in class Landroidx/compose/material3/TextFieldKt; or its super classes (declaration of 'androidx.compose.material3.TextFieldKt' appears in /data/app/~~hWNpS5NQIa0cv5wDFM3ITw==/com.<package-name>-IlNA4GFNg4kloexJLTKSXw==/base.apk)

Current versions:
Compose: 1.3.1
Compose Compiler: 1.4.3
Kotlin: 1.8.10
Dialogs Library: 1.1.0

[Crash] DateTimeException: Invalid value for MinuteOfHour

I received this crash from crashlytics recently. The user somehow managed to input "70" into the minute field while probably trying to input "07:00".
This may be due to a laggy device.

Sheets used in the app version: 1.0.4
Device: Lenovo TAB4 8
Android 8.1

Specs clearly show it's a low-performance device.
Analytics don't indicate that the user made any input into the dialog before crashing, which is weird. Could it be that the initial time (which I set to current system time) somehow was at 70 minutes??
But the stacktrace shows that the crash happened upon tapping a number.
There has been 1 crash for this one user.

Fatal Exception: java.time.DateTimeException: Invalid value for MinuteOfHour (valid values 0 - 59): 70
       at java.time.temporal.ValueRange.checkValidValue(ValueRange.java:311)
       at java.time.temporal.ChronoField.checkValidValue(ChronoField.java:730)
       at java.time.LocalTime.of(LocalTime.java:315)
       at com.maxkeppeler.sheets.clock.utils.UtilsKt.convertTimeTextValuesIntoTime(UtilsKt.java:73)
       at com.maxkeppeler.sheets.clock.ClockState.getTimeOfTextValues(ClockState.kt:74)
       at com.maxkeppeler.sheets.clock.ClockState.refreshTimeValue(ClockState.kt:95)
       at com.maxkeppeler.sheets.clock.ClockState.onEnterValue(ClockState.java:114)
       at com.maxkeppeler.sheets.clock.ClockViewKt$ClockView$1$3.invoke(ClockView.kt:68)
       at com.maxkeppeler.sheets.clock.ClockViewKt$ClockView$1$3.invoke(ClockView.kt:68)
       at com.maxkeppeler.sheets.clock.views.KeyItemComponentKt$KeyItemComponent$2.invoke(KeyItemComponent.kt:102)
       at com.maxkeppeler.sheets.clock.views.KeyItemComponentKt$KeyItemComponent$2.invoke(KeyItemComponent.kt:95)
       at androidx.compose.foundation.ClickableKt$clickable$4$gesture$1$1$2.invoke-k-4lQ0M(Clickable.kt:167)
       at androidx.compose.foundation.ClickableKt$clickable$4$gesture$1$1$2.invoke(Clickable.kt:156)
       at androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:255)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTaskKt.java:178)
       at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTaskKt.java:166)
       at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
       at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
       at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
       at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)
       at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter$PointerEventHandlerCoroutine.offerPointerEvent(SuspendingPointerInputFilter.java:566)
       at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.dispatchPointerEvent(SuspendingPointerInputFilter.kt:456)
       at androidx.compose.ui.Modifier$Node.isKind-H91voCI$ui_release(Modifier.java:201)
       at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.java:120)
       at androidx.compose.ui.node.BackwardsCompatNode.onPointerEvent-H0pRuoY(BackwardsCompatNode.kt:370)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:314)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
       at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
       at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass(HitPathTracker.kt:183)
       at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.java:102)
       at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:98)
       at androidx.compose.ui.platform.AndroidComposeView.sendMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1343)
       at androidx.compose.ui.platform.AndroidComposeView.handleMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1289)
       at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:1228)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2968)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2657)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2968)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2657)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2968)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2657)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2968)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2657)
       at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:448)
       at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1829)
       at android.app.Dialog.dispatchTouchEvent(Dialog.java:815)
       at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:410)
       at android.view.View.dispatchPointerEvent(View.java:12016)
       at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4809)
       at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4623)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4161)
       at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4214)
       at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4180)
       at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4307)
       at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4188)
       at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4364)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4161)
       at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4214)
       at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4180)
       at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4188)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4161)
       at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6682)
       at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6656)
       at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6617)
       at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6785)
       at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:187)
       at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
       at android.os.MessageQueue.next(MessageQueue.java:325)
       at android.os.Looper.loop(Looper.java:142)
       at android.app.ActivityThread.main(ActivityThread.java:6518)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

This is all I have to go with. I can't repro this issue myself.

Prevent user from picking Future Dates with disabledDates CalendarConfig parameter

In the CalendarConfig constructor, there is disabledDates parameter. How can you use it to prevent user from selecting future dates?

Before version 1.1.1 we used a constant - something like this ....

disabledTimeline = CalendarTimeline.FUTURE

However, this is not being resolved on my end when I upgrade to version 1.1.1*
error

I see the disabledDates takes a list of LocalDates so I came up with this work-around but it is not helping.

val startRange = LocalDate.now().minusYears(10) // allow dates up to 1 year in the past
    val disabledDates = startRange..LocalDate.now()
    val disabledDatesList = disabledDates.toList

I will appreciate any leads to hack this.

Cheers.

BadParcelableException is thrown when using lambdas in SheetState

Exception occurs when using the SheetState lambdas as it tries to save and restore the SheetState.
Remove lambdas from saveable data class SheetStateData.

FATAL EXCEPTION: main
                                                                                                    Process: com.mk.wearsocials, PID: 2541
                                                                                                    android.os.BadParcelableException: Parcelable encountered IOException writing serializable object (name = com.maxkeppeker.sheets.core.models.base.SheetState$SheetStateData)
                                                                                                    	at android.os.Parcel.writeSerializable(Parcel.java:2751)
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2517)
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2316)
                                                                                                    	at android.os.Parcel.writeList(Parcel.java:1382)
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2460)
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2316)
                                                                                                    	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265)
                                                                                                    	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802)
                                                                                                    	at android.os.Bundle.writeToParcel(Bundle.java:1389)
                                                                                                    	at android.os.Parcel.writeBundle(Parcel.java:1334)
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2433)
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2323)
                                                                                                    	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265)
                                                                                                    	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802)
                                                                                                    	at android.os.Bundle.writeToParcel(Bundle.java:1389)
                                                                                                    	at android.os.Parcel.writeBundle(Parcel.java:1334)
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2433)
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2323)
                                                                                                    	at android.os.BaseBundle.dumpStats(BaseBundle.java:1874)
                                                                                                    	at android.os.BaseBundle.dumpStats(BaseBundle.java:1911)
                                                                                                    	at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:150)
                                                                                                    	at android.os.Handler.handleCallback(Handler.java:942)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:201)
                                                                                                    	at android.os.Looper.loop(Looper.java:288)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:7872)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
                                                                                                    Caused by: java.io.NotSerializableException: androidx.compose.runtime.ParcelableSnapshotMutableState
                                                                                                    	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
                                                                                                    	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
                                                                                                    	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
                                                                                                    	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
                                                                                                    	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
                                                                                                    	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
                                                                                                    	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
                                                                                                    	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
                                                                                                    	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
                                                                                                    	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
                                                                                                    	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
                                                                                                    	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
                                                                                                    	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
                                                                                                    	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
                                                                                                    	at android.os.Parcel.writeSerializable(Parcel.java:2746)
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2517) 
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2316) 
                                                                                                    	at android.os.Parcel.writeList(Parcel.java:1382) 
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2460) 
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2316) 
                                                                                                    	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265) 
                                                                                                    	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802) 
                                                                                                    	at android.os.Bundle.writeToParcel(Bundle.java:1389) 
                                                                                                    	at android.os.Parcel.writeBundle(Parcel.java:1334) 
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2433) 
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2323) 
                                                                                                    	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265) 
                                                                                                    	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802) 
                                                                                                    	at android.os.Bundle.writeToParcel(Bundle.java:1389) 
                                                                                                    	at android.os.Parcel.writeBundle(Parcel.java:1334) 
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2433) 
                                                                                                    	at android.os.Parcel.writeValue(Parcel.java:2323) 
                                                                                                    	at android.os.BaseBundle.dumpStats(BaseBundle.java:1874) 
                                                                                                    	at android.os.BaseBundle.dumpStats(BaseBundle.java:1911) 
                                                                                                    	at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:150) 
                                                                                                    	at android.os.Handler.handleCallback(Handler.java:942) 
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:99) 
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:201) 
                                                                                                    	at android.os.Looper.loop(Looper.java:288) 
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:7872) 
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method) 
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) 

[Crash] [CalendarDialog] Can navigate to disabled months after going back and forth, leading to a crash

To reproduce:

  1. Open a Calendar Dialog, setting CalendarTimeline to PAST
  2. Navigate to the month that's after the current one
  3. Quickly return back while the button is animating away
  4. Go back several months. You are now able to navigate between disabled months
  5. Go back to the year that's disabled. For example, go from Jan 2023 to Dec 2022
  6. Tap on the year selection (2022 text)
  7. Year selection opens but current year is not shown
  8. Tap anywhere
  9. App crashes with the following stacktrace
Fatal Exception: java.lang.IllegalArgumentException: Index should be non-negative (-1)
       at androidx.compose.foundation.lazy.LazyListScrollPosition.update-AhXoVpI(LazyListScrollPosition.kt:101)
       at androidx.compose.foundation.lazy.LazyListState.l(LazyListState.kt:2)
       at androidx.compose.foundation.lazy.LazyListState$scrollToItem$2.invokeSuspend(LazyListState.kt:244)
       at androidx.compose.foundation.lazy.LazyListState$scrollToItem$2.invoke(LazyListState.kt:12)
       at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2$1.invokeSuspend(ScrollableState.kt:178)
       at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2$1.invoke(ScrollableState.kt:12)
       at androidx.compose.foundation.MutatorMutex$mutateWith$2.invokeSuspend(MutatorMutex.kt:160)
       at androidx.compose.foundation.MutatorMutex$mutateWith$2.invoke(MutatorMutex.kt:12)
       at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(UndispatchedKt.java:89)
       at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScopeKt.java:264)
       at androidx.compose.foundation.MutatorMutex.mutateWith(MutatorMutex.kt:153)
       at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2.invokeSuspend(ScrollableState.kt:175)
       at androidx.compose.foundation.gestures.DefaultScrollableState$scroll$2.invoke(ScrollableState.kt:12)
       at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(UndispatchedKt.java:89)
       at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScopeKt.java:264)
       at androidx.compose.foundation.gestures.DefaultScrollableState.scroll(ScrollableState.kt:174)
       at androidx.compose.foundation.lazy.LazyListState.scroll(LazyListState.kt:268)
       at androidx.compose.foundation.gestures.ScrollableState.scroll$default(ScrollableState.java:53)
       at androidx.compose.foundation.lazy.LazyListState.scrollToItem(LazyListState.kt:243)
       at androidx.compose.foundation.lazy.LazyListState.scrollToItem$default(LazyListState.java:238)
       at com.maxkeppeler.sheets.calendar.CalendarViewKt$CalendarView$1.invokeSuspend(CalendarView.kt:72)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
       at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
       at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
       at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run(AndroidUiDispatcher.android.kt:2)
       at android.os.Handler.handleCallback(Handler.java:942)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.app.ActivityThread.main(ActivityThread.java:7913)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:942)

[CalendarDialog] when using disabledTimeline property, disabled days are not shown instead of being disabled

Using disabledTimeline = PAST or FUTURE results in disabled days not being shown in the dialog, which in turn results in the following representation:

Spoiler

Screenshot_20230125-223026

Which looks unsightly. This behavior is not customizable.

Proposed solution: add a parameter that would allow disabled days to show up as "greyed-out".

Update: because of the issue above, for months that are lacking any enabled days, the dialog is completely empty.

[InfoDialog] Body text is misaligned

Just take a look at the screenshot

Screenshot

Screenshot_20230305-220816

InfoDialog(
        state = dialogState,
        header = Header.Default(
            title = "Move",
        ),
        body = InfoBody.Default(
            bodyText = "abc",
        ),
        selection = InfoSelection(
            onPositiveClick = positiveButton.onClick,
            onNegativeClick = negativeButton?.onClick ?: {},
            negativeButton = negativeButton?.let { SelectionButton(it.title, type = ButtonStyle.TEXT) },
            positiveButton = SelectionButton(positiveButton.title, type = ButtonStyle.TEXT),
        ),
        properties = DialogProperties(
            dismissOnClickOutside = dismissOnClickOutside,
            dismissOnBackPress = dismissOnClickOutside,
        ),
    )

Docs problems

  • InfoDialog uses class "Header" which has subTitle, but in reality, Header.Default has no subtitle
  • Datetime artifact name is wrong, it's date-time, not date_time
  • Not all properties of duration dialogs are documented. For example, displayClearButton
  • Notion is a good choice, but the page is super laggy (notion is prone to performance issues). I suggest moving everything into collapsible subheaders.
  • CalendarSample.kt etc. should be clickable links to files, not just text (where do I find them??)

Can't create DateRangePicker inside ModalBottomSheetLayout

I show in your BottomSheetSample.
When I use CalendarView inside BottomSheetSample with CalendarSelection.Dates - all work correctly, but when I try use CalendarSelection.Period - I catch error. It is library bug or I do something wrong?

package com.mk.sheets.compose.samples

import android.util.Range
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import com.maxkeppeker.sheets.core.models.base.rememberSheetState
import com.maxkeppeler.sheets.calendar.CalendarView
import com.maxkeppeler.sheets.calendar.models.CalendarConfig
import com.maxkeppeler.sheets.calendar.models.CalendarSelection
import com.maxkeppeler.sheets.calendar.models.CalendarStyle
import kotlinx.coroutines.launch
import java.time.LocalDate

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheetSample(
    state: ModalBottomSheetState,
    screenContent: @Composable () -> Unit,
) {
    val coroutine = rememberCoroutineScope()
    val hideBottomSheet = { coroutine.launch { state.animateTo(ModalBottomSheetValue.Hidden) } }
    val dialogSheetState =
        rememberSheetState(visible = true, onCloseRequest = { hideBottomSheet(); })

    LaunchedEffect(state.currentValue) {
        when (state.currentValue) {
            ModalBottomSheetValue.Hidden,
            ModalBottomSheetValue.Expanded -> {
                dialogSheetState.invokeReset() // Manually reset internal state if required
            }
            ModalBottomSheetValue.HalfExpanded -> Unit

        }
    }

    ModalBottomSheetLayout(
        content = screenContent,
        sheetState = state,
        sheetContent = {
            val selectedDateRange = remember {
                val value = Range(LocalDate.now().minusDays(20), LocalDate.now().minusDays(10))
                mutableStateOf(value)
            }

            Surface(
                color = MaterialTheme.colorScheme.surface,
            ) {
                CalendarView(
                    sheetState = dialogSheetState,
                    config = CalendarConfig(
                        style = CalendarStyle.MONTH
                    ),
                    selection = CalendarSelection.Period(
                        selectedRange = selectedDateRange.value
                    ) { startDate, endDate ->
                        selectedDateRange.value = Range(startDate, endDate)
                    },
                )
            }
        }
    )
}

StackTrace:

 FATAL EXCEPTION: main
 Process: com.mk.sheets.compose, PID: 19557
 java.lang.IndexOutOfBoundsException: index: 0, size: 0
 	at androidx.compose.runtime.external.kotlinx.collections.immutable.internal.ListImplementation.checkElementIndex$runtime_release(ListImplementation.kt:15)
 	at androidx.compose.runtime.external.kotlinx.collections.immutable.implementations.immutableList.SmallPersistentVector.get(SmallPersistentVector.kt:146)
 	at androidx.compose.runtime.snapshots.SnapshotStateList.get(SnapshotStateList.kt:72)
 	at androidx.compose.runtime.snapshots.SnapshotStateList.set(SnapshotStateList.kt:103)
 	at com.maxkeppeler.sheets.calendar.CalendarState.processSelection(CalendarState.kt:242)
 	at com.maxkeppeler.sheets.calendar.CalendarViewKt$CalendarView$onSelection$1.invoke(CalendarView.kt:59)
 	at com.maxkeppeler.sheets.calendar.CalendarViewKt$CalendarView$onSelection$1.invoke(CalendarView.kt:58)
 	at com.maxkeppeler.sheets.calendar.views.CalendarDateItemComponentKt$CalendarDateItemComponent$normalModifier$1.invoke(CalendarDateItemComponent.kt:95)
 	at com.maxkeppeler.sheets.calendar.views.CalendarDateItemComponentKt$CalendarDateItemComponent$normalModifier$1.invoke(CalendarDateItemComponent.kt:95)
 	at androidx.compose.foundation.ClickableKt$clickable$4$gesture$1$2.invoke-k-4lQ0M(Clickable.kt:153)
 	at androidx.compose.foundation.ClickableKt$clickable$4$gesture$1$2.invoke(Clickable.kt:142)
 	at androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1$1.invokeSuspend(TapGestureDetector.kt:222)
 	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
 	at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:178)
 	at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
 	at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
 	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
 	at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
 	at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)
 	at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter$PointerEventHandlerCoroutine.offerPointerEvent(SuspendingPointerInputFilter.kt:566)
 	at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.dispatchPointerEvent(SuspendingPointerInputFilter.kt:456)
 	at androidx.compose.ui.input.pointer.SuspendingPointerInputFilter.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.kt:469)
 	at androidx.compose.ui.node.BackwardsCompatNode.onPointerEvent-H0pRuoY(BackwardsCompatNode.kt:394)
 	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:314)
 	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
 	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
 	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
 	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
 	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
 	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
 	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
 	at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:301)
 	at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass(HitPathTracker.kt:183)
 	at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:102)
 	at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:98)
 	at androidx.compose.ui.platform.AndroidComposeView.sendMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1329)
 	at androidx.compose.ui.platform.AndroidComposeView.handleMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1275)
 	at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:1214)
 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3920)
 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3594)
 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3920)
 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3594)
 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3920)
 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3594)
 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3920)
 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3594)
 	at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:915)
 	at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1957)
 	at android.app.Activity.dispatchTouchEvent(Activity.java:4182)
 	at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:873)
 	at android.view.View.dispatchPointerEvent(View.java:15458)
 	at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:7457)
 	at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:7233)
 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6595)
 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6652)
 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6618)
 	at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:6786)
 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6626)
 	at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:6843)
 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6599)
 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6652)
 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6618)
 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6626)
 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6599)
 	at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:9880)
 	at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:9718)
 	at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:9671)
 	at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:10014)
 	at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:220)
 	at android.os.MessageQueue.nativePollOnce(Native Method)
 	at android.os.MessageQueue.next(MessageQueue.java:335)
 	at android.os.Looper.loop(Looper.java:206)
 	at android.app.ActivityThread.main(ActivityThread.java:8653)

                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
 	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.platform.MotionDurationScaleImpl@544d50b, androidx.compose.runtime.BroadcastFrameClock@8ba3fe8, StandaloneCoroutine{Cancelling}@c361601, AndroidUiDispatcher@381dca6]

(FR) Clock: Max and Min time

Hi,

Love your work. I just had a FR for the clock dialog to have a time boundary where the user will only be able to select time within the specified boundary.

Library introduces a heavy `material-icons-extended` as a dependency into the resulting APK

Including this as a dependency increases the resulting APK size by a significant margin and adds overhead for builds run with R8.
Material-icons-extended should not be included into the library because that dependency contains a lot of unused code

Proposed solution:
Copy the code of required icons directly into the source code of the library or provide a way for users to supply their own icons instead

some question about "CalendarSelection.Dates"

when i using "CalendarDialog" , now i want to Select multiple dates , everyThings is perfect . we have “CalendarSelection.Dates” ,but this is have no effect in “CalendarSelection.Dates.selectedDates”. When I open Dialog a second time dialog does not display the contents of my last selection. my code Abstract :" val selectedDates = remember { mutableStateOf(listOf()) } , selectedDates = selectedDates.value,"

[Feature] Add enabled state to dialog buttons

We're facing a use-case where we want to show a dialog to the user with a timeout on an OK button to force the user to pay attention. Right now, the InfoDialog has no way of setting a button as "disabled". This could be achieved, in its simplest form, by allowing to configure the color of the button text for SelectionButtons, or as a better solution, by allowing to set a property such as enabled: Boolean on a dialog button to set whether it's clickable or not.

Android 6.0 CalendarView

java.lang.NoClassDefFoundError: Failed resolution of: Ljava/time/LocalDate;
at com.maxkeppeler.sheets.calendar.utils.Constants.(Constants.kt:28)
at com.maxkeppeler.sheets.calendar.models.CalendarConfig.(CalendarConfig.kt:39)

Build warning: String multiple substitutions

Displays an annoying warning more than 20 times per build:

/Users/user/.gradle/caches/transforms-3/cb62b282d40f6ab9806859ef9ffc5e0c/transformed/clock-1.1.0/res/values/values.xml:4:4: Multiple substitutions specified in non-positional format of string resource string/scd_clock_dialog_boundary_hint. Did you mean to add the formatted="false" attribute?

[CalendarDialog] Allows selecting disabled months

Although since 1.0.4 disabled dates in the dialog are shown, the calendar dialog's behavior differs for year selection and month selection. Years that are disallowed are not shown for selection, but months are, and selecting them gives the user a list of days where none are active.

Desired behavior: users can't select months with 0 enabled days.

Readme

Please compress the images on readme.md (tinypng.com)

[API] 1.1.1 Introduces significant breaking changes without deprecation warnings

Some breaking changes (SheetState -> UseCaseState and rememberSheetState -> rememberUseCaseState, removal of the CalendarTimeline) were introduced in 1.1.1 version without any of these indications about breaking changes:

  1. Bumping a major version
  2. Introducing deprecation warnings first
  3. Providing typealiases
  4. Providing an alpha-suffixed version with changes
  5. Providing a documented migration path

This led to significant amount of build failures in our project upon upgrading.

Suggestion:
Next time, as this library is stable now, please give users time to migrate to the new API by using a combination of the common practices mentioned above.

[Calendar] Able to select years outside of the specified CalendarTimeline when not using date constraints.

With the recent changes to the calendar date handling (1.1.0), which were welcome, we noticed that when using calendar timeline, for example FUTURE, although the user can not select a month that is fully in the past, they can select any year they want. All days will be grayed-out for that month upon selection.

Also, upon selecting an invalid year, despite not being able to select months because they all are unavailable, the user can still scroll back and forth between disabled months, circumventing the protection.

(FR) Calendar: Max and Min date instead of just year

Hello, thanks again for this amazing library.
Just found one thing that is missing from calendar: date boundaries, something like:

CalendarDialog(
    state = calendarDialog,
    config = CalendarConfig(
        minDate = LocalDate.now().minusYears(1),
        maxDate = LocalDate.now().plusYears(1)
    ),
    selection = CalendarSelection.Date(selectedDate = value) {
        onValueChange(it)
    }
)

This use case cannot be done using either disabledDates or disabledTimeline.

[Feature] Provide withButtonView across all views/dialogs

It's often the case that buttons are not needed at all. For example, bottom sheets already have a convenient way to dismiss them, so a positive button is not needed. Using the view (InfoView, etc) counterparts have more use cases of not needing a button such as providing buttons elsewhere or using animations.

Providing withButtonView across all dialogs would be good, or allowing the positive button to be null.

Use case 1:

I want my own button so that I can animate it

studio64_Q3LdExE2z2

Use case 2:

This is a bottom sheet only for information so I don't need any buttons. The user can just swipe it away

studio64_TbJ9e0yYfk

Crash: No static method TextButton

Crash on entering composition with ClockComponent

compose-material3 = "1.1.0-alpha01"
compose = "1.4.0-alpha01"
compose-activity = "1.7.0-alpha02"
compose-compiler = "1.3.2"
java.lang.NoSuchMethodError: No static method TextButton(Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/material3/ButtonElevation;Landroidx/compose/ui/graphics/Shape;Landroidx/compose/foundation/BorderStroke;Landroidx/compose/material3/ButtonColors;Landroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V in class Landroidx/compose/material3/ButtonKt; or its super classes (declaration of 'androidx.compose.material3.ButtonKt' appears in /data/app/~~Hfpewa0d1yNCgujP5fX8aQ==/com.example.package-7oldB481MCd-Oz-IEPAD-A==/base.apk)
                            	at com.maxkeppeker.sheets.core.views.ButtonsComponentKt.SelectionButtonComponent(ButtonsComponent.kt:125)
                            	at com.maxkeppeker.sheets.core.views.ButtonsComponentKt.ButtonsComponent(ButtonsComponent.kt:70)
                            	at com.maxkeppeler.sheets.clock.ClockViewKt$ClockView$2.invoke(ClockView.kt:73)
                            	at com.maxkeppeler.sheets.clock.ClockViewKt$ClockView$2.invoke(ClockView.kt:72)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at com.maxkeppeker.sheets.core.views.base.FrameBaseKt.FrameBase(FrameBase.kt:98)
                            	at com.maxkeppeler.sheets.clock.ClockViewKt.ClockView(ClockView.kt:52)
                            	at com.maxkeppeler.sheets.clock.ClockDialogKt$ClockDialog$1.invoke(ClockDialog.kt:51)
                            	at com.maxkeppeler.sheets.clock.ClockDialogKt$ClockDialog$1.invoke(ClockDialog.kt:50)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at androidx.compose.material3.SurfaceKt$Surface$1.invoke(Surface.kt:129)
                            	at androidx.compose.material3.SurfaceKt$Surface$1.invoke(Surface.kt:113)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                            	at androidx.compose.material3.SurfaceKt.Surface-T9BRK9s(Surface.kt:110)
                            	at com.maxkeppeker.sheets.core.views.base.DialogBaseKt$DialogBase$4.invoke(DialogBase.kt:80)
                            	at com.maxkeppeker.sheets.core.views.base.DialogBaseKt$DialogBase$4.invoke(DialogBase.kt:64)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at androidx.compose.ui.window.AndroidDialog_androidKt$Dialog$dialog$1$1$1$2.invoke(AndroidDialog.android.kt:181)
                            	at androidx.compose.ui.window.AndroidDialog_androidKt$Dialog$dialog$1$1$1$2.invoke(AndroidDialog.android.kt:180)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at androidx.compose.ui.window.AndroidDialog_androidKt.DialogLayout(AndroidDialog.android.kt:496)
                            	at androidx.compose.ui.window.AndroidDialog_androidKt.access$DialogLayout(AndroidDialog.android.kt:1)
                            	at androidx.compose.ui.window.AndroidDialog_androidKt$Dialog$dialog$1$1$1.invoke(AndroidDialog.android.kt:178)
                            	at androidx.compose.ui.window.AndroidDialog_androidKt$Dialog$dialog$1$1$1.invoke(AndroidDialog.android.kt:175)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at androidx.compose.ui.window.DialogLayout.Content(AndroidDialog.android.kt:272)
                            	at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:250)
                         E  	at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:249)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                            	at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:177)
                            	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:123)
                            	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:122)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                            	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:114)
                            	at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:157)
                            	at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:156)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                            	at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:156)
                            	at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:140)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
                            	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
                            	at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:78)
                            	at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3274)
                            	at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3264)
                            	at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
                            	at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1)
                            	at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3264)
                            	at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3199)
                            	at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:587)
                            	at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:950)
                            	at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:3874)
                            	at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:3874)
                            	at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:519)
                            	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:140)
                            	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:131)
                            	at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1063)
                            	at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:131)
                            	at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:182)
                            	at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361)
                            	at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:203)
                            	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:138)
                         E  	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:131)
                            	at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1150)
                            	at android.view.View.dispatchAttachedToWindow(View.java:21291)
                            	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3491)
                            	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
                            	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
                            	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
                            	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
                            	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2771)
                            	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2286)
                            	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8948)
                            	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1231)
                            	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
                            	at android.view.Choreographer.doCallbacks(Choreographer.java:899)
                            	at android.view.Choreographer.doFrame(Choreographer.java:832)
                            	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
                            	at android.os.Handler.handleCallback(Handler.java:942)
                            	at android.os.Handler.dispatchMessage(Handler.java:99)
                            	at android.os.Looper.loopOnce(Looper.java:201)
                            	at android.os.Looper.loop(Looper.java:288)
                            	at android.app.ActivityThread.main(ActivityThread.java:7898)
                            	at java.lang.reflect.Method.invoke(Native Method)
                            	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
                            	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

onPositiveButton, onNegativeButton lambdas

I would suggest adding different kind of lambdas inside the SheetState. Like for example, I want to implement a different logic when I press Ok, and not use the same logic for Cancel button as well. onFinishedRequest() is used for all buttons, but I cannot distinguish positive and negative buttons in any way. Good library btw. :)

Landscape dialogs do not use all available height, resulting in small layouts.

After updating to 1.1.0, we noticed that on landscape, dialogs are sometimes unnecessarily small (depends on target device dpi)
Is this intended? The button sizes and paddings do not follow the Android accessibility guidelines in most landscape layouts.
See attached screenshots for examples. Calendar dates' click areas are less than 18dp, whereas the minimum is 48x48dp. I think that the issue stems from paddings being too large for layouts - you can see that the landscape dialog occupies a very small portion of the screen.

Screenshots

Screenshot_20230220-143221
Screenshot_20230220-143213

[Crash] DateTime dialog adds an extra month and day to the initial value

A datetime dialog defined as following

@Composable
fun datetimeDialog(
    title: String?,
    onSelectDate: (LocalDateTime) -> Unit,
    initial: LocalDateTime? = null, // kotlin datetime
    startWithTime: Boolean = false,
    maxYear: Int = MaxYear,
    minYear: Int = MinYear,
): UseCaseState {
    val state = rememberUseCaseState()
    DateTimeDialog(
        header = remember(title) { title?.let { Header.Default(it) } },
        state = state,
        config = DateTimeConfig(icons = LibIcons.Rounded, maxYear = maxYear, minYear = minYear),
        selection = remember(initial, startWithTime, onSelectDate) {
            DateTimeSelection.DateTime(
                selectedTime = initial?.time?.toJavaLocalTime(),
                selectedDate = initial?.date?.toJavaLocalDate(),
                startWithTime = startWithTime,
                onPositiveClick = { onSelectDate(it.toKotlinLocalDateTime()) }
            )
        }
    )

    return state
}

When shown, starts with month and day values incremented by one.

For example, a value of 5/9/23 (verified as being correct) when passed to the dialog, upon opening, shows that the selected date is "Jun 10 2023" which means that the day and month values are bigger than the actual ones by one. Clicking "OK" without changing anything updates values to correct ones, but reopening the dialog results in another increment, and this can be repeated indefinitely. After reaching month 12, the dialog displays the month value as unset. Clicking "OK" will crash the app.

Stacktrace

java.lang.NullPointerException
   at com.maxkeppeler.sheets.date_time.DateTimeState.onFinish(DateTimeState.kt:160)
   at com.maxkeppeler.sheets.date_time.DateTimeViewKt$DateTimeView$4$1.invoke(DateTimeView.kt:140)
   at com.maxkeppeler.sheets.date_time.DateTimeViewKt$DateTimeView$4$1.invoke(DateTimeView.kt:140)
   at com.maxkeppeker.sheets.core.views.ButtonsComponentKt$ButtonsComponent$1$3$1.invoke(ButtonsComponent.kt:97)
   at com.maxkeppeker.sheets.core.views.ButtonsComponentKt$ButtonsComponent$1$3$1.invoke(ButtonsComponent.kt:97)
   at androidx.compose.foundation.ClickablePointerInputNode$pointerInput$3.invoke-k-4lQ0M(Clickable.kt:781)
   at androidx.compose.foundation.ClickablePointerInputNode$pointerInput$3.invoke(Clickable.kt:775)
   at androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:255)
   at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
   at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:177)
   at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
   at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:476)
   at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:510)
   at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:499)
   at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:368)
   at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl$PointerEventHandlerCoroutine.offerPointerEvent(SuspendingPointerInputFilter.kt:672)
   at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.dispatchPointerEvent(SuspendingPointerInputFilter.kt:548)
   at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.kt:570)
   at androidx.compose.foundation.AbstractClickablePointerInputNode.onPointerEvent-H0pRuoY(Clickable.kt:739)

I suppose the error is that the month and day selection starts from 1, not zero :D

Setting dismissOnClickOutside to false doesn't work for top and bottom side

I'm trying to create a permanent/mandatory dialog using CoreDialog

val dialogState = rememberSheetState(onDismissRequest = { Timber.w("DISMISSED") })

CoreDialog(
    state = dialogState,
    selection = CoreSelection(false),
    properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false),
    body = {
        Row(horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
            CircularProgressIndicator()
            Spacer(Modifier.width(16.dp))
            Text(text = "Loading...", style = MaterialTheme.typography.body2)
        }
    }
)

Setting dismissOnBackPress to false works as intended. But setting dismissOnClickOutside to false doesn't work fully, the dialog can be closed by clicking outside and onDismissRequest is called EXCEPT if you click on the right/left side of the dialog, then it won't be dismissed.

A problem with dialog height, maybe?

Fix setup for buttons

Currently, the negative and positive buttons are required. It makes sense that the positive button is always required. But the negative button can be optional. Currently, even setting the negative button to null, will lead to a button being displayed.

[Feature] Datetime selection dialog is confusing to users

We are not satisfied with the UX/UI of the datetime dialog. While we are using a custom implementation, you may want to address the following issue:

The dialog has no indication as to what the user must do to confirm that they've entered the value. This means that even if the user has scrolled the day / month / year values to their desired values, they can't tap "OK" until they have tapped each value (it starts being filled with a primary color). If the user taps "Select time" without confirming their date values, they will all be reset.
It took me as a developer 3 minutes to understand what's going on, imagine how long it will take a regular user to understand.

Library thinks 12pm is 12am

When the time selected by the user is exactly 12pm (and any minute), the library changes that time to 12am.

The culprit is this function:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
internal fun isAm(currentTime: LocalTime): Boolean {
    return currentTime.hour <= 12 // should be <
}

Because 12 hours is already PM, not AM

Button colors doesn't respect current theme

I tried to implement a simple confirmation dialog with CoreDialog, here's what I get:
image

Despite that the primary color is blue, the button that showed is purple, and there are no parameters I can pass to customize it.
I use Material2. Do you have any solution?

[Feature request] Allow submitting a Duration of zero when using DurationDialog

Hi, first of all - thank you for this amazing library and all the work you have put into this project!

I am developing a work time tracking app, where users shall be able to input the duration of their pause / break time as well. For this I've already seen that using a DurationDialog would be just the perfect choice, as I've modelled the pause with a Duration.

I have a use case in which users can longtap on the duration of their pause of the respective tracked work time to input or adjust the duration of the pause / break - however, the DurationDialog does not allow to submit a Duration of zero - could this be added as a customisation option, so that a duration of zero / newTimeInSeconds of 0L could be submitted in onPositiveClick: (Long) -> Unit lambda?

As of right now, the positive click button remains in disabled state, "forcing" users to choose a duration > 0, leaving them unable to set a pause duration of Duration.ZERO if they would like to change it back again.

Screenshot of DurationDialog cleared with positive button disabled

I believe this would involve an enhancement of the libraries' internal class DurationState to allow validation of a timeInfoInseconds of = 0 in isValid()

Thank you in advance for considering this!

One day I hope to be able to submit a PR, however right now I'm too new to android development and programming in general...

Thanks you in advance for letting me know what you think about this feature request and thank you for this project!

Happy easter and best regards from the town of Karlsruhe =)

(Calendar) Add missing checks for Calendar setup

Currently it's possible to deactivate a timeline (past or future) while overlaying with the defined selected date/dates/ period which leads to design glitches as it's not supposed to exist. The current CalendarDialogSample3 shows this issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.