More Related Content Similar to The Workflow Pattern, Composed (2021) (20) The Workflow Pattern, Composed (2021)2. ● Android for 7+ years
● Square: Foundation, Design Systems
● Google: Compose
● Kotlin Slack: #compose
Zach who?
18. Compose @ Square
Summer
2021
Winter
2021
Winter/Sprin
g 2021
Summer
2020
Polish & land
Workflow integration
Design system
initial release
Initial design system
infra and
components, EAP
Design system
considering adoption,
evaluation pilot
Spring
2020
Prototyping Workflow
integration
Spring
2019
Jetpack Compose
first announced
Compose release
timeline announced
Compose 1.0 released
(timeline not to scale)
22. data class NameRendering(
val name: String
) : AndroidViewRendering<NameRendering> {
override val viewFactory: ViewFactory<NameRendering>
get() = LayoutRunner.bind(
layoutId = layout.rendering_layout,
constructor = ::RenderingViewBinding
)
}
23. data class NameRendering(
val name: String
) : AndroidViewRendering<NameRendering> {
override val viewFactory: ViewFactory<NameRendering>
get() = LayoutRunner.bind(
layoutId = layout.rendering_layout,
constructor = ::RenderingViewBinding
)
}
24. data class NameRendering(
val name: String
) : AndroidViewRendering<NameRendering> {
override val viewFactory: ViewFactory<NameRendering>
get() = LayoutRunner.bind(
layoutId = layout.rendering_layout,
constructor = ::RenderingViewBinding
)
}
25. data class NameRendering(
val name: String
) : AndroidViewRendering<NameRendering> {
override val viewFactory: ViewFactory<NameRendering>
get() = LayoutRunner.bind(
layoutId = R.layout.rendering_layout,
constructor = ::NameLayoutRunner
)
}
26. class NameLayoutRunner(view: View) : LayoutRunner<NameRendering> {
private val nameView = view.findViewById<TextView>(R.id.name)
override fun showRendering(
rendering: NameRendering,
viewEnvironment: ViewEnvironment
) {
nameView.text = rendering.name
}
}
27. class NameLayoutRunner(view: View) : LayoutRunner<NameRendering> {
private val nameView = view.findViewById<TextView>(R.id.name)
override fun showRendering(
rendering: NameRendering,
viewEnvironment: ViewEnvironment
) {
nameView.text = rendering.name
}
}
28. data class NameRendering(
val name: String
) : AndroidViewRendering<NameRendering> {
override val viewFactory: ViewFactory<NameRendering>
get() = LayoutRunner.bind(
layoutId = R.layout.rendering_layout,
constructor = ::NameLayoutRunner
)
}
class NameLayoutRunner(view: View) : LayoutRunner<NameRendering> {
private val nameView = view.findViewById<TextView>(R.id.name)
override fun showRendering(
rendering: NameRendering,
viewEnvironment: ViewEnvironment
) {
nameView.text = rendering.name
}
}
29. data class NameRendering(
val name: String
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
BasicText(name)
}
}
30. data class NameRendering(
val name: String
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
BasicText(name)
}
}
31. data class NameRendering(
val name: String
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
Text(name)
}
}
32. interface ComposeRendering : AndroidViewRendering<Nothing> {
override val viewFactory: ViewFactory<Nothing>
get() = ComposeRenderingViewFactory
@Composable
fun Content(viewEnvironment: ViewEnvironment)
}
33. interface ComposeRendering : AndroidViewRendering<Nothing> {
override val viewFactory: ViewFactory<Nothing>
get() = ComposeRenderingViewFactory
@Composable
fun Content(viewEnvironment: ViewEnvironment)
}
34. interface ComposeRendering : AndroidViewRendering<Nothing> {
override val viewFactory: ViewFactory<Nothing>
get() = object : ComposeViewFactory<ComposeRendering>() {
override val type: KClass<in ComposeRendering> =
ComposeRendering::class
@Composable override fun Content(
rendering: ComposeRendering,
viewEnvironment: ViewEnvironment
) {
rendering.Content(viewEnvironment)
}
}
}
35. interface ComposeRendering : AndroidViewRendering<Nothing> {
override val viewFactory: ViewFactory<Nothing>
get() = object : ComposeViewFactory<ComposeRendering>() {
@Composable override fun Content(
rendering: ComposeRendering,
viewEnvironment: ViewEnvironment
) {
rendering.Content(viewEnvironment)
}
}
}
36. interface ComposeRendering : AndroidViewRendering<Nothing> {
override val viewFactory: ViewFactory<Nothing>
get() = object : ComposeViewFactory<ComposeRendering>() {
@Composable override fun Content(
rendering: ComposeRendering,
viewEnvironment: ViewEnvironment
) {
rendering.Content(viewEnvironment)
}
}
}
37. data class NameRendering(
val name: String
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
Text(name)
}
}
40. data class PersonRendering(
val name: String,
val details: Any
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
Column {
BasicText(name)
WorkflowRendering(
details, viewEnvironment,
Modifier.weight(1f)
)
}
}
}
41. data class PersonRendering(
val name: String,
val details: Any
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
Column {
Text(name)
WorkflowRendering(
details, viewEnvironment,
Modifier.weight(1f)
)
}
}
}
42. data class PersonRendering(
val name: String,
val details: Any
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
Column {
Text(name)
WorkflowRendering(
details, viewEnvironment,
Modifier.weight(1f)
)
}
}
}
43. data class PersonRendering(
val name: String,
val details: Any
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
Column {
Text(name)
WorkflowRendering(
details, viewEnvironment,
Modifier.weight(1f)
)
}
}
}
44. data class PersonRendering(
val name: String,
val details: Any
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
Column {
Text(name)
WorkflowRendering(
details, viewEnvironment,
Modifier.weight(1f)
)
}
}
}
45. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
46. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
47. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
48. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
51. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
52. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
53. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
54. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = rendering::class
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
55. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
56. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember(renderingCompatibilityKey) {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
57. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
58. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
59. /**
* Renders [rendering] into the composition using this [ViewEnvironment]'s [ViewRegistry] to
* generate the view.
*
* This function fulfills a similar role as [WorkflowViewStub], but is much more convenient to use
* from Composable functions. Note, however, that just like [WorkflowViewStub], it doesn't matter
* whether the factory registered for the rendering is using classic Android views or Compose.
*
* ## Example
*
* ```
* data class FramedRendering<R : Any>(
* val borderColor: Color,
* val child: R
* ) : ComposeRendering {
*
* @Composable override fun Content(viewEnvironment: ViewEnvironment) {
* Surface(border = Border(borderColor, 8.dp)) {
* WorkflowRendering(child, viewEnvironment)
* }
* }
* }
* ```
*
* @param rendering The workflow rendering to display. May be of any type for which a [ViewFactory]
* has been registered in [viewEnvironment]'s [ViewRegistry].
* @param modifier A [Modifier] that will be applied to composable used to show [rendering].
*
* @throws IllegalArgumentException if no factory can be found for [rendering]'s type.
*/
@WorkflowUiExperimentalApi
@Composable public fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
// This will fetch a new view factory any time the new rendering is incompatible with the previous
// one, as determined by Compatible. This corresponds to WorkflowViewStub's canShowRendering
// check.
val renderingCompatibilityKey = Compatible.keyFor(rendering)
// By surrounding the below code with this key function, any time the new rendering is not
// compatible with the previous rendering we'll tear down the previous subtree of the composition,
// including its lifecycle, which destroys the lifecycle and any remembered state. If the view
// factory created an Android view, this will also remove the old one from the view hierarchy
// before replacing it with the new one.
key(renderingCompatibilityKey) {
val viewFactory = remember {
// The view registry may return a new factory instance for a rendering every time we ask it, for
// example if an AndroidViewRendering doesn't share its factory between rendering instances. We
// intentionally don't ask it for a new instance every time to match the behavior of
// WorkflowViewStub and other containers, which only ask for a new factory when the rendering is
// incompatible.
viewEnvironment[ViewRegistry]
// Can't use ViewRegistry.buildView here since we need the factory to convert it to a
// compose one.
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
// Just like WorkflowViewStub, we need to manage a Lifecycle for the child view. We just provide
// a local here – ViewFactoryAndroidView will handle setting the appropriate view tree owners
// on the child view when necessary. Because this function is surrounded by a key() call, when
// the rendering is incompatible, the lifecycle for the old view will be destroyed.
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
// We need to propagate min constraints because one of the likely uses for the modifier passed
// into this function is to directly control the layout of the child view – which means
// minimum constraints are likely to be significant.
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
63. interface ViewFactory<in RenderingT : Any> {
val type: KClass<in RenderingT>
fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup? = null
): View
}
64. interface ViewFactory<in RenderingT : Any> {
fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup? = null
): View
}
65. interface ViewFactory<in RenderingT : Any> {
fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup? = null
): View
}
66. interface ViewFactory<in RenderingT : Any> {
fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup? = null
): View
}
67. interface ViewFactory<in RenderingT : Any> {
fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup? = null
): View
}
68. interface ViewFactory<in RenderingT : Any> {
fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup? = null
): View
}
69. abstract class ComposeViewFactory<RenderingT : Any> : ViewFactory<RenderingT> {
@Composable abstract fun Content(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
)
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(rendering, environment)
}
}
}
}
70. abstract class ComposeViewFactory<RenderingT : Any> : ViewFactory<RenderingT> {
@Composable abstract fun Content(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
)
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(rendering, environment)
}
}
}
}
71. abstract class ComposeViewFactory<RenderingT : Any> : ViewFactory<RenderingT> {
@Composable abstract fun Content(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
)
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(rendering, environment)
}
}
}
}
72. abstract class ComposeViewFactory<RenderingT : Any> : ViewFactory<RenderingT> {
@Composable abstract fun Content(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
)
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(rendering, environment)
}
}
}
}
73. abstract class ComposeViewFactory<RenderingT : Any> : ViewFactory<RenderingT> {
@Composable abstract fun Content(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
)
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(initialRendering, initialViewEnvironment)
}
}
}
}
74. abstract class ComposeViewFactory<RenderingT : Any> : ViewFactory<RenderingT> {
@Composable abstract fun Content(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
)
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(rendering, environment)
}
}
}
}
75. abstract class ComposeViewFactory<RenderingT : Any> : ViewFactory<RenderingT> {
@Composable abstract fun Content(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
)
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(rendering, environment)
}
}
}
}
76. abstract class ComposeViewFactory<RenderingT : Any> : ViewFactory<RenderingT> {
@Composable abstract fun Content(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
)
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(rendering, environment)
}
}
}
}
77. interface ComposeRendering : AndroidViewRendering<Nothing> {
override val viewFactory: ViewFactory<Nothing>
get() = object : ComposeViewFactory<ComposeRendering>() {
@Composable override fun Content(
rendering: ComposeRendering,
viewEnvironment: ViewEnvironment
) {
rendering.Content(viewEnvironment)
}
}
}
78. data class PersonRendering(
val name: String,
val details: Any
) : ComposeRendering {
@Composable override fun Content(viewEnvironment: ViewEnvironment) {
Column {
BasicText(name)
WorkflowRendering(
details, viewEnvironment,
Modifier.weight(1f)
)
}
}
}
79. data class PersonRendering(
val name: String,
val details: Any
)
object PersonViewFactory : ComposeViewFactory<PersonRendering>() {
@Composable override fun Content(
rendering: PersonRendering,
viewEnvironment: ViewEnvironment
) {
Column {
BasicText(rendering.name)
WorkflowRendering(
rendering.details, viewEnvironment,
Modifier.weight(1f)
)
}
}
}
80. data class PersonRendering(
val name: String,
val details: Any
)
val personViewFactory: ViewFactory<PersonRendering> =
composeViewFactory { rendering, viewEnvironment ->
Column {
BasicText(rendering.name)
WorkflowRendering(
rendering.details, viewEnvironment,
Modifier.weight(1f)
)
}
}
82. @Composable fun WorkflowRendering(
rendering: Any,
viewEnvironment: ViewEnvironment,
modifier: Modifier = Modifier
) {
val renderingCompatibilityKey = Compatible.keyFor(rendering)
key(renderingCompatibilityKey) {
val viewFactory: ComposeViewFactory<Any> = remember {
viewEnvironment[ViewRegistry]
.getFactoryForRendering(rendering)
.asComposeViewFactory()
}
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(modifier, propagateMinConstraints = true) {
viewFactory.Content(rendering, viewEnvironment)
}
}
}
}
85. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
override val type: KClass<in R> get() = originalFactory.type
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context, container = null)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
86. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
override val type: KClass<in R> get() = originalFactory.type
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context, container = null)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
87. val color = composeViewFactory<Color> { rendering, _ ->
Text(rendering.toString())
}
val red = composeViewFactory<Unit> { _, viewEnvironment ->
Row {
Text("Red: ")
WorkflowRendering(Color.Red, viewEnvironment)
}
}
88. val color = composeViewFactory<Color> { rendering, _ ->
Text(rendering.toString())
}
val red = composeViewFactory<Unit> { _, viewEnvironment ->
Row {
Text("Red: ")
val renderingCompatibilityKey = Compatible.keyFor(Color.Red)
key(renderingCompatibilityKey) {
val lifecycleOwner = rememberChildLifecycleOwner()
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
Box(propagateMinConstraints = true) {
color.Content(Color.Red, viewEnvironment)
}
}
}
}
}
89. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
override val type: KClass<in R> get() = originalFactory.type
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context, container = null)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
90. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
override val type: KClass<in R> get() = originalFactory.type
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context, container = null)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
91. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context, container = null)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
92. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context, container = null)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
93. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context, container = null)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
94. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
95. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(rendering, environment)
}
}
}
96. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
97. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
98. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}w, lifecycleOwner)
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
99. fun <R : Any> ViewFactory<R>.asComposeViewFactory(): ComposeViewFactory<R> =
(this as? ComposeViewFactory) ?: object : ComposeViewFactory<R>() {
private val originalFactory = this@asComposeViewFactory
@Composable override fun Content(
rendering: R,
viewEnvironment: ViewEnvironment
) {
AndroidView(
factory = { context ->
originalFactory.buildView(rendering, viewEnvironment, context)
.also { view ->
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view"
}
}
},
update = { view ->
view.showRendering(rendering, viewEnvironment)
}
)
}
}
104. ● More flexible than previous hooks
● Gotcha: root views (e.g. modals)
● Only a pattern
ViewTree*Owners… 😐
105. ● Multiple mechanisms
○ View onSaveInstanceState/onRestoreInstanceState
○ AndroidX SavedStateRegistry
○ Compose SaveableStateRegistry
● IDs
● Lifecycle-sensitive
Implementing view state restoration is hard
107. class ComposeViewFactory<RenderingT : Any>(
override val type: KClass<RenderingT>,
private val content: @Composable() (RenderingT, ViewEnvironment) -> Unit
) : ViewFactory<RenderingT> {
override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View {
val composeContainer = FrameLayout(contextForNewView)
val renderState = mutableStateOf<Pair<RenderingT, ViewEnvironment>?>(
// This will be updated immediately by bindShowRendering below.
value = null,
areEquivalent = StructurallyEqual
)
FrameManager.ensureStarted()
composeContainer.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
renderState.value = Pair(rendering, environment)
}
composeContainer.setOrContinueContent(initialViewEnvironment) {
val (rendering, environment) = renderState.value!!
showRenderingWrappedWithRoot(rendering, environment)
}
return composeContainer
}
@Composable internal fun showRenderingWrappedWithRoot(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
) {
wrapWithRootIfNecessary(viewEnvironment) {
content(rendering, viewEnvironment)
}
}
}
108. abstract class ComposeViewFactory<RenderingT : Any> : ViewFactory<RenderingT> {
@Composable abstract fun Content(
rendering: RenderingT,
viewEnvironment: ViewEnvironment
)
final override fun buildView(
initialRendering: RenderingT,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = ComposeView(contextForNewView).also { composeView ->
composeView.bindShowRendering(
initialRendering,
initialViewEnvironment
) { rendering, environment ->
composeView.setContent {
Content(rendering, environment)
}
}
}
}
110. ● But there are benefits if you're willing to invest
Writing a navigation library is hard
112. ● Design system Compose components almost done
● Integration with internal app scaffolding
● Samples
● Will start using for features soon
Current status
114. Zach Klippenstein / twitter.com/zachklipp
kotlinlang.slack.com #squarelibraries
github.com/square/workflow
developer.android.com/jetpack/compose
bit.ly/workflow-compose-blog
Thank you! Questions?