Simple composable for rendering transitions between backstacks.

Overview

compose-backstack

Maven Central GitHub license

Simple library for Jetpack Compose for rendering backstacks of screens and animated transitions when the stack changes. It is not a navigation library, although it is meant to be easy to plug into your navigation library of choice (e.g. compose-router), or even just use on its own.

The Compose version that this library is compatible with is indicated in this library's version number.

Usage

The entry point to the library is the Backstack composable. It essentially looks like this:

@Composable fun <T : Any> Backstack(
    backstack: List<T>,
    content: @Composable() (T) -> Unit
)

The API is very similar to a lot of composables that draw lists: it takes a list of keys and a composable function that knows how to draw a key. In this case, a key represents a distinct screen in the backstack. When the top key in the stack changes between compose passes, the screens will be animated with a transition.

The actual API takes a few more parameters, e.g. to allow custom animations. See the source kdoc for details!

Example

 sealed class Screen {
   object ContactList: Screen()
   data class ContactDetails(val id: String): Screen()
   data class EditContact(val id: String): Screen()
 }

 data class Navigator(
   val push: (Screen) -> Unit,
   val pop: () -> Unit
 )

 @Composable fun App() {
   var backstack: List<Screen> by remember { mutableStateOf(listOf(Screen.ContactList)) }
   val navigator = remember {
     Navigator(
       push = { backstack += it },
       pop = { backstack = backstack.dropLast(1) }
     )
   }

   Backstack(backstack) { screen ->
     when(screen) {
       Screen.ContactList -> ShowContactList(navigator)
       is Screen.ContactDetails -> ShowContact(screen.id, navigator)
       is Screen.EditContact -> ShowEditContact(screen.id, navigator)
     }
   }
 }

Custom transitions

Transitions between screens are defined by implementing the BackstackTransition interface and passing the implementation to the Backstack composable. This interface has a single method:

fun Modifier.modifierForScreen(
    visibility: State<Float>,
    isTop: Boolean
): Modifier

The modifierForScreen method is called for every active/visible screen in the backstack, and must return a Modifier that will be applied to the entire screen. Compose has many Modifiers built-in, which can be used to do a wide variety of visual transformations such as adjust position, size, transparency, etc.

When animating between two screens, visibility will be somewhere between 0 and 1 for both the top screen and the screen immediately under the top one.

The isTop flag indicates if the returned modifier will be applied to the top screen or not, and can be used to display a different animation for the top vs under-top screens. For example, the Slide transition uses isTop to determine whether to translate the screen to the end/right, or the start/left.

Visibility will always transition between 0 and 1. If you need to map that range to a different range of floats, or any other type, you can use one of the lerp functions provided by Compose.

Testing custom transitions

You can use the BackstackViewerApp composable in the backstack-viewer artifact to test your custom transitions interactively. This composable is used by the sample app, and in the screenshots below.

Inspecting the backstack

The compose-backstack-xray gives you a single extension function: FrameController.xrayed(Boolean). This function returns a FrameController that will wrap its receiver and, when passed true, display it in an interactive, translucent, pseudo-3D stack – similar to Android Studio's Layout Inspector. The top-most screen in the stack will still be rendered in its regular position, but with a very low opacity, and will still be interactive.

Backstack inspector

Advanced usage: FrameController

Both transition animations and the xray tool are implemented via the FrameController API. This is the lowest-level way to plug into the Backstack function. More advanced features (e.g. stack peeking like the iOS back gesture) can be implemented by writing custom FrameControllers. For more information, see the kdoc in the source: FrameController.kt.

Samples

There is a sample app in the sample module that demonstrates various transition animations and the behavior with different backstacks.

Slide Transition Crossfade Transition Custom Transition

Gradle

compose-backstack is on Maven Central:

allprojects {
    repositories {
        mavenCentral()
    }
}

