Code Monkey home page Code Monkey logo

airbnb / showkase Goto Github PK

View Code? Open in Web Editor NEW
2.0K 33.0 105.0 118.07 MB

πŸ”¦ Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements

Home Page: https://medium.com/airbnb-engineering/introducing-showkase-a-library-to-organize-discover-and-visualize-your-jetpack-compose-elements-d5c34ef01095

License: Apache License 2.0

Kotlin 100.00%
android android-library jetpack-compose jetpack-android annotation-processor ui-components reusable-components design-systems

showkase's Introduction

Showkase

Showkase Version Compatible with Compose

Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements. With minimal configuration it generates a UI browser that helps you easily find your components, colors & typography. It also renders your components in common situations like dark mode, right-to-left layouts, and scaled fonts which help in finding issues early.

Why should you use Showkase?

  • When using component based UI toolkits (like React, Compose, Flutter, etc), our codebase often ends up with hundreds of components that are hard to discover, visualize, search and organize.
  • Showkase eliminates the manual work of maintaining a UI preview/browser app that each company is forced to build in order to maintain their design system.
  • Since all the available UI elements are now easily searchable and discoverable, there is better reuse of UI elements in your repo. This makes it useful for maintaining consistency across your app. The biggest problem for enforcing a design system is discoverability and Showkase hopefully solves that problem for your team.
  • It allows you to quickly visualize @Composable components, Color properties and TextStyle (Typography) as you are building them. The goal is to improve the turnaround time in creating production-ready UI elements.
  • Showkase aids in catching common UI issues early with the help of auto-generated permutations of your components.

Features

  • Super simple setup
  • Support for visualizing composables(@ShowkaseComposable), colors(@ShowkaseColor) & typography(@ShowkaseTypography).
  • First class support for @Preview annotation. If you are already using @Preview for previews in Android Studio, using Showkase is even easier as all those components are included in the Showkase browser.
  • Support for top level, class, object & companion object functions and properties to be annotated with the Showkase annotations.
  • 5 Permutations are auto created for each composable (Basic Example, Dark Mode, RTL, Font Scaled, Display Scaled. Look in the gif above for examples)'. More to be added in the future!
  • Support for searching a Showkase UI element by name or group.
  • KDoc support that shows the documentation that you added for a component in the Showkase browser.
  • Multi-module support for showcasing UI elements across multiple modules.
  • Support for constraining a component with a custom height/width using additional parameters in the annotation.
  • Descriptive error messages so that the users of the library can fix any incorrect setup.
  • Incremental annotation processor that makes the code-gen more performant.

Installation

Using Showkase is straightforward and takes just a couple of minutes to get started.

Step 1: Add the dependency to your module's build.gradle file. If you have a multi-module setup, add this dependency to all the modules with UI elements that should be displayed inside the Showkase browser.

Showkase supports both ksp and kapt. By default, it uses kapt as we only recently added ksp support.

If you want Showkase to be available only in debug builds (Recommended and practical for most use cases)

debugImplementation "com.airbnb.android:showkase:1.0.2"
implementation "com.airbnb.android:showkase-annotation:1.0.2"
kspDebug "com.airbnb.android:showkase-processor:1.0.2" or kaptDebug "com.airbnb.android:showkase-processor:1.0.2"

If you want Showkase to be available in your release builds as well

implementation "com.airbnb.android:showkase:1.0.2"
ksp "com.airbnb.android:showkase-processor:1.0.2" or kapt "com.airbnb.android:showkase-processor:1.0.2"

You will also need to ensure that the generated file isn't being stripped away and will need to add this to your proguard file -

-keep public class * extends com.airbnb.android.showkase.models.ShowkaseProvider

Step 2: Add the relevant annotations for every UI element that should be a part of the Showkase browser.

For @Composable components, you can either use the @Preview annotation that Compose comes with or use the @ShowkaseComposable annotation:

@Preview(name = "Custom name for component", group = "Custom group name")
@Composable
fun MyComponent() { ... }

// or

@ShowkaseComposable(name = "Name of component", group = "Group Name")
@Composable
fun MyComponent() { ... }

For Color properties, you can add the @ShowkaseColor annotation to the field:

@ShowkaseColor(name = "Primary Color", group = "Material Design")
val primaryColor = Color(0xFF6200EE)

For TextStyle properties that are useful for typography, you can add the @ShowkaseTypography annotation to the field:

@ShowkaseTypography(name = "Custom name for style", group = "Custom group name")
val h1 = TextStyle(
    fontWeight = FontWeight.Light,
    fontSize = 96.sp,
    letterSpacing = (-1.5).sp
)

Step 3: Define an implementation of the ShowkaseRootModule interface in your root module. If your setup involves only a single module, add this implementation in that module. Ensure that this implementation is also annotated with the @ShowkaseRoot annotation.

@ShowkaseRoot
class MyRootModule: ShowkaseRootModule

Step 4: Showkase is now ready for use! Showkase comes with an Activity that you need to start for accessing the UI browser. Typically you would start this activity from the debug menu of your app but you are free to start this from any place you like! A nice helper extension function getBrowserIntent is generated for you so you might have to build the app once before it's available for use. Just start the intent and that's all you need to do for accessing Showkase!

startActivity(Showkase.getBrowserIntent(context))

Documentation

1. @ShowkaseComposable

Used to annotate @Composable functions that should be displayed inside the Showkase browser. If you are using the @Preview annotation with your @Composable function already then you don't need to use this annotation. Showkase has first class support for @Preview.

Here's how you would use it with your @Composable function:

@ShowkaseComposable(name = "Name", group = "Group")
@Composable
fun MyComposable() {
    .......
    .......
}

Name and group are optional. Look at the properties section to understand the behavior when you don't pass any properties.

Note: Make sure that you add this annotation to only those functions that meet the following criteria:

  • Functions that don't have any parameters
  • If it does have a parameter, it has to be annotated with @PreviewParameter that is provided a PreviewParameterProvider implementation.
  • Stacked @Preview and ShowkaseComposable annotations are only supported with KSP at the moment. This is because of this issue.
  • If you use @Preview to generate UI in the Showkase app, you have to make them internal or public functions. If you would like to have private previews, but skip them in during compilation, you can add skipPrivatePreviewcompiler flag:

