Mohit SarveiyaUsing Square Workflow for Android & iOS@heyitsmohit
View Slide
Using Square Workflow for Android & iOS● Purpose● Building screens & setting up state● Working with Compose
Purpose● Clear boundaries between components● Immutable State● Separation between state & UI
Workflow
Workflow (State)
Workflow (State)Actions
Workflow (State)Rendering
ActionUI
ActionUIState
ActionUIStateRendering
Creating Workflows● State● Actions● Screens
Square Workflow with AndroidScreens, Workflows & Renderings
9:41LoginUsernamePassword
WorkflowLayout RunnerScreen
Setup● AndroidX View Model● View Binding
Screen● Represents view model for screen
Screendata class LoginScreen(val username: String,Val password: String,val onLoginClicked: ()->Unit)
Layout Runner● Specifies how to update views● Supports view binding
Layout Runnerclass LoginLayoutRunner(val loginViewBinding: LoginViewBinding) : LayoutRunner {}
Layout Runneroverride fun showRendering(rendering: LoginScreen) {loginViewBinding.username.text = rendering.usernameloginViewBinding.username.setTextChangedListener {rendering.onUsernameChanged(it.toString())}loginViewBinding.login.setOnClickListener { rendering.onLoginClicked() }}
Workflow (State)RenderingActions
data class State(val username: String,val password: String)State
fun onUsernameChanged(username: String) = action {state = state.copy(username = username)}Action
object LoginWorkflow : StatefulWorkflow {}Workflow
object LoginWorkflow : StatefulWorkflow {override fun initialState() { }override fun render() { }}Workflow
fun render(renderState: State) {}Workflow
fun render(renderState: State): LoginScreen {}Workflow
fun render(renderState: State): LoginScreen {LoginScreen(username = renderState.username,onUsernameChanged = {...},onLoginCliked = {})}Workflow
fun render(renderState: State): LoginScreen {LoginScreen(username = renderState.username,onUsernameChanged = { context.actionSink.send() },onLoginCliked = {})}Workflow
fun render(renderState: State): LoginScreen {LoginScreen(username = renderState.username,onUsernameChanged = { context.actionSink.send(onUserChanged(it)) },onLoginCliked = {})}Workflow
fun initialState(): State {State(username = "")}Workflow
View & View ModelView View Model Workflow
View Modelclass LoginViewModel : ViewModel() {val renderings: StateFlow by lazy {renderWorkflowIn(workflow = LoginWorkflow,scope = viewModelScope,savedStateHandle = savedState)}}
Viewclass LoginActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState) val model: LoginViewModel by viewModels()setContentView(WorkflowLayout(this).apply { start(model.renderings) })}}
Viewclass LoginActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState) setContentView(WorkflowLayout(this).apply { start(model.renderings) })}}
Navigation & Props
Workflow BWorkflow AProps
9:41LoginUsernamePassword9:41Username
Todo WorkflowLogin WorkflowRoot Workflow
Root Workflow● Navigation States● Backstack
sealed class State {object Login : State()}State
sealed class State {object Login : State()data class Todo(val username: String) : State()}State
Root Workflowobject RootWorkflow : StatefulWorkflow {}
Root Workflowobject RootWorkflow : StatefulWorkflow {fun render(renderProps: Unit,renderState: State,context: RenderContext): BackStackScreen}
Root Workflowfun render(...): BackStackScreen { val backstackScreens = mutableListOf()}
Root Workflowfun render(...): BackStackScreen { val loginScreen = context.renderChild(LoginWorkflow)}
Root Workflowfun render(...): BackStackScreen { val loginScreen = context.renderChild(LoginWorkflow)backstackScreens += loginScreen}
Root Workflowfun render(...): BackStackScreen { when (renderState) {is Todo->{}}}
Root Workflowfun render(...): BackStackScreen { when (renderState) {is Todo->{val todoScreen = context.renderChild(TodoWorkflow)backstackScreens += todoScreen}}}
Todo List Screendata class TodoListScreen(val username: String,val todoTitles: List)
Todo Workflow Statedata class State(val todos: List)
Todo Workflow Statefun initialState() = State(listOf(TodoModel(title = "Workout",note = "Workout")))
Todo WorkflowLogin WorkflowPropsUsername
Propsobject TodoListWorkflow : StatefulWorkflow() {data class ListProps(val username: String)}
Root Workflowfun render(...): BackStackScreen {when (renderState) {is Todo->{val todoScreen = context.renderChild(TodoWorkflow)backstackScreens += todoScreen}}}
Root Workflowfun render(...): BackStackScreen {when (renderState) {is Todo->{val todoScreen = context.renderChild(TodoWorkflow,ListProps(username = renderState.username))}}}
View ModelView
View Modelclass LoginViewModel : ViewModel() {val renderings: StateFlow by lazy {renderWorkflowIn(workflow = RootWorkflow,scope = viewModelScope,savedStateHandle = savedState)}}
Todo Layout RunnerLogin Layout Runner
View Registryval viewRegistry = ViewRegistry(BackStackContainer,LoginLayoutRunner,TodoListLayoutRunner)
View RegistrysetContentView(WorkflowLayout(this).apply { start(model.renderings, viewRegistry) })
Square Workflow with Jetpack Compose
9:41Hello9:41Goodbye
WorkflowBinding
Renderingdata class Rendering(val message: String,val onClick: ()->Unit)
BindingcomposeScreenViewFactory { rendering, _->Text(rendering.message,modifier = Modifier.clickable(onClick = rendering.onClick).fillMaxSize().wrapContentSize(Alignment.Center))}
Stateenum class State {Hello,Goodbye;fun theOtherState(): State = when (this) {Hello->GoodbyeGoodbye->Hello}}
Actionval helloAction = action {state = state.theOtherState()}
Workflowobject HelloWorkflow : StatefulWorkflow {}
Workflowobject HelloWorkflow : StatefulWorkflow { override fun render(): Rendering = Rendering(message = renderState.name,onClick = { context.actionSink.send(helloAction) }) }
Thank You!www.codingwithmohit.com@heyitsmohit