dependencies {
    implementation "com.zachklipp:compose-backstack:${Versions.backstack}"

    // For BackstackViewerApp.
    debugImplementation "com.zachklipp:compose-backstack-viewer:${Versions.backstack}"
}
Comments
  • Invalid (?) screen object causes app crash

    Invalid (?) screen object causes app crash

    When migrating from compose-backstack 0.6.0-alpha04 to 0.8.0+beta02, my app is crashing with the following FATAL EXCEPTION backtrace:

    java.lang.IllegalArgumentException: Type of the key XXXX@179c61d is not supported. On Android you can only use types which can be stored inside the Bundle.
            at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:78)
            at com.zachklipp.compose.backstack.BackstackKt.Backstack(Backstack.kt:135)
            ...
    

    The key, redacted here as XXXX, refers to the screen object, which is a normal sealed class. It worked perfectly on the old compose-backstack version.

    From the example in the readme file, I would say that the old way of expressing screens has not changed. Do screens have to meet certain requirements so that they can be stored inside the Bundle?

    bug 
    opened by noe 5
  • Support for jetpack compose beta

    Support for jetpack compose beta

    Now that Jetpack Compose beta is out, the API should stabilize and the need to continuously update should disappear. At least from beta01 to beta02 is seems there were no changes in the existing API.

    Is it planned to upgrade compose-backstack to compose beta?

    opened by noe 2
  • Fix 'save instance state' functionality

    Fix 'save instance state' functionality

    Fixes #28

    Most of this functionality is borrowed directly from how rememberSavedInstanceState works. Each screen now registers its own provider with its parent UiSavedStateRegistry. This means when compose-provided (topmost) registry saves, all of our child registries recursively save too. The rest of the implementation is identical, we still trigger save when a screen transitions offscreen, and restore that state when a screen comes back onscreen.

    Happy to make any changes/improvements!

    opened by grandstaish 2
  • Crash in Compose beta04

    Crash in Compose beta04

    java.lang.NoSuchFieldError: No static field Companion of type Landroidx/compose/foundation/layout/BoxScope$Companion; in class Landroidx/compose/foundation/layout/BoxScope; or its superclasses (declaration of 'androidx.compose.foundation.layout.BoxScope' appears in /data/app/~~AX-A0zaOMg9SMabil1N_6Q==/se.gustavkarlsson.skylight.android.develop-t5puiJgmSbi1p-8tA0NHWg==/base.apk)
            at com.zachklipp.compose.backstack.BackstackKt.Backstack(Backstack.kt:234)
            at com.zachklipp.compose.backstack.BackstackKt.Backstack(Backstack.kt:166)
    

    I'm guessing it has to do with this change: https://android-review.googlesource.com/c/platform/frameworks/support/+/1619084

    opened by gustavkarlsson 1
  • Child state saving issues

    Child state saving issues

    Hello Zach! 👋

    I was playing around with this library yesterday in my app and I ran into a couple issues with your ChildSavedStateRegistry composable function. I think I've solved one of the issues, but I need help with the other.

    The first issue was that child values weren't restored across process recreation. There's a rememberSavedInstanceState call with a mutable map, but that map never gets mutated (and so the values never get saved). I think the fix is as straightforward as this.

    With the first fix applied, child restoration is mostly working. The one (major) exception is that the currently visible child doesn't get saved/restored. The reason being that we only ever call performSave() for offscreen children. And compose doesn't do any magic to propagate save calls down to child registries. I haven't yet been able to solve this one.

    opened by grandstaish 1
  • Consider handling low memory callbacks automatically

    Consider handling low memory callbacks automatically

    Once #4 is done, we can listen for system low memory callbacks and set the flag to false for hidden screens to discard them and free up memory. This should drop things like running animations and bitmaps held by those screens' compositions. If a screen really needs to hold on to longer persistent state, it can use the (soon-to-come) UiSavedStateRegistry APIs, which I think should survive this.

    opened by zach-klippenstein 1
  • Add maven plugin and wire up dokka publishing

    Add maven plugin and wire up dokka publishing

    https://github.com/jitpack-io/multi-flavor-lib-demo/blob/master/energylib/build.gradle https://github.com/Kotlin/dokka/issues/42#issuecomment-410458026

    opened by zach-klippenstein 1
  • Fix TransitionController to no longer perform side effects directly in composition.

    Fix TransitionController to no longer perform side effects directly in composition.

    Performing side effects in composition, without using one of the effect functions, is dangerous because if compositions may be ran on multiple threads in parallel, and if the composition does not complete successfully, the side effects may be running in an invalid state. TransitionController was updating non-MutableState properties directly from updateTransition(which is called directly from a composition) and even launching coroutines to perform transition animations. This is particularly bad because if the composition later failed, we'd be animating a transition which, as far as compose is concerned, never actually occured.

    Instead, now TransitionController.updateBackstack simply sets targetKeys (which is now a MutableState) and ensures that displayedKeys (previously called activeKeys) is initialized on the first call. Animations are performed by a long-running LaunchedEffect which uses snapshotFlow to observe targetKeys changes and run the animations inline. Using a conflated Flow means we also don't need to explicitly synchronize to avoid overlapping transitions – the flow collector exerts backpressure while a transition is animating, and any backstack updates that occur during that time will be conflated.

    This change also makes all the properties of TransitionController that are updated by rememberTransitionController MutableStates, because while we don't need to observe them for changes, we do need to make sure they are thread-safe and rolled back if the composition fails.

    opened by zach-klippenstein 0
  • Completely rewrite the internals and most of the APIs.

    Completely rewrite the internals and most of the APIs.

    • All the actual transition logic is moved out of the Backstack composable and into TransitionController.
    • BackstackInspector and InspectionGestureDetector are replaced with a single API in a new, dedicated module: xrayed().
    • Backstack now just handles the container and managing saveable state.
    • Introduces FrameController, an abstraction which can be used to implement both transitions, the inspector functionality, and things like #17.
    • Removes the spring animations from the inspector. They added unnecessary lag to the gestures.
    • Adds more tests for state handling.
    • Fixed some races that caused navigation animation glitches.

    This change fixes a number of issues:

    • Closes #4
    • Closes #5
    • Closes #16
    • Closes #42
    • Closes #44 (camera distance API is used to improve the 3D view, but not for zooming itself)
    • Unblocks #17
    opened by zach-klippenstein 0
  • [DNM] Experimenting with RestorableStateHolder shouldSave API

    [DNM] Experimenting with RestorableStateHolder shouldSave API

    This PR has 3 commits:

    1. The first brings in the proposed RestorableStateHolder from the Compose CL.
    2. The second removes the requirement that the key type T be bundlable by using the compound key hash as the saver key instead (as I proposed in comments on that CL).
    3. The third adds the shouldSave API proposed by Andrey Kulikov, and uses it to drop screens' states when navigating backwards.

    This PR should not be merged as-is. It proves that these changes work, and serves as a test bed to see how they integrate with this library (spoiler: very cleanly).

    The CI build will fail, but I manually verified that the sample app works as expected.

    opened by zach-klippenstein 0
  • Replace ChildSavedStateRegistry with official compose tooling

    Replace ChildSavedStateRegistry with official compose tooling

    This CL introduces a nice API for manually saving your children's view state. We should replace our custom implementation with it once it's released.

    The current implementation was done mostly in #29.

    opened by zach-klippenstein 0
  • API overhaul because splitting the keys and the composables don't work.

    API overhaul because splitting the keys and the composables don't work.

    All of our tests and demos were built using String as the key, with content that does nothing but render the key. This approach doesn't reflect reality very well, and masked #63, where keys for more interesting objects can get out of sync with the content lambda that can render them. When popping, you would wind up crashing when the up to date lambda is unable to interpret the key for the screen that is being animated away.

    The fix is to change the API from something that takes a list of keys and a function that can render them, to a list of model objects that themselves are able to provide @Composable Content(). IMHO the updated API actually feels pretty good, more like the conventional hoisted-state @Composable Foo(model: FooModel) idiom. (Of course I've been working on this all day, so I'm biased.)

    We provide a new interface:

    interface BackstackFrame<out K : Any> {
      val key: K
      @Composable fun Content()
    }
    

    And change the signature of the Backstack() function:

    fun <K : Any> Backstack(
      frames: List<BackstackFrame<K>>,
      modifier: Modifier = Modifier,
      frameController: FrameController<K>
    )
    

    Note that the param type, K, is still the type of the key, not the type of a particular flavor of BackstackFrame. This makes it easy for us to provide convenience functions to map lists of arbitrary model objects to BackstackFrame instances, so it's not much more verbose than it used to be to make it go.

    Before:

    Backstack(backstack) { screen ->
      when(screen) {
        Screen.ContactList -> ShowContactList(navigator)
        is Screen.ContactDetails -> ShowContact(screen.id, navigator)
        is Screen.EditContact -> ShowEditContact(screen.id, navigator)
      }
    }
    

    After:

    Backstack(
      backstack.toBackstackModel { screen ->
        when(screen) {
          Screen.ContactList -> ShowContactList(navigator)
          is Screen.ContactDetails -> ShowContact(screen.id, navigator)
          is Screen.EditContact -> ShowEditContact(screen.id, navigator)
        }
      }
    )
    

    Note that there are two flavors of toBackstackModel. The second one supports models with more interesting keys.

    data class Portrait(
      val id: Int,
      val url: String
    )
    
    Backstack(
      backstack.toBackstackModel(
        getKey = { it.id }
      ) {
        PrettyPicture(it.url)
      }
    )
    

    Fixes #63

    opened by rjrjr 0
  • Map-based backstack crashes when popping.

    Map-based backstack crashes when popping.

    If you modify BackstackTransitionsTest.assertTransition to build its backstacks out of Map<Int, String>, like so, you crash with NoSuchElementException when popping. We're popping to a list that no longer includes the information to paint the outgoing screen, which is a pretty realistic situation.

        val firstBackstack = mapOf(1 to "one")
        val secondBackstack = mapOf(1 to "one", 2 to "two")
        var backstack by mutableStateOf(if (forward) firstBackstack else secondBackstack)
        compose.mainClock.autoAdvance = false
        compose.setContent {
          Backstack(
            backstack.keys.toList(),
            frameController = rememberTransitionController(
              animationSpec = animation,
              transition = transition
            )
          ) { BasicText(backstack.getValue(it)) }
        }
    
    opened by rjrjr 1
  • Support LifecycleOwner, SavedStateRegistryOwner for child screens.

    Support LifecycleOwner, SavedStateRegistryOwner for child screens.

    Android navigation providers like fragments, jetpack nav, and this one are expected to provide Lifecycles to their child content that represents the lifecycle of their navigation frame, and saved state registries (the android SavedStateRegistry and the compose SaveableStateRegistry) that will save and restore state. This library currently only satisfies ⅓ of that contract - it provides the compose state registry, via the composition local LocalSaveableStateRegistry. However, it is not enough to also provide LocalLifecycle and LocalSavedStateRegistry - those work for composable, but not Android Views. Android views, in particular ComposeView, access these things via the methods on the ViewTree*Owners classes. These methods search up the view tree hierarchy. So navigation providers need to ensure that the LifecycleOwner and SavedStateRegistryOwner are set somewhere in the view tree for each navigation frame. As far as I can tell, even jetpack nav does not handle this case.

    One way to do this would be for the compose mechanism for hosting android views inside compositions, AndroidView, to read the aforementioned composition locals and set the view tree owners to the same values on the view it manages. Unfortunately, as of the time of writing, it does not do this. I have filed an issue about it, but it’s not clear the compose team agrees this is a valid responsibility.

    Another, much hackier way to solve this is for the navigation provider to insert and android view at the root of each child’s content, set the view tree owners on there, then host the child composable content inside that view. This would ensure that any child Android views in that content would find the owners on that android view when searching up the view tree, at the cost of having an otherwise entirely useless view in the middle of what might otherwise be pure composable content. However, this hack would be entirely transparent to users of this library, at least in terms of api.

    This might require changing the FrameController API to deal in composable wrapper functions instead of modifiers.

    opened by zach-klippenstein 0
  • Proposal: More flexible transition API (aka SharedElementTransition support)

    Proposal: More flexible transition API (aka SharedElementTransition support)

    Animating entire screens is one use case for this library, but it's not uncommon to need to animate particular parts of those screens differently or independently. The Android animation APIs have the concept of "shared element transitions", which use string tags to animate views inside screens separate from the rest of the screen (e.g. expanding a hero image). This library currently doesn't provide a way to do this. Neither does Compose itself.

    The simplest solution I've been able to come up with so far borrows some concepts from Compose's Transition API (animation keys) and some from this library's current API (more dynamic screen key management).

    Example

    It's probably easiest to introduce with a sketch. Note the uses of the transitionElement modifier.

    sealed class AppScreen {
      object List : AppScreen()
      data class Detail(val item: Item) : AppScreen()
    }
    
    @Composable fun App() {
      var currentScreen: AppScreen by state { List }
    
      // Establishes the scope for transition element keys.
      TransitionScope(currentScreen) { screen ->
        // Animate whole screens using a slide animation.
        Box(modifier = Modifier.transitionElement(
          tag = "screen",
          transition = Transitions.Slide
        )) {
          when (screen) {
            List -> ListScreen(onItemSelected = { currentScreen = Detail(it) })
            is Detail -> DetailScreen(screen.item)
          }
        }
      }
    }
    
    @Composable fun ListScreen(onItemSelected: (Item) -> Unit) {
      AdapterList(items) { item ->
        // Real app should use ListItem.
        Row(modifier = Modifier.clickable { onItemSelected(item) }) {
          Image(
            item.image,
            // Link the preview image to the hero in the detail screen.
            modifier = Modifier.aspectRatio(1f)
              .transitionElement("hero-${item.id}", Transitions.ByBounds)
          )
          Text(item.description)
        }
      }
    }
    
    @Composable fun DetailScreen(item: Item) {
      Column {
        Image(
          item.image,
          // Link the hero image to the one in the list screen.
          modifier = Modifier.transitionElement("hero-${item.id}", Transitions.ByBounds)
        )
        Text(item.fullText)
      }
    }
    

    Frontend API

    These are the important APIs used by this sketch:

    TransitionScope

    @Composable fun <K> TransitionScope<K>(key: K, content: @Composable (K) -> Unit)
    

    A wrapper composable that defines the scope in which transitionElements are associated by tag. Transitions are performed when the key passed to this composable changes. Each transitionElement that is present in the previous screen is animated out, and each one that is present in the new screen is animated in. More on what "in" and "out" mean below.

    transitionElement

    fun Modifier.transitionElement(
      tag: String,
      transition: ScopedTransition,
      vararg keyedValues: Pair<Key, Any>
    ): Modifier
    

    Returns a Modifier that will tag the modified element with a string tag and an associated transition type. The tag is used to associate elements between different screens in the TransitionScope. The transition defines the animations used to animate the element when it is added to or removed from the composition. More on this below.

    Animation keys

    This isn't used in the above sample, but each transitionElement can also optionally take a map of keys (actually Compose's PropKey) of arbitrary types. These keys behave like they would for a TransitionDefinition, but instead of having each key's state be defined statically, the state is defined at the use site. So for example, a key for Color could be defined, itemColor, and passed to transitionElement along with a value. The transition would then be able to read the incoming/outgoing values for this key and animate between them. E.g.: transitionElement("tag", customTransition, colorKey to Color.Red).

    Note that all elements implicitly get the bounds of their modified composables as an implicit "key". The Slide and Bounds transitions only use that value, so no additional keys need to be specified.

    Transitions.Slide

    A transition that animates incoming and outgoing elements separately, by sliding them horizontally side-by-side. The direction of the movement needs to be specified somehow, not sure what makes the most sense for that. A backstack would need to calculate this direction from whether or not the screen existed in the previous stack or not. Note that because this transition is applied around each screen, the entire screen's contents will be transformed.

    Transitions.Bounds

    A transition that takes both the bounds from the element being removed and the one being added, does some math to map coordinates from each other's parent layouts, and then animates the bounds of the outgoing element to the incoming one (both scaling and transforming). Note that this animation's coordinates need to be relative to the TransitionScope, since the wrapping Slide transition will also be animating each hero element along with its containing screen.

    Note that a special use case for this transition is when two elements that exist in two screens with teh same bounds are nested inside another transitionElement-modified Composable, such as the "screen" one in the example. In this case, the transition causes the nested elements to appear as if they are static, and not moving, while the rest of the screen animates around them.

    Backend API

    A transition (such as Slide or Bounds) is defined as something like the following interface:

    interface ScopedTransition {
    
      // Called when Composable modified with a `transitionElement` is being added to the
      // composition, either because the scope's screen key changed, or the first time the
      // scope itself is added to the composition.
      //
      // Returns a Modifier that will be applied to the element. The modifier may be animated
      // by returning different ones over time.
      @Composable fun adding(
        // The bounds of the element being added.
        bounds: LayoutCoordinates,
        // If the element tag was present in the previous screen, this will be the bounds of that
        // element. If this is the first time the tag is used, it will be null.
        replacingBounds: LayoutCoordinates?,
        // Arbitrary keyed values specified by the incoming element.
        keyedValues: Map<PropKey, Any>,
        // Arbitrary keyed values specified by the outgoing element, if exists.
        replacingKeyedValues: Map<PropKey, Any>?
      ): Modifier
    
      // Called whenever a composable modified by a `transitionElement` is being removed from
      // the composition, either because the scope's screen key changed or the scope itself is
      // being removed from the composition.
      //
      // Returns a Modifier that will be applied to the element. The modifier may be animated
      // by returning different ones over time.
      @Composable fun removing(
        bounds: LayoutCoordinates,
        replacedByBounds: LayoutCoordinates?,
        keyedValues: Map<PropKey, Any>,
        replacedByKeyedValues: Map<PropKey, Any>?
      ): Modifier
    }
    

    Transition implementations can be composed by composing the returned modifiers. For example, a Bounds transition might be combined with a Crossfade transition to make the transition look even smoother. Transition implementations can take parameters (e.g. the Bounds transition might take an enum that determines whether to only animate the composable being added, the one being removed, or both to support Crossfade behavior; Slide needs to know which direction to slide).

    Transitioning between existing elements is only one potential use case. A "transition" could also be defined that only animates elements being added for the very first time or removed (e.g. Transitions.SlideIn, Transitions.FadeOut, etc), and doesn't transition between existing elements at all (such a transition would simply return Modifier when replacingBounds or replacedByBounds is non-null).

    Benefits over existing Backstack API

    This transition API is (intended to be) a superset of the existing one in this library. All the functionality currently provided (except maybe "inspectors", see below) should be obtainable by using a transitionElement immediately inside the TransitionScope, like the "screen" element in the example.

    Benefits over Compose's Transition API

    The Transition API is provides functionality that gets pretty close to this, but requires a pre-defined TransitionDefinition. One of the main use cases of the proposed API is to determine the coordinates of shared elements before and after their screen transitions, which can only be known when those elements are actually composed. It might be possible to tweak the standard Transition API to support this, but I haven't thought about what that would look like.

    Open Questions

    • [ ] How do transitionElement modifiers register themselves with the TransitionScope? A couple possibilities are the transitionElement function is itself Composable, so it is aware of its state and could access an Ambient provided by the TransitionScope, although this technique is being replaced in the standard library with composed modifiers. I'm not sure composed modifiers support the other functionality we need though (see below questions).
    • [ ] transitionElement is effectively a modifier that needs to apply other modifiers to the thing that it's modifying. I'm not sure this sort of "meta-modifier" concept is even supported at all, or supported in a performant-enough way for animations. Might need to use a wrapper composable instead.
    • [ ] The Slide transition needs additional information to choose a movement direction. How should that be communicated?
    • [ ] Should nested TransitionScopes be supported, or explicitly forbidden? If supported, should transitionElements only apply to the innermost enclosing scope or be able to specify which of any of their enclosing scopes they belong to? Simplest to forbidden them for an MVP at least.
    • [ ] The one feature that the current backstack library provides that I don't think this proposed API could support is the inspector. Nested transitions such as Bounds in the example would not be transformed by the inspector, since they explicitly break out of the transformations from their parent.
    opened by zach-klippenstein 4