If you use KSP:

ksp {
 arg("skipPrivatePreviews", "true")
}

If you use KAPT:

kapt {
 arguments {
  arg("skipPrivatePreviews", "true")
 }
}

This is identical to how @Preview works in Compose as well so Showkase just adheres to the same rules.

// Consider a simple data class
data class Person(
    val name: String,
    val age: Int
)

// In order to pass a person object as a parameter to our composable function, we will annotate 
// our parameter with `@PreviewParameter` and pass a `PreviewParameterProvider` implementation.
@ShowkaseComposable(name = "Name", group = "Group") or @Preview(name = "Name", group = "Group")
@Composable
fun MyComposable(
    @PreviewParameter(provider = ParameterProvider::class) person: Person
) {
    ...
}

class ParameterProvider : PreviewParameterProvider<Person> {
    override val values: Sequence<Person>
        get() = sequenceOf(
            Person("John", 12),
            Person("Doe", 20)
        )

    override val count: Int
        get() = super.count
}

// Since we return 2 objects through our ParameterProvider, Showkase will create 2 previews 
// for this single composable function. Each preview will have it's own parameter that's passed 
// to it - Person("John", 12) for the first preview and Person("Doe", 20) for the second one. 
// This is an effective way to preview your composables against different sets of data.

Alternatively, you could simply wrap your function inside another function that doesn't accept any parameters -

@Composable
fun MyComposable(name: String) {
    .......
    .......
}

In order to make this function compatible with Showkase, you could further wrap this function inside a method that doesn't accept parameters in the following way:

@ShowkaseComposable(name = "Name", group = "Group")
@Composable
fun MyComposablePreview() {
    MyComposable("Name")
}
Representing component styles in Showkase

There are usecases where you might have a component that supports multiple styles. Consider the following example where you have a CustomButton composable and it supports 3 different sizes - Large/Medium/Small. You want to be able to document this in Showkase so that your team can visualize what these different styles look like in action. One option would be to treat them as 3 separate components. However this isn't ideal. ShowkaseComposable offers two properties that help you represent this information better - styleName & defaultStyle. Using these properties allow you to describe all the styles offered by a given Composable component in an organized manner as they are shown on the same screen in the Showkase browser.

Here's what the usage would actually look like for this example -

// Actual component
@Composable
fun CustomButton(
    size: ButtonSize,
    ...
)

// Previews
@ShowkaseComposable(name = "CustomButton", group = "Buttons", defaultStyle = true)
@Composable
fun Preview_CustomButton_Large() {
    CustomButton(size = ButtonSize.Large)
}

@ShowkaseComposable(name = "CustomButton", group = "Buttons", styleName = "Medium size")
@Composable
fun Preview_CustomButton_Medium() {
    CustomButton(size = ButtonSize.Medium)
}

@ShowkaseComposable(name = "CustomButton", group = "Buttons", styleName = "Small size")
@Composable
fun Preview_CustomButton_Small() {
    CustomButton(size = ButtonSize.Small)
}

