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)
}
37. data class NameRendering(
val name: String
) : ComposeRendering {
@Composable
override fun Content(viewEnvironment: ViewEnvironment) {
Text(name)
}
}
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)
}
}
}
}
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)
)
}
}
112. ● Design system Compose components almost done
● Integration with internal app scaffolding
● Samples
● Will start using for features soon
Current status