Releases(v0.9.0+1.0.0-beta07)
  • v0.9.0+1.0.0-beta07(May 19, 2021)

    • Upgrade: Compose to beta07. (#55, #59)
    • Fix state saving for screens that are not saveable/bundleable. (#57)
    • Fix TransitionController to no longer perform side effects directly in composition. (#58)
    Source code(tar.gz)
    Source code(zip)
  • 0.8.0+beta02(Mar 15, 2021)

    • Upgrade: Compose to beta02. (#39, #47)
    • Completely rewrote most of the internals and APIs: (#50)
      • All the actual transition logic is moved out of the Backstack composable and into TransitionController.
      • BackstackInspector and InspectionGestureDetector are replaced with a single API in a new, dedicated module: xrayed().
      • Backstack now just handles the container and managing saveable state.
      • Introduces FrameController, an abstraction which can be used to implement both transitions, the inspector functionality, and things like #17.
      • Removes the spring animations from the inspector. They added unnecessary lag to the gestures.
      • Adds more tests for state handling.
      • Fixed some races that caused navigation animation glitches.
    • Use Material DropdownMenu for spinner. (#51)
    Source code(tar.gz)
    Source code(zip)
  • 0.6.0-alpha04(Oct 13, 2020)

  • 0.5.0+dev16(Aug 10, 2020)

  • 0.4.0(Jul 14, 2020)

    • New: Start using UiSavedStateRegistry to preserve screen "view" state. (#25)
    • New: Automatically save/restore the state of a BackstackViewerApp using the new saved state tools. (#21)
      • Buggy implementation subsequently fixed by @grandstaish in #29, thanks!
    • Fix: Rename master branch to main.
    • Upgrade: Kotlin to 1.3.71. (#15)
    • Upgrade: Compose to dev14. (#31)
    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Mar 30, 2020)

    This is the first release of this library published to Maven Central.

    • Introduce inspection mode for peering into the past. (#12)
    • Add RTL support to the Slide transition.
    • Add system back support to the viewer app. (#14)
    • Replace custom opacity modifier with the new built-in one.
    • Add more API documentation to the README.
    • Tweak the sample custom transition. (#10)
    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Mar 23, 2020)

  • 0.1.0(Mar 23, 2020)

Owner
Zach Klippenstein
Engineer at Square, previously Amazon. Opinions my own.
Zach Klippenstein
a set of Settings like composable items to help android Jetpack Compose developers build complex settings screens

This library provides a set of Settings like composable items to help android Jetpack Compose developers build complex settings screens without all the boilerplate

Bernat Borrás Paronella 178 Jan 4, 2023
Sample app that shows how to create a bitmap from a Jetpack composable

Creating Bitmaps From Jetpack Composables This app demonstrates how to create a bitmap from a Jetpack composable without the need to display the compo

Johann Blake 14 Nov 29, 2022
Jetpack Compose Text composable to show html text from resources

HtmlText Current Compose Version: 1.0.3 Compose HtmlText Text composable to show html text from resources Add to your project Add actual HtmlText libr

Alexander Karkossa 57 Dec 23, 2022
A library that enables Safe Navigation for you Composable destinations when using Jetpack Compose Navigation

A library that enables Safe Navigation for you Composable destinations when using Jetpack Compose Navigation

Roman Levinzon 59 Oct 19, 2022
Capturable - 🚀Jetpack Compose utility library for capturing Composable content and transforming it into Bitmap Image🖼️

Capturable ?? A Jetpack Compose utility library for converting Composable content into Bitmap image ??️ . Made with ❤️ for Android Developers and Comp

Shreyas Patil 494 Dec 29, 2022
Kapture - A small library for Jetpack Compose to capture Composable content to Android Bitmap

kapture A small utility library for Jetpack Compose to capture Composable conten

Kaustubh Patange 10 Dec 9, 2022
Boat - A scoped and composable way to navigate

Boat Boat is an implementation of a scoped, simple and composable way to navigat

Bloder 5 Feb 9, 2022
Flippable - A Jetpack Compose utility library to create flipping Composable views with 2 sides

?? Flippable A Jetpack Compose utility library to create flipping Composable views with 2 sides. Built with ❤︎ by Wajahat Karim and contributors Demo

Wajahat Karim 298 Dec 23, 2022
Row Coloumn Box Compose Constraint Layout Modifier.xyz Animator Tween animation MutableState Creating custom composable Corners Canvas LaunchedEffect

Row Coloumn Box Compose Constraint Layout Modifier.xyz Animator Tween animation MutableState Creating custom composable Corners Canvas LaunchedEffect

Shivaraj M Patil 1 Apr 13, 2022
Pinocchio is a group of libraries for various common UI components. It could contain Composable, View, and everything related to UI.

Pinocchio Pinocchio is a group of libraries for various common UI components. It could contain Composable, View, and everything related to UI. All UI

NAVER Z 24 Nov 30, 2022
GlassmorphicColumn @Composable

glassmorphic-composables GlassmorphicColumn @Composable GlassmorphicRow @Composable With Non-Image background Setup Gradle: allprojects { reposito

Jakhongir Madaminov 62 Dec 8, 2022
Mocking with Jetpack Compose - stubbing and verification of Composable functions

Mockposable A companion to mocking libraries that enables stubbing and verification of functions annotated with @androidx.compose.runtime.Composable.

Jesper Åman 21 Nov 15, 2022
A simple library for automatically animating between Compose states.

compose-autotransition Status: Experimental A simple library for automatically animating between Compose states. var scale by remember { mutableStateO

Zach Klippenstein 64 Oct 13, 2022
This library will make it easier to pass arguments between screens in Jetpack Compose.

Compose Navigation This library will make it easier to pass arguments between screens in Jetpack Compose Setup allprojects { repositories { ...

Nguyen Van Tan 1 Oct 30, 2021
Sample project to demonstrate how to have clear and better interactions between composables and viewmodels.

Form Validation Sample project to demonstrate how to have clear and better interactions between composables and viewmodels. Concepts used uiState conc

Saurabh Pant 20 Dec 22, 2022
🚀🌆🏙 Display differences or animate progress between 2 images or Composables with overlay and customization options, zoom, pan gestures, and progress to observe properties for animating before-after progress

Compose Before-After Composables to display Images, or Composables as before and after composables to display differences or animate progress between

Smart Tool Factory 56 Dec 22, 2022
It's a simple app written in Kotlin that shows a simple solution for how to save an image into Firebase Storage, save the URL in Firestore, and read it back using Jetpack Compose.

It's a simple app written in Kotlin that shows a simple solution for how to save an image into Firebase Storage, save the URL in Firestore, and read it back using Jetpack Compose.

Alex 10 Dec 29, 2022
A simple implementation of collapsing toolbar for Jetpack Compose

compose-collapsing-toolbar A simple implementation of CollapsingToolbarLayout for Jetpack Compose Installation You should add mavenCentral() repositor

onebone 321 Dec 31, 2022
A simple Snake application to demonstrate the use of Compose for Desktop platform with Kotlin

Snake App using Compose for Desktop A simple Snake desktop application to demonstrate the use of Compose UI toolkit for Desktop platform with Kotlin.

Serge Nino Martin Villasica 12 Nov 18, 2022