@ShowkaseComposable currently supports the following properties:
Property Name Description
name The name that should be used to describe your @Composable function. If you don't pass any value, the name of the composable function is used as the name.
group The grouping key that will be used to group it with other @Composable functions. This is useful for better organization and discoverability of your components. If you don't pass any value for the group, the name of the class that wraps this function is used as the group name. If the function is a top level function, the composable is added to a "Default Group".
widthDp The width that your component will be rendered inside the Showkase browser. Use this to restrict the size of your preview inside the Showkase browser.
heightDp The height that your component will be rendered inside the Showkase browser. Use this to restrict the size of your preview inside the Showkase browser.
skip Setting this to true will skip this composable from rendering in the Showkase browser. A good use case for this would be when you want to have composable with @Preview but want to stop Showkase from picking it up and rendering it in its browser
styleName The name of the style that a given composable represents. This is useful for scenarios where a given component has multiple style variants and you want to organize them through Showkase for better discoverability.
defaultStyle A boolean value to denote whether the current composable is using its default style (or the only style if the composable doesn't have other style variants)
2. @ShowkaseColor

Used to annotate Color properties that should be presented inside the Showkase browser. Here's how you would use it with your Color fields:

@ShowkaseColor(name = "Name", group = "Group")
val redColor = Color.Red

@ShowkaseColor("Primary", "Light Colors")
val primaryColor = Color(0xFF6200EE)

Name and group are optional. Look at the properties section below to understand the behavior when you don't pass any properties.

@ShowkaseColor currently supports the following properties:
Property Name Description
name The name that should be used to describe your Color fields. If you don't pass any value, the name of the color field is used as the name.
group The grouping key that will be used to group it with other Color fields. This is useful for better organization and discoverability of your colors. If you don't pass any value for the group, the name of the class that wraps this field is used as the group name. If the field is a top level field, the color is added to a "Default Group".
3. @ShowkaseTypography

Used to annotate TextStyle properties that should be presented inside the Showkase browser. Here's how you would use it with your TextStyle fields:

@ShowkaseTypography(name = "Name", group = "Group")
val h1 = TextStyle(
    fontWeight = FontWeight.Light,
    fontSize = 96.sp,
    letterSpacing = (-1.5).sp
)

Name and group are optional. Look at the properties section below to understand the behavior when you don't pass any properties.

@ShowkaseTypography currently supports the following properties:
Property Name Description
name The name that should be used to describe your TextStyle fields. If you don't pass any value, the name of the textStyle field is used as the name.
group The grouping key that will be used to group it with other TextStyle fields. This is useful for better organization and discoverability of your typography. If you don't pass any value for the group, the name of the class that wraps this field is used as the group name. If the field is a top level field, the textStyle is added to a "Default Group".
4. @ShowkaseRoot

Used to annotate the ShowkaseRootModule implementation class. This is needed to let Showkase know more about the module that is going to be the root module for aggregating all the Showkase supported UI elements across all the different modules(if you are using a multi-module project). If you are only using a single module in your project, add it to that module. You are allowed to have only one @ShowkaseRoot per module.

Here's an example of how you would use it:

@ShowkaseRoot
class MyRootModule: ShowkaseRootModule

Note: The root module is the main module of your app that has a dependency on all other modules in the app. This is relevant because we generate the Showkase related classes in the package of the root module and we need to be able to access the UI elements across all the sub modules. This is only possible from the root module as it typically has a dependency on all the sub-modules.

5. Showkase Object

The Showkase object is the receiver for all the helper methods that this library generates. Currently there are a few extension functions that are generated with the Showkase object as the receiver. In order to get access to these functions, you need to build the app once so that the methods can be generated for your use.

Extension function Description
getBrowserIntent Helper function that return an intent to start the ShowkaseBrowser activity.
getMetadata Helper function that's give's you access to Showkase metadata. This contains data about all the composables, colors and typography in your codebase that's rendered in the Showkase Browser.
// Example Usage

val intent = Showkase.getBrowserIntent(context)
startActivity(intent)

val metadata = Showkase.getMetadata()
val components = metadata.componentList
val colors= metadata.colorList
val typography = metadata.typographyList

Frequently Asked Questions

Is Airbnb using Jetpack Compose in their main app? Airbnb has been one of the earliest adopters of Jetpack Compose and has been using it in production since early 2021. Compose is a really critical pillar of our overall Android strategy and we continue to heavily invest in building more tooling on top of it. We spoke about our experience of using Jetpack Compose at Google I/O 2022.
Can I contribute to this library? Pull requests are welcome! We'd love help improving this library. Feel free to browse through open issues to look for things that need work. If you have a feature request or bug, please open a new issue so we can track it.
How do I provide feedback? The issues tab is the best place to do that.
Why can't we have a single annotation like `Showkase` for all the UI elements? Why did you create a different annotation for each UI element(@ShowkaseComposable for composables, @ShowkaseColor for colors & @ShowkaseTypography for text styles)? This was done mostly for future proofing. Even though these annotations have the same properties right now, it's possible that they will diverge as we add more features. Once more people start using this library, we will get a more clear idea about whether that needs to happen or not. If we find that it didn't evolve the way we expected, we will consider consildating these annotations.
I would like to customize my component browser. Can this library still be useful? We provide a nice helper function that gives you access to the metadata of all your UI elements that are configured to work with Showkase. You can use `Showkase.getMetadata()` to get access to it and then use it in whatever way you see fit.

Coming Soon!

Here are some ideas that we are thinking about. We are also not limited to these and would love to learn more about your use cases.

  • Support for passing a @PreviewParameter parameter to @Preview/@ShowkaseComposable components. (See https://github.com/airbnb/Showkase#1-showkasecomposable)
  • Hooks for screenshot testing. Since all your components are a part of the Showkase browser, this would be a good opportunity to make this a part of your CI and detect diffs in components. (The getMetadata method can be useful to accomplish this. More info here - https://github.com/airbnb/Showkase#5-showkase-object)
  • Support for other UI elements that are a part of your design system (like icons, spacing, etc)
  • Generating a web version of the Showkase browser with documentation, search and screenshots.

Contributing

Pull requests are welcome! We'd love help improving this library. Feel free to browse through open issues to look for things that need work. If you have a feature request or bug, please open a new issue so we can track it.

License

Copyright 2023 Airbnb, Inc.

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.

showkase's People

Contributors

adrinieto avatar androiddevnotes avatar benkay avatar elihart avatar guillemroca avatar guk-ioki avatar jaloveeye avatar jisungbin avatar jmartinesp avatar julioromano avatar marcorighini avatar mataku avatar nathan3d avatar ntsk avatar oas004 avatar ofalvai avatar omico avatar oscarnylander avatar paulwoitaschek avatar pmecho avatar rossbacher avatar seanfreiburg avatar simonmarquis avatar stefankram avatar themanol avatar tzxdtc avatar vinaygaba avatar wang-li 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  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

showkase's Issues

Previewing a component that is scrollable

Hi!

I have a component that uses the verticalScroll modifier. Previewing it in AS works fine, but when opening the group which contains this composable in the Showkase activity, I get an exception saying:

java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like ScrollableContainer and LazyColumn is not allowed. If you want to add a header before the list of items please take a look on LazyColumn component which has a DSL api which allows to first add a header via item() function and then the list of items via items().

How should I go about resolving this, or isn't this functionality taken into account yet?

NoSuchMethodError: No static method Text

When launching showcase I get the following crash. I am using compose beta 09 since I cannot use beta 08 due to other reasons. Is this crash because showkase is not yet compatible with compose 09 or am I doing something wrong. The stacktrace does not make much sense to me. I would assume Text should be available in either version, right?

Thanks for helping.

java.lang.NoSuchMethodError: No static method Text-6FffQQw(Ljava/lang/String;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JLandroidx/compose/ui/text/style/TextOverflow;ZILkotlin/jvm/functions/Function1;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V in class Landroidx/compose/material/TextKt; or its super classes (declaration of 'androidx.compose.material.TextKt' appears in /data/app/com.example.demo-ZeBUS3UgHK5iiyUWfoVzew==/base.apk) at com.airbnb.android.showkase.ui.ShowkaseBrowserAppKt.ToolbarTitle(ShowkaseBrowserApp.kt:177) at com.airbnb.android.showkase.ui.ShowkaseBrowserAppKt.ShowkaseAppBarTitle(ShowkaseBrowserApp.kt:152) at com.airbnb.android.showkase.ui.ShowkaseBrowserAppKt.ShowkaseAppBar(ShowkaseBrowserApp.kt:91) at com.airbnb.android.showkase.ui.ShowkaseBrowserAppKt$ShowkaseAppBar$2.invoke(Unknown Source:8) at com.airbnb.android.showkase.ui.ShowkaseBrowserAppKt$ShowkaseAppBar$2.invoke(Unknown Source:10) at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:140) at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2156) at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2399) at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2578) at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2571) at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:523) at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2564) at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:2540) at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:613) at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:764) at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:103) at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:447) at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:416) at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34) at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109) at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41) at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:964) at android.view.Choreographer.doCallbacks(Choreographer.java:790) at android.view.Choreographer.doFrame(Choreographer.java:721) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Render previews when a preview is annotated with more than one @Preview annotation

