From b54be87de3a410f5bcad9d49719a8321dd5253ae Mon Sep 17 00:00:00 2001 From: Zach Klippenstein Date: Sat, 22 Feb 2020 18:12:02 -0800 Subject: [PATCH] Introduce ComposeWorkflow, a self-rendering Workflow. `ComposeWorkflow` is like a `StatelessWorkflow`, but it has a special `render` method: it's a `@Composable` function, and it functions like the body of a `bindCompose` lambda where the rendering is just the workflow's props and a `Sink` accepting `OutputT`s. --- core-compose/api/core-compose.api | 17 ++++ .../workflow/compose/ComposeRendering.kt | 51 +++++++++++ .../workflow/compose/ComposeWorkflow.kt | 57 +++++++++++++ .../workflow/compose/ComposeWorkflowImpl.kt | 84 +++++++++++++++++++ .../hello-compose-rendering/build.gradle.kts | 51 +++++++++++ .../HelloComposeRenderingTest.kt | 43 ++++++++++ .../src/main/AndroidManifest.xml | 36 ++++++++ .../HelloComposeRenderingActivity.kt | 38 +++++++++ .../HelloRenderingWorkflow.kt | 56 +++++++++++++ .../hellocomposerendering/HelloWorkflow.kt | 56 +++++++++++++ .../src/main/res/values/strings.xml | 18 ++++ .../src/main/res/values/styles.xml | 23 +++++ .../ui/compose/tooling/ComposeWorkflows.kt | 43 ++++++++++ settings.gradle.kts | 1 + 14 files changed, 574 insertions(+) create mode 100644 core-compose/src/main/java/com/squareup/workflow/compose/ComposeRendering.kt create mode 100644 core-compose/src/main/java/com/squareup/workflow/compose/ComposeWorkflow.kt create mode 100644 core-compose/src/main/java/com/squareup/workflow/compose/ComposeWorkflowImpl.kt create mode 100644 samples/hello-compose-rendering/build.gradle.kts create mode 100644 samples/hello-compose-rendering/src/androidTest/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingTest.kt create mode 100644 samples/hello-compose-rendering/src/main/AndroidManifest.xml create mode 100644 samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingActivity.kt create mode 100644 samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloRenderingWorkflow.kt create mode 100644 samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloWorkflow.kt create mode 100644 samples/hello-compose-rendering/src/main/res/values/strings.xml create mode 100644 samples/hello-compose-rendering/src/main/res/values/styles.xml create mode 100644 samples/nested-renderings/src/main/res/values/java/com/squareup/workflow/ui/compose/tooling/ComposeWorkflows.kt diff --git a/core-compose/api/core-compose.api b/core-compose/api/core-compose.api index 8512592c..3fca494e 100644 --- a/core-compose/api/core-compose.api +++ b/core-compose/api/core-compose.api @@ -1,3 +1,20 @@ +public final class com/squareup/workflow/compose/ComposeRendering { + public static final field Companion Lcom/squareup/workflow/compose/ComposeRendering$Companion; + public static final fun ()V + public fun (Lkotlin/jvm/functions/Function2;)V +} + +public final class com/squareup/workflow/compose/ComposeRendering$Companion { + public final fun getFactory ()Lcom/squareup/workflow/ui/ViewFactory; + public final fun getNoopRendering ()Lcom/squareup/workflow/compose/ComposeRendering; +} + +public abstract class com/squareup/workflow/compose/ComposeWorkflow : com/squareup/workflow/Workflow { + public fun ()V + public fun asStatefulWorkflow ()Lcom/squareup/workflow/StatefulWorkflow; + public abstract fun render (Ljava/lang/Object;Lcom/squareup/workflow/Sink;Lcom/squareup/workflow/ui/ViewEnvironment;Landroidx/compose/Composer;)V +} + public final class com/squareup/workflow/ui/compose/ComposeViewFactory : com/squareup/workflow/ui/ViewFactory { public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function3;)V public fun buildView (Ljava/lang/Object;Lcom/squareup/workflow/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; diff --git a/core-compose/src/main/java/com/squareup/workflow/compose/ComposeRendering.kt b/core-compose/src/main/java/com/squareup/workflow/compose/ComposeRendering.kt new file mode 100644 index 00000000..13c303e3 --- /dev/null +++ b/core-compose/src/main/java/com/squareup/workflow/compose/ComposeRendering.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2020 Square 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. + */ +@file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry") + +package com.squareup.workflow.compose + +import androidx.compose.Composable +import com.squareup.workflow.compose.ComposeRendering.Companion.Factory +import com.squareup.workflow.compose.ComposeRendering.Companion.NoopRendering +import com.squareup.workflow.ui.ViewEnvironment +import com.squareup.workflow.ui.ViewFactory +import com.squareup.workflow.ui.compose.bindCompose + +/** + * A workflow rendering that renders itself using a [Composable] function. + * + * This is the rendering type of [ComposeWorkflow]. To stub out [ComposeWorkflow]s in `RenderTester` + * tests, use [NoopRendering]. + * + * To use this type, make sure your `ViewRegistry` registers [Factory]. + */ +class ComposeRendering internal constructor( + internal val render: @Composable() (ViewEnvironment) -> Unit +) { + companion object { + /** + * A [ViewFactory] that renders a [ComposeRendering]. + */ + val Factory: ViewFactory = bindCompose { rendering, environment -> + rendering.render(environment) + } + + /** + * A [ComposeRendering] that doesn't do anything. Useful for unit testing. + */ + val NoopRendering = ComposeRendering {} + } +} diff --git a/core-compose/src/main/java/com/squareup/workflow/compose/ComposeWorkflow.kt b/core-compose/src/main/java/com/squareup/workflow/compose/ComposeWorkflow.kt new file mode 100644 index 00000000..abfbcd48 --- /dev/null +++ b/core-compose/src/main/java/com/squareup/workflow/compose/ComposeWorkflow.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2020 Square 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. + */ +package com.squareup.workflow.compose + +import androidx.compose.Composable +import com.squareup.workflow.Sink +import com.squareup.workflow.StatefulWorkflow +import com.squareup.workflow.Workflow +import com.squareup.workflow.ui.ViewEnvironment + +/** + * A stateless [Workflow][com.squareup.workflow.Workflow] that [renders][render] itself as + * [Composable] function. Effectively defines an inline + * [bindCompose][com.squareup.workflow.ui.compose.bindCompose]. + * + * This workflow does not have access to a [RenderContext][com.squareup.workflow.RenderContext] + * since render contexts are only valid during render passes, and this workflow's [render] method + * is invoked after the render pass, when view bindings are being shown. + * + * While this workflow is "stateless" in the usual workflow sense (it doesn't have a `StateT` type), + * since [render] is a Composable function, it can use all the usual Compose facilities for state + * management. + */ +abstract class ComposeWorkflow : + Workflow { + + /** + * Renders [props] using Compose. This function will be called to update the UI whenever the + * [props] or [viewEnvironment] changes. + * + * @param props The data to render. + * @param outputSink A [Sink] that can be used from UI event handlers to send outputs to this + * workflow's parent. + * @param viewEnvironment The [ViewEnvironment] passed down through the `ViewBinding` pipeline. + */ + @Composable abstract fun render( + props: PropsT, + outputSink: Sink, + viewEnvironment: ViewEnvironment + ) + + override fun asStatefulWorkflow(): StatefulWorkflow = + ComposeWorkflowImpl(this) +} diff --git a/core-compose/src/main/java/com/squareup/workflow/compose/ComposeWorkflowImpl.kt b/core-compose/src/main/java/com/squareup/workflow/compose/ComposeWorkflowImpl.kt new file mode 100644 index 00000000..cdd4eca9 --- /dev/null +++ b/core-compose/src/main/java/com/squareup/workflow/compose/ComposeWorkflowImpl.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2020 Square 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. + */ +package com.squareup.workflow.compose + +import androidx.compose.MutableState +import androidx.compose.StructurallyEqual +import androidx.compose.mutableStateOf +import com.squareup.workflow.RenderContext +import com.squareup.workflow.Sink +import com.squareup.workflow.Snapshot +import com.squareup.workflow.StatefulWorkflow +import com.squareup.workflow.action +import com.squareup.workflow.compose.ComposeWorkflowImpl.State +import com.squareup.workflow.contraMap + +internal class ComposeWorkflowImpl( + private val workflow: ComposeWorkflow +) : StatefulWorkflow, OutputT, ComposeRendering>() { + + // This doesn't need to be a @Model, it only gets set once, before the composable ever runs. + class SinkHolder(var sink: Sink? = null) + + data class State( + val propsHolder: MutableState, + val sinkHolder: SinkHolder, + val rendering: ComposeRendering + ) + + override fun initialState( + props: PropsT, + snapshot: Snapshot? + ): State { + val propsHolder = mutableStateOf(props, areEquivalent = StructurallyEqual) + val sinkHolder = SinkHolder() + return State(propsHolder, sinkHolder, ComposeRendering { environment -> + // The sink will get set on the first render pass, so it should never be null. + val sink = sinkHolder.sink!! + // Important: Use the props from the MutableState, _not_ the one passed into render. + workflow.render(propsHolder.value, sink, environment) + }) + } + + override fun onPropsChanged( + old: PropsT, + new: PropsT, + state: State + ): State { + state.propsHolder.value = new + return state + } + + override fun render( + props: PropsT, + state: State, + context: RenderContext, OutputT> + ): ComposeRendering { + // The first render pass needs to cache the sink. The sink is reusable, so we can just pass the + // same one every time. + if (state.sinkHolder.sink == null) { + state.sinkHolder.sink = context.actionSink.contraMap(::forwardOutput) + } + + // onPropsChanged will ensure the rendering is re-composed when the props changes. + return state.rendering + } + + // Compiler bug doesn't let us call Snapshot.EMPTY. + override fun snapshotState(state: State): Snapshot = Snapshot.of("") + + private fun forwardOutput(output: OutputT) = action { setOutput(output) } +} diff --git a/samples/hello-compose-rendering/build.gradle.kts b/samples/hello-compose-rendering/build.gradle.kts new file mode 100644 index 00000000..b5193c6f --- /dev/null +++ b/samples/hello-compose-rendering/build.gradle.kts @@ -0,0 +1,51 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +/* + * Copyright 2020 Square 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. + */ +plugins { + id("com.android.application") + kotlin("android") +} + +apply(from = rootProject.file(".buildscript/configure-android-defaults.gradle")) +apply(from = rootProject.file(".buildscript/android-sample-app.gradle")) + +android { + defaultConfig { + applicationId = "com.squareup.sample.hellocomposerendering" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } +} + +apply(from = rootProject.file(".buildscript/configure-compose.gradle")) +tasks.withType { + kotlinOptions.apiVersion = "1.3" +} + +dependencies { + implementation(project(":core-compose")) + implementation(Dependencies.AndroidX.appcompat) + implementation(Dependencies.Compose.foundation) + implementation(Dependencies.Compose.layout) + implementation(Dependencies.Compose.material) + implementation(Dependencies.Workflow.core) + implementation(Dependencies.Workflow.runtime) + + androidTestImplementation(Dependencies.Compose.test) + androidTestImplementation(Dependencies.Test.AndroidX.junitExt) + androidTestImplementation(Dependencies.Test.junit) +} diff --git a/samples/hello-compose-rendering/src/androidTest/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingTest.kt b/samples/hello-compose-rendering/src/androidTest/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingTest.kt new file mode 100644 index 00000000..8b98d717 --- /dev/null +++ b/samples/hello-compose-rendering/src/androidTest/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2020 Square 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. + */ +package com.squareup.sample.hellocomposerendering + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.ui.test.android.AndroidComposeTestRule +import androidx.ui.test.assertIsDisplayed +import androidx.ui.test.doClick +import androidx.ui.test.findByText +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class HelloComposeRenderingTest { + + // Launches the activity. + @Rule @JvmField val composeRule = AndroidComposeTestRule() + + @Test fun togglesBetweenStates() { + findByText("Hello") + .assertIsDisplayed() + .doClick() + findByText("Goodbye") + .assertIsDisplayed() + .doClick() + findByText("Hello") + .assertIsDisplayed() + } +} diff --git a/samples/hello-compose-rendering/src/main/AndroidManifest.xml b/samples/hello-compose-rendering/src/main/AndroidManifest.xml new file mode 100644 index 00000000..dad2243e --- /dev/null +++ b/samples/hello-compose-rendering/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + diff --git a/samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingActivity.kt b/samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingActivity.kt new file mode 100644 index 00000000..3bf82072 --- /dev/null +++ b/samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingActivity.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Square 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. + */ +package com.squareup.sample.hellocomposerendering + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.squareup.workflow.compose.ComposeRendering +import com.squareup.workflow.diagnostic.SimpleLoggingDiagnosticListener +import com.squareup.workflow.ui.ViewRegistry +import com.squareup.workflow.ui.WorkflowRunner +import com.squareup.workflow.ui.setContentWorkflow + +private val viewRegistry = ViewRegistry(ComposeRendering.Factory) + +class HelloComposeRenderingActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentWorkflow(viewRegistry) { + WorkflowRunner.Config( + HelloWorkflow, + diagnosticListener = SimpleLoggingDiagnosticListener() + ) + } + } +} diff --git a/samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloRenderingWorkflow.kt b/samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloRenderingWorkflow.kt new file mode 100644 index 00000000..72b1429f --- /dev/null +++ b/samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloRenderingWorkflow.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2020 Square 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. + */ +package com.squareup.sample.hellocomposerendering + +import androidx.compose.Composable +import androidx.ui.core.Alignment +import androidx.ui.core.Modifier +import androidx.ui.foundation.Clickable +import androidx.ui.foundation.Text +import androidx.ui.layout.fillMaxSize +import androidx.ui.layout.wrapContentSize +import androidx.ui.material.MaterialTheme +import androidx.ui.material.ripple.ripple +import com.squareup.sample.hellocomposerendering.HelloRenderingWorkflow.Toggle +import com.squareup.workflow.Sink +import com.squareup.workflow.compose.ComposeWorkflow +import com.squareup.workflow.ui.ViewEnvironment + +/** + * A [ComposeWorkflow] that is used by [HelloWorkflow] to render the screen. + * + * This workflow has type `Workflow`. + */ +object HelloRenderingWorkflow : ComposeWorkflow() { + + object Toggle + + @Composable override fun render( + props: String, + outputSink: Sink, + viewEnvironment: ViewEnvironment + ) { + MaterialTheme { + Clickable( + onClick = { outputSink.send(Toggle) }, + modifier = Modifier.ripple(bounded = true) + .fillMaxSize() + ) { + Text(props, modifier = Modifier.wrapContentSize(Alignment.Center)) + } + } + } +} diff --git a/samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloWorkflow.kt b/samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloWorkflow.kt new file mode 100644 index 00000000..2d784cfc --- /dev/null +++ b/samples/hello-compose-rendering/src/main/java/com/squareup/sample/hellocomposerendering/HelloWorkflow.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2020 Square 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. + */ +package com.squareup.sample.hellocomposerendering + +import com.squareup.sample.hellocomposerendering.HelloWorkflow.State +import com.squareup.sample.hellocomposerendering.HelloWorkflow.State.Goodbye +import com.squareup.sample.hellocomposerendering.HelloWorkflow.State.Hello +import com.squareup.workflow.RenderContext +import com.squareup.workflow.Snapshot +import com.squareup.workflow.StatefulWorkflow +import com.squareup.workflow.action +import com.squareup.workflow.compose.ComposeRendering +import com.squareup.workflow.parse + +object HelloWorkflow : StatefulWorkflow() { + enum class State { + Hello, + Goodbye; + + fun theOtherState(): State = when (this) { + Hello -> Goodbye + Goodbye -> Hello + } + } + + private val helloAction = action { + nextState = nextState.theOtherState() + } + + override fun initialState( + props: Unit, + snapshot: Snapshot? + ): State = snapshot?.bytes?.parse { source -> if (source.readInt() == 1) Hello else Goodbye } + ?: Hello + + override fun render( + props: Unit, + state: State, + context: RenderContext + ): ComposeRendering = context.renderChild(HelloRenderingWorkflow, state.name) { helloAction } + + override fun snapshotState(state: State): Snapshot = Snapshot.of(if (state == Hello) 1 else 0) +} diff --git a/samples/hello-compose-rendering/src/main/res/values/strings.xml b/samples/hello-compose-rendering/src/main/res/values/strings.xml new file mode 100644 index 00000000..ad1e200b --- /dev/null +++ b/samples/hello-compose-rendering/src/main/res/values/strings.xml @@ -0,0 +1,18 @@ + + + Hello Compose Rendering + diff --git a/samples/hello-compose-rendering/src/main/res/values/styles.xml b/samples/hello-compose-rendering/src/main/res/values/styles.xml new file mode 100644 index 00000000..1c37bd6d --- /dev/null +++ b/samples/hello-compose-rendering/src/main/res/values/styles.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/samples/nested-renderings/src/main/res/values/java/com/squareup/workflow/ui/compose/tooling/ComposeWorkflows.kt b/samples/nested-renderings/src/main/res/values/java/com/squareup/workflow/ui/compose/tooling/ComposeWorkflows.kt new file mode 100644 index 00000000..85d10445 --- /dev/null +++ b/samples/nested-renderings/src/main/res/values/java/com/squareup/workflow/ui/compose/tooling/ComposeWorkflows.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2020 Square 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. + */ +package com.squareup.workflow.ui.compose.tooling + +import androidx.compose.Composable +import androidx.compose.Immutable +import com.squareup.workflow.Sink +import com.squareup.workflow.compose.ComposeWorkflow +import com.squareup.workflow.ui.ViewBinding + +/** + * Draws this [ComposeWorkflow] using a special preview `ViewRegistry`. + * + * The sink passed to [ComposeWorkflow.render] will be a no-op implementation, since previews can't + * process input. + * + * Use inside `@Preview` Composable functions. + */ +//@Composable fun ComposeWorkflow.preview( +// props: PropsT, +// stubBinding: ViewBinding = PreviewStubViewBinding +//) { +// val containerHints = PreviewContainerHints(stubBinding) +// render(props, NoopSink, containerHints) +//} + +//@Immutable +//private object NoopSink : Sink { +// override fun send(value: Any?) = Unit +//} diff --git a/settings.gradle.kts b/settings.gradle.kts index 9ef9186d..8d99743b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,5 +18,6 @@ rootProject.name = "workflow-compose" include( ":core-compose", ":samples:hello-compose-binding", + ":samples:hello-compose-rendering", ":samples:nested-renderings" )