Whenever I annotate a composable with two @Preview annotations it no longer exists inside the group that it should (and isn't visible whatsoever in the Components list).

My use case is that I have a preview that looks like this

@Preview(
    name = "LargeContainedButton",
    group = "Design System",
)
@Preview(
    name = "LargeContainedButton β€’ dark",
    group = "Design System",
    uiMode = Configuration.UI_MODE_NIGHT_YES,
)
@Composable
fun LargeContainedButtonPreview() {
    AppTheme {
        LargeContainedButton({}) {
            Text("Contained Button (Large)")
        }
    }
}

In order to be able to see both Dark and Day mode on the IDE itself without launching showkase.

I am not sure if there is a better way to do this in the first place which wouldn't produce this problem, but it anyway feels like this should work regardless as it's a normal use case, what do you think?

Showkase for Epoxy?

Hello πŸ‘‹

Are there any plans to open source the Showkase annotation you have for Epoxy?

Trying to maintain design system components with epoxy in a showcase app has been challenging mostly for the manual work it involves (multiple permutations) and the organization it involves.

Thank you πŸ™

Component browser crashes when using Compose-beta04

The implemention of the inline Row function changed in beta04, and now when trying to open the browser in a project compiled with the latest Compose version it immediately crashes with java.lang.NoSuchFieldError: No static field Companion of type Landroidx/compose/foundation/layout/RowScope$Companion; in class Landroidx/compose/foundation/layout/RowScope; or its superclasses

The fix should be to just bump the Compose version in the library to the latest (I suppose beta05 should drop today)

Can't build the project if app is a root module

Hi, we have a multi-module project. Previously I had one module 'main' with some submodules containing previews and it worked. But we also have some modules which are not dependencies of 'main' module. Only the top-level 'app' module has a dependency to all modules. I tried to move @ShowkaseRoot to the 'app' module.

Now building project fails with this exception:

Caused by: java.lang.NullPointerException
	at com.airbnb.android.showkase.processor.ShowkaseProcessor.init(ShowkaseProcessor.kt:61)
	at org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor.init(incrementalProcessors.kt:41)
	at org.jetbrains.kotlin.kapt3.base.ProcessorWrapper.init(annotationProcessing.kt:197)

Any ideas what might help?

Dark mode demo not working

Captura de pantalla 2021-03-08 a las 9 26 04

Hi.

I'm using this code to change (and animate) dark mode from a debug drawer:

@Composable
fun RestaurantMenuNumbersTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    darkThemeState: MutableState<Boolean> = remember { mutableStateOf(darkTheme) },
    content: @Composable (MutableState<Boolean>) -> Unit,
) {

    val transition = updateTransition(targetState = darkThemeState.value)

    val colors = if (darkThemeState.value) {
        DarkColorPalette
    } else {
        LightColorPalette
    }

    val animateColors = Colors(
        primary = transition.animateColor { colors.primary }.value,
        primaryVariant = transition.animateColor { colors.primaryVariant }.value,
        secondary = transition.animateColor { colors.secondary }.value,
        secondaryVariant = transition.animateColor { colors.secondaryVariant }.value,
        background = transition.animateColor { colors.background }.value,
        surface = transition.animateColor { colors.surface }.value,
        error = transition.animateColor { colors.error }.value,
        onPrimary = transition.animateColor { colors.onPrimary }.value,
        onSecondary = transition.animateColor { colors.onSecondary }.value,
        onBackground = transition.animateColor { colors.onBackground }.value,
        onSurface = transition.animateColor { colors.onSurface }.value,
        onError = transition.animateColor { colors.onError }.value,
        isLight = colors.isLight,
    )

    MaterialTheme(
        colors = animateColors,
        typography = Typography,
        shapes = Shapes,
        content = { content(darkThemeState) }
    )
}

So, showkase not shows the dark mode / l right mode correctly

Support Compose 1.1.0

I know Compose 1.1.0 is still at alpha stage but would it be possible to have an alternative release following the compose alpha track in addition to the stable one?

Crash when using MDC Compose Theme Adapter

When using MDC Compose Theme Adapter (https://material-components.github.io/material-components-android-compose-theme-adapter/), and using MdcTheme in a @Preview, ShowkaseBrowserActivity crashes as the activity theme does not extend Theme.MaterialComponents (crash occurs on the following line: https://github.com/material-components/material-components-android-compose-theme-adapter/blob/774fe521be155b3a4c342394462965bb7aa3a9d8/lib/src/main/java/com/google/android/material/composethemeadapter/MdcTheme.kt#L168).

I attempted to work around the issue by overriding the theme declaration for ShowkaseBrowserActivitys theme:

<style name="Theme.App.NoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar" />

which partially solved the issue - ShowkaseBrowserActivity no longer crashes on rendering a @Composable in the ShowkaseComponentsInAGroupScreen. The crash still happens on ShowkaseComponentDetailScreen, however, for reasons that are not entirely clear to me. I can only assume that it has to do with the Context-manipulation used for the different preview-flavors has something to do with it.

How to launch intent from library module

I have a library module where I've added Showkase as a compileOnly dependency, to allow who implements the module decide if want te add the module or not.

When I try to add the method to create the intent, is not created.

Method will be there:

https://github.com/alorma/Compose-Debug-Drawer/blob/main/developer-shortcuts/src/main/java/com/alorma/developer_shortcuts/ShortcutsModule.kt#L20

And this is the gradle:

https://github.com/alorma/Compose-Debug-Drawer/blob/main/developer-shortcuts/build.gradle#L52

Thanks

ClassNotFoundException: androidx.compose.foundation.layout.InnerPadding

This project looks really promising, but I run into problems when I try to integrate this into my application.

As soon as I run
startActivity(createShowkaseBrowserIntent(this))
the application crashes with the following stacktrace:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.peerke.amuza.debug, PID: 26801
    java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/compose/foundation/layout/InnerPadding;
        at com.airbnb.android.showkase.ui.ShowkaseBrowserAppKt$ShowkaseBrowserApp$2.invoke(Unknown Source:1)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:151)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.material.ScaffoldKt$ScaffoldContent$1.invoke(Scaffold.kt:330)
        at androidx.compose.material.ScaffoldKt$ScaffoldContent$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.material.ScaffoldKt$ScaffoldSlot$1.invoke(Scaffold.kt:388)
        at androidx.compose.material.ScaffoldKt$ScaffoldSlot$1.invoke(Unknown Source:13)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:151)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.foundation.layout.StackKt$Stack$stackChildren$1.invoke(Stack.kt:54)
        at androidx.compose.foundation.layout.StackKt$Stack$stackChildren$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.ui.LayoutKt.Layout(Layout.kt:675)
        at androidx.compose.foundation.layout.StackKt.Stack(Stack.kt:56)
        at androidx.compose.material.ScaffoldKt.ScaffoldSlot(Scaffold.kt:388)
        at androidx.compose.material.ScaffoldKt.ScaffoldContent(Scaffold.kt:325)
        at androidx.compose.material.ScaffoldKt.access$ScaffoldContent(Unknown Source:0)
        at androidx.compose.material.ScaffoldKt$Scaffold$child$1$1$1$1.invoke(Scaffold.kt:210)
        at androidx.compose.material.ScaffoldKt$Scaffold$child$1$1$1$1.invoke(Unknown Source:13)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:151)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.foundation.layout.StackKt$Stack$stackChildren$1.invoke(Stack.kt:54)
        at androidx.compose.foundation.layout.StackKt$Stack$stackChildren$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.ui.LayoutKt.Layout(Layout.kt:675)
        at androidx.compose.foundation.layout.StackKt.Stack(Stack.kt:56)
        at androidx.compose.material.ScaffoldKt$Scaffold$child$1$1.invoke(Scaffold.kt:209)
        at androidx.compose.material.ScaffoldKt$Scaffold$child$1$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.AmbientKt.Providers(Ambient.kt:175)
        at androidx.compose.material.SurfaceKt$Surface$1.invoke(Surface.kt:107)
        at androidx.compose.material.SurfaceKt$Surface$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.ui.LayoutKt.Layout(Layout.kt:675)
        at androidx.compose.material.SurfaceKt.SurfaceLayout(Surface.kt:129)
        at androidx.compose.material.SurfaceKt.Surface-biUpMIw(Surface.kt:97)
        at androidx.compose.material.ScaffoldKt$Scaffold$child$1.invoke(Scaffold.kt:204)
        at androidx.compose.material.ScaffoldKt$Scaffold$child$1.invoke(Unknown Source:13)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:151)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.material.ScaffoldKt.Scaffold-tiGujGI(Scaffold.kt:240)
E/AndroidRuntime:     at com.airbnb.android.showkase.ui.ShowkaseBrowserAppKt.ShowkaseBrowserApp(ShowkaseBrowserApp.kt:40)
        at com.airbnb.android.showkase.ui.ShowkaseBrowserActivity$onCreate$1.invoke(ShowkaseBrowserActivity.kt:37)
        at com.airbnb.android.showkase.ui.ShowkaseBrowserActivity$onCreate$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.ui.selection.SelectionContainerKt$SelectionContainer$3$1.invoke(SelectionContainer.kt:88)
        at androidx.compose.ui.selection.SelectionContainerKt$SelectionContainer$3$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.ui.LayoutKt.Layout(Layout.kt:675)
        at androidx.compose.ui.selection.SelectionLayoutKt.SelectionLayout(SelectionLayout.kt:80)
        at androidx.compose.ui.selection.SelectionContainerKt$SelectionContainer$3.invoke(SelectionContainer.kt:85)
        at androidx.compose.ui.selection.SelectionContainerKt$SelectionContainer$3.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.AmbientKt.Providers(Ambient.kt:175)
        at androidx.compose.ui.selection.SelectionContainerKt.SelectionContainer(SelectionContainer.kt:82)
        at androidx.compose.ui.selection.SelectionContainerKt.SelectionContainer(SelectionContainer.kt:39)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$1$1.invoke(Wrapper.kt:274)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$1$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.AmbientKt.Providers(Ambient.kt:175)
        at androidx.compose.ui.platform.AmbientsKt.ProvideCommonAmbients(Ambients.kt:109)
        at androidx.compose.ui.platform.AndroidAmbientsKt$ProvideAndroidAmbients$3.invoke(AndroidAmbients.kt:101)
        at androidx.compose.ui.platform.AndroidAmbientsKt$ProvideAndroidAmbients$3.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.AmbientKt.Providers(Ambient.kt:175)
        at androidx.compose.ui.platform.AndroidAmbientsKt.ProvideAndroidAmbients(AndroidAmbients.kt:93)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$1.invoke(Wrapper.kt:273)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.AmbientKt.Providers(Ambient.kt:175)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.kt:272)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:142)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.ComposerKt.invokeComposable(Composer.kt:2686)
        at androidx.compose.runtime.Composer.composeInitial(Composer.kt:1853)
        at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:241)
        at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:110)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.kt:264)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Unknown Source:3)
E/AndroidRuntime:     at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.kt:557)
        at android.view.View.dispatchAttachedToWindow(View.java:19701)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3455)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3462)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3462)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3462)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3462)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3462)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2260)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1940)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8041)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1163)
        at android.view.Choreographer.doCallbacks(Choreographer.java:986)
        at android.view.Choreographer.doFrame(Choreographer.java:902)
        at android.view.Choreographer$FrameHandler.handleMessage(Choreographer.java:1091)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7710)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
     Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.compose.foundation.layout.InnerPadding" on path: DexPathList[[zip file "/data/app/com.peerke.amuza.debug-2piQ_OS2K27Bh1BbnrT3cg==/base.apk"],nativeLibraryDirectories=[/data/app/com.peerke.amuza.debug-2piQ_OS2K27Bh1BbnrT3cg==/lib/arm64, /data/app/com.peerke.amuza.debug-2piQ_OS2K27Bh1BbnrT3cg==/base.apk!/lib/arm64-v8a, /system/lib64, /system/product/lib64]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:196)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        	... 113 more

I use the following versions:
Showkase: 1.0.0-alpha02
Gradle Wrapper: 6.6.1
Android Gradle Plugin: 4.1.0-rc03
Jetpack Compose: 1.0.0-alpha03

NullPointerException build error for 'showkase-processor-testing' module

Steps taken

β€’ Fork the project.
β€’ Build the project.

Expected

β€’ Project build successful.

Actual

β€’ Build error thrown NullPointerException, with the following crashlog:

Build file '/Users/karandhillon/Desktop/Personal/Showkase/showkase-processor-testing/build.gradle' line: 82

A problem occurred evaluating project ':showkase-processor-testing'.
> java.lang.NullPointerException (no error message)

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.GradleScriptException: A problem occurred evaluating project ':showkase-processor-testing'.
	at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:93)
	at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.lambda$apply$0(DefaultScriptPluginFactory.java:133)
	at org.gradle.configuration.ProjectScriptTarget.addConfiguration(ProjectScriptTarget.java:77)
	at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:136)
	at org.gradle.configuration.BuildOperationScriptPlugin$1.run(BuildOperationScriptPlugin.java:65)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
	at org.gradle.configuration.BuildOperationScriptPlugin.lambda$apply$0(BuildOperationScriptPlugin.java:62)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:43)
	at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:62)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$applyToMutableState$0(DefaultProjectStateRegistry.java:248)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:275)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.applyToMutableState(DefaultProjectStateRegistry.java:247)
	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:42)
	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:26)
	at org.gradle.configuration.project.ConfigureActionsProjectEvaluator.evaluate(ConfigureActionsProjectEvaluator.java:35)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.lambda$run$0(LifecycleProjectEvaluator.java:100)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$applyToMutableState$0(DefaultProjectStateRegistry.java:248)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$withProjectLock$3(DefaultProjectStateRegistry.java:308)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:178)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:308)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:289)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.applyToMutableState(DefaultProjectStateRegistry.java:247)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:91)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
	at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:63)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:710)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:145)
	at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:36)
	at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:64)
	at org.gradle.configuration.DefaultProjectsPreparer.prepareProjects(DefaultProjectsPreparer.java:46)
	at org.gradle.configuration.BuildTreePreparingProjectsPreparer.prepareProjects(BuildTreePreparingProjectsPreparer.java:57)
	at org.gradle.configuration.BuildOperationFiringProjectsPreparer$ConfigureBuild.run(BuildOperationFiringProjectsPreparer.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
	at org.gradle.configuration.BuildOperationFiringProjectsPreparer.prepareProjects(BuildOperationFiringProjectsPreparer.java:40)
	at org.gradle.initialization.DefaultGradleLauncher.prepareProjects(DefaultGradleLauncher.java:228)
	at org.gradle.initialization.DefaultGradleLauncher.doClassicBuildStages(DefaultGradleLauncher.java:165)
	at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:150)
	at org.gradle.initialization.DefaultGradleLauncher.executeTasks(DefaultGradleLauncher.java:126)
	at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:72)
	at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:67)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:178)
	at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:67)
	at org.gradle.internal.invocation.GradleBuildController.run(GradleBuildController.java:56)
	at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:60)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:63)
	at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32)
	at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:67)
	at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76)
	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.lambda$execute$0(InProcessBuildActionExecuter.java:54)
	at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:87)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:53)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:29)
	at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.lambda$execute$0(BuildTreeScopeLifecycleBuildActionExecuter.java:33)
	at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:49)
	at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:32)
	at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:27)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:104)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:55)
	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:64)
	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:37)
	at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.lambda$execute$0(SessionScopeLifecycleBuildActionExecuter.java:54)
	at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:67)
	at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:50)
	at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:36)
	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:36)
	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:25)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:59)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:31)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31)
	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75)
	at org.gradle.util.Swapper.swap(Swapper.java:38)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52)
	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
Caused by: java.lang.NullPointerException
	at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.from(DefaultConfigurableFileCollection.java:190)
	at org.gradle.api.internal.file.DefaultFileOperations.configurableFiles(DefaultFileOperations.java:126)
	at org.gradle.groovy.scripts.DefaultScript.files(DefaultScript.java:158)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.gradle.internal.metaobject.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:484)
	at org.gradle.internal.metaobject.BeanDynamicObject.tryInvokeMethod(BeanDynamicObject.java:196)
	at org.gradle.groovy.scripts.BasicScript$ScriptDynamicObject.tryInvokeMethod(BasicScript.java:130)
	at org.gradle.internal.metaobject.ConfigureDelegate.invokeMethod(ConfigureDelegate.java:77)
	at build_atncjkjkhxk6as55q39rna32o$_run_closure2.doCall(/Users/karandhillon/Desktop/Personal/Showkase/showkase-processor-testing/build.gradle:82)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.gradle.util.ClosureBackedAction.execute(ClosureBackedAction.java:71)
	at org.gradle.util.ConfigureUtil.configureTarget(ConfigureUtil.java:154)
	at org.gradle.util.ConfigureUtil.configure(ConfigureUtil.java:105)
	at org.gradle.api.internal.project.DefaultProject.dependencies(DefaultProject.java:1230)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.gradle.internal.metaobject.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:484)
	at org.gradle.internal.metaobject.BeanDynamicObject.tryInvokeMethod(BeanDynamicObject.java:196)
	at org.gradle.internal.metaobject.CompositeDynamicObject.tryInvokeMethod(CompositeDynamicObject.java:98)
	at org.gradle.internal.extensibility.MixInClosurePropertiesAsMethodsDynamicObject.tryInvokeMethod(MixInClosurePropertiesAsMethodsDynamicObject.java:34)
	at org.gradle.groovy.scripts.BasicScript$ScriptDynamicObject.tryInvokeMethod(BasicScript.java:134)
	at org.gradle.internal.metaobject.AbstractDynamicObject.invokeMethod(AbstractDynamicObject.java:163)
	at org.gradle.groovy.scripts.BasicScript.invokeMethod(BasicScript.java:83)
	at build_atncjkjkhxk6as55q39rna32o.run(/Users/karandhillon/Desktop/Personal/Showkase/showkase-processor-testing/build.gradle:50)
	at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:91)
	... 140 more

Android Studio also throws a lint error as shown below:
ss

Kapt always fails with ClassNotFoundException

I'm trying to integrate Showkase in a compose project as a separate Android app module, and I consistently get this error every time I build the project:

Caused by: java.lang.ClassNotFoundException: androidx.compose.runtime.Composable
	at com.airbnb.android.showkase.processor.ShowkaseProcessor.init(ShowkaseProcessor.kt:58)
	at org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor.init(incrementalProcessors.kt:41)
	at org.jetbrains.kotlin.kapt3.base.ProcessorWrapper.init(annotationProcessing.kt:175)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$ProcessorState.<init>(JavacProcessingEnvironment.java:686)
	... 41 more

I made sure to be using the save versions for compose, compose-activity, compose-navigation and compose-constraintlayout. Have tried to disable many other dependencies that might be causing the issue with kapt, but at this point I'm not sure what else to try.

Versions:

showkase = "1.0.0-alpha12"
compose = "1.0.0-beta09"
composeConstraintlayout = "1.0.0-alpha07"
composeActivity = "1.3.0-beta02"
composeNavigation = "2.4.0-alpha03"

Duplicate classes when building multiple sub modules

Hi !
I've run into an issue when setting the kapt showkase processor in multiple sub modules in our project.
Works fine with a main module and one sub module but not anymore when adding another one.

Execution failed for task ':application:mergeLibDexDevelopmentDebug'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.DexMergingTaskDelegate
   > There was a failure while executing work items
      > A failure occurred while executing com.android.build.gradle.internal.tasks.DexMergingWorkAction
         > com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: 
           Learn how to resolve the issue at https://developer.android.com/studio/build/dependencies#duplicate_classes.
           Type com.airbnb.android.showkase.LiveLiterals$ShowkaseMetadataUiKt is defined multiple times: 

C:\Users\xxx\Documents\projects\xxx\feature\schedules\build\.transforms\761e96e2c30a858d67dede619ffe9cac\transformed\classes\classes.dex, 
C:\Users\xxx\Documents\projects\xxx\feature\marketplace\build\.transforms\2a25bfd32f11783abb2a278e250a60d6\transformed\classes\classes.dex

Library is a really good idea btw so good job on that !

As a side note, to give you some feedback of our usage of it :
Our project add feature modules on the fly depending of the app configuration and resolve them with DI at runtime. So the implementation : Root module that have it's children doesn't work for us.
To make it work, I did a showkase module where the root annotation is, and that implement all the other compose modules when building in debug. If a better way exist, i'll be glad to know it !

Thanks for your time and the library again.

Allow ignoring some preview from showing up in the Showkase component list

My use case is that I may have one specific composable, but with a various number of different configurations, with different parameters etc, meaning many separate @Preview functions on one composable. On the showkase component list however, it'd be nice to only have one of them, while ignoring the rest, to make it easier to navigate and find what I am looking for without duplacation.

An extra annotation @ShowkaseIgnore or something similar comes to mind first, but maybe there's a better way to approach this.

java.lang.NoSuchMethodError: No static method

The crash:

2020-10-20 15:09:13.677 19942-19942/com.example.composedesigndemo E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.composedesigndemo, PID: 19942
    java.lang.NoSuchMethodError: No static method Scaffold-tiGujGI(Landroidx/compose/ui/Modifier;Landroidx/compose/material/ScaffoldState;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function2;Landroidx/compose/material/FabPosition;ZLkotlin/jvm/functions/Function3;Landroidx/compose/ui/graphics/Shape;FJJJJJLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V in class Landroidx/compose/material/ScaffoldKt; or its super classes (declaration of 'androidx.compose.material.ScaffoldKt' appears in /data/app/com.example.composedesigndemo-N8mZJ-OmTeglUTramA5Y0w==/base.apk)
        at com.airbnb.android.showkase.ui.ShowkaseBrowserAppKt.ShowkaseBrowserApp(ShowkaseBrowserApp.kt:40)
        at com.airbnb.android.showkase.ui.ShowkaseBrowserActivity$onCreate$1.invoke(ShowkaseBrowserActivity.kt:37)
        at com.airbnb.android.showkase.ui.ShowkaseBrowserActivity$onCreate$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:144)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.AmbientKt.Providers(Ambient.kt:175)
        at androidx.compose.ui.platform.AmbientsKt.ProvideCommonAmbients(Ambients.kt:113)
        at androidx.compose.ui.platform.AndroidAmbientsKt$ProvideAndroidAmbients$3.invoke(AndroidAmbients.kt:105)
        at androidx.compose.ui.platform.AndroidAmbientsKt$ProvideAndroidAmbients$3.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:144)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.AmbientKt.Providers(Ambient.kt:175)
        at androidx.compose.ui.platform.AndroidAmbientsKt.ProvideAndroidAmbients(AndroidAmbients.kt:97)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$1.invoke(Wrapper.kt:269)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:144)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.AmbientKt.Providers(Ambient.kt:175)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.kt:268)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Unknown Source:10)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(ComposableLambda.kt:144)
        at androidx.compose.runtime.internal.ComposableLambda.invoke(Unknown Source:10)
        at androidx.compose.runtime.ComposerKt.invokeComposable(Composer.kt:2703)
        at androidx.compose.runtime.Composer.composeInitial(Composer.kt:1867)
        at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:243)
        at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:115)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.kt:262)
        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Unknown Source:3)
        at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.kt:562)
        at android.view.View.dispatchAttachedToWindow(View.java:19553)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3430)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3437)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3437)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3437)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3437)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3437)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2028)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1721)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7598)
2020-10-20 15:09:13.677 19942-19942/com.example.composedesigndemo E/AndroidRuntime:     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
        at android.view.Choreographer.doCallbacks(Choreographer.java:790)
        at android.view.Choreographer.doFrame(Choreographer.java:725)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

The only set of components I have:

const val THE_GROUP = "CTA"
val buttonTextPrimary = TextStyle(color = Color.White, fontWeight = FontWeight.Bold)

@Composable
fun cta(variant: String, text: String) {
    when (ButtonType.valueOf(variant)) {
        ButtonType.PRIMARY -> {
            primaryButton(text)
        }
        ButtonType.DANGEROUS -> {
            dangerousButton(text)
        }
        ButtonType.SECONDARY -> {
            secondaryButton(text)
        }
        ButtonType.POSITIVE -> {
            positiveButton(text)
        }
        ButtonType.HYPER_LINK -> {
            hyperlink(text)
        }
    }
}

@Composable
@Preview(name = "hyperlink", group = THE_GROUP)
fun hyperlink(@PreviewParameter(ButtonTextProvider::class) text: String) {
    val containerModifier = Modifier
    val textStyle = TextStyle(color = colorPrimary, fontWeight = FontWeight.Bold)
    Box(modifier = containerModifier.padding(32.dp, 16.dp, 32.dp, 16.dp)) {
        Text(text, style = textStyle)
    }
}

@Composable
@Preview(name = "secondary", group = THE_GROUP)
fun positiveButton(@PreviewParameter(ButtonTextProvider::class) text: String) {
    val containerModifier = Modifier.clip(RoundedCornerShape(50))
        .background(Color.Green)
        .wrapContentHeight()
    Box(modifier = containerModifier.padding(32.dp, 16.dp, 32.dp, 16.dp)) {
        Text(text, style = buttonTextPrimary)
    }
}

@Composable
@Preview(name = "secondary", group = THE_GROUP)
fun secondaryButton(@PreviewParameter(ButtonTextProvider::class) text: String) {
    val containerModifier = Modifier.border(1.dp, colorPrimary, RoundedCornerShape(50))
    Box(modifier = containerModifier.padding(32.dp, 16.dp, 32.dp, 16.dp)) {
        Text(text)
    }
}

@Composable
@Preview(name = "dangerous", group = THE_GROUP)
fun dangerousButton(@PreviewParameter(ButtonTextProvider::class) text: String) {
    val containerModifier = Modifier.clip(RoundedCornerShape(50))
        .background(Color.Red)
        .wrapContentHeight()
    Box(modifier = containerModifier.padding(32.dp, 16.dp, 32.dp, 16.dp)) {
        Text(text, style = buttonTextPrimary)
    }
}

@Composable
@Preview(name = "primary", group = THE_GROUP)
fun primaryButton(@PreviewParameter(ButtonTextProvider::class) text: String) {
    val containerModifier = Modifier.clip(RoundedCornerShape(50))
        .background(colorPrimary)
        .wrapContentHeight()
    Box(modifier = containerModifier.padding(32.dp, 16.dp, 32.dp, 16.dp)) {
        Text(text, style = buttonTextPrimary)
    }
}

Any ideas?

Make declaring root module optional for single module projects

Current project setup requires the following class to be declared in each module

@ShowkaseRoot
class MyRootModule: ShowkaseRootModule

This is mainly needed to make this library compatible with multi-module projects. It would be nice if this was optional for single module projects. Not a big deal but would be a nice-to-have.

Question/Feature Request: A way to showcase custom data structures/themes

Hi!

In our app we have many similar screens with different themes. I'm planning to use composition locals to decide which colors to use, kinda like this:

data class LobbyColors(
  val a: Color,
  val b: Color,
  ...
)

val lotteryA = LobbyColors(
  a = ...,
  b = ...,
)

val lotteryB = LobbyColors(
  a = ...,
  b = ...,
)

I would like to get a preview of these different configurations (e.g Showkase -> Colors -> LotteryA -> all colors defined in lotteryA). How can I do that? Or if not possible, consider this a feature request :)

Showkase scaffold UI

Hi.

Just wondering, why not using scaffold and TopAppBar to have a better material "look" on showkase, and instead show a custom top bar

Captura de pantalla 2021-03-09 a las 17 35 09

No preview found in a minified and shrinked build (Proguard)

πŸ‘‹ Thanks guys for the amazing work on Showkase, we have implemented it in our apps: we use it to quickly see if we missed the implementation of our components.

Problem

When we launch the showkase activity, it does not find any preview.

Capture d’écran 2021-08-17 aΜ€ 17 33 12

Expected behavior

When we launch the showkase activity, it finds all composables annotated with @Preview.

Hypothesis

It is ok in debug but ko in release. Do you think we should specify something in proguard settings ?

Support for @ExperimentalAnimationApi annotation

When using @ExperimentalAnimationApi, the project doesn't build as the functions generated by Showkase are not annotated with it

image

image

Am I missing something or is there anything I should enable to have support for this?

java.lang.NoSuchMethodError: No static method setContent

I'm using 1.0.0-alpha08 version which is the same compose version as the library. Any ideas on how to solve it?

java.lang.NoSuchMethodError: No static method setContent$default(Landroidx/activity/ComponentActivity;Landroidx/compose/runtime/Recomposer;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Landroidx/compose/runtime/Composition; in class Landroidx/compose/ui/platform/WrapperKt; or its super classes (declaration of 'androidx.compose.ui.platform.WrapperKt' appears in /data/app/com.yektasarioglu.testapp.debug-ZwvsZzo9Jrsy4DLjhICVeg==/base.apk)
        at com.airbnb.android.showkase.ui.ShowkaseBrowserActivity.onCreate(ShowkaseBrowserActivity.kt:26)
        at android.app.Activity.performCreate(Activity.java:8085)
        at android.app.Activity.performCreate(Activity.java:8073)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1320)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3870)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4076)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:91)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:103)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2473)
        at android.os.Handler.dispatchMessage(Handler.java:110)
        at android.os.Looper.loop(Looper.java:219)
        at android.app.ActivityThread.main(ActivityThread.java:8349)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055)

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.