Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Using Square Workflow for Android & iOS
Search
Mohit S
May 18, 2022
Programming
500
1
Share
Using Square Workflow for Android & iOS
Mohit S
May 18, 2022
More Decks by Mohit S
See All by Mohit S
Guide to Improving Compose Performance
heyitsmohit
0
320
Building Shared UIs across Platforms with Compose
heyitsmohit
1
700
Building Multiplatform Apps with Compose
heyitsmohit
2
600
Building StateFlows with Jetpack Compose
heyitsmohit
6
2k
Building Android Testing Infrastructure
heyitsmohit
1
600
Migrating to Kotlin State & Shared Flows
heyitsmohit
1
850
Building Android Infrastructure Teams at Scale
heyitsmohit
3
400
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
640
Challenges of Building Kotlin Multiplatform Libraries
heyitsmohit
1
510
Other Decks in Programming
See All in Programming
t *testing.T は どこからやってくるの?
otakakot
1
710
CDK Deployのための ”反響定位”
watany
5
810
属人化しないコード品質の作り方_2026.04.07.pdf
muraaano
0
230
ついに来た!本格的なマルチクラウド時代の Google Cloud
maroon1st
0
220
実践CRDT
tamadeveloper
0
590
Claude CodeでETLジョブ実行テストを自動化してみた
yoshikikasama
0
660
GNU Makeの使い方 / How to use GNU Make
kaityo256
PRO
16
5.6k
Kubernetes上でAgentを動かすための最新動向と押さえるべき概念まとめ
sotamaki0421
3
620
HTML-Aware ERB: The Path to Reactive Rendering @ RubyKaigi 2026, Hakodate, Japan
marcoroth
0
180
The Less-Told Story of Socket Timeouts
coe401_
3
590
AI時代のPhpStorm最新事情 #phpcon_odawara
yusuke
0
190
Swift Concurrency Type System
inamiy
1
540
Featured
See All Featured
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
270
How to train your dragon (web standard)
notwaldorf
97
6.6k
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
280
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.4k
KATA
mclloyd
PRO
35
15k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
150
Making Projects Easy
brettharned
120
6.6k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
5.7k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.5k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
720
Transcript
Mohit Sarveiya Using Square Workflow for Android & iOS @heyitsmohit
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) Actions
Workflow (State) Rendering
Action UI
Action UI State
Action UI State Rendering
Action UI State Rendering
Creating Workflows • State • Actions • Screens
Square Workflow with Android Screens, Workflows & Renderings
9:41 Login Username Password
Workflow Layout Runner Screen
Setup • AndroidX View Model • View Binding
Screen • Represents view model for screen
Screen data class LoginScreen( val username: String, Val password: String,
val onLoginClicked: () -> Unit )
Layout Runner • Specifies how to update views • Supports
view binding
Layout Runner class LoginLayoutRunner( val loginViewBinding: LoginViewBinding ) : LayoutRunner<LoginScreen>
{ }
Layout Runner override fun showRendering(rendering: LoginScreen) { loginViewBinding.username.text = rendering.username
loginViewBinding.username.setTextChangedListener { rendering.onUsernameChanged(it.toString()) } loginViewBinding.login.setOnClickListener { rendering.onLoginClicked() } }
Layout Runner override fun showRendering(rendering: LoginScreen) { loginViewBinding.username.text = rendering.username
loginViewBinding.username.setTextChangedListener { rendering.onUsernameChanged(it.toString()) } loginViewBinding.login.setOnClickListener { rendering.onLoginClicked() } }
Layout Runner override fun showRendering(rendering: LoginScreen) { loginViewBinding.username.text = rendering.username
loginViewBinding.username.setTextChangedListener { rendering.onUsernameChanged(it.toString()) } loginViewBinding.login.setOnClickListener { rendering.onLoginClicked() } }
Layout Runner override fun showRendering(rendering: LoginScreen) { loginViewBinding.username.text = rendering.username
loginViewBinding.username.setTextChangedListener { rendering.onUsernameChanged(it.toString()) } loginViewBinding.login.setOnClickListener { rendering.onLoginClicked() } }
Workflow Layout Runner Screen
Workflow (State) Rendering Actions
data class State( val username: String, val password: String )
State
fun onUsernameChanged(username: String) = action { state = state.copy(username =
username) } Action
object LoginWorkflow : StatefulWorkflow<State, LoginScreen> { } Workflow
object LoginWorkflow : StatefulWorkflow<State, LoginScreen> { override fun initialState() {
} override fun render() { } } Workflow
object LoginWorkflow : StatefulWorkflow<State, LoginScreen> { 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
= { ... }, onLoginCliked = {} ) } Workflow
fun onUsernameChanged(username: String) = action { state = state.copy(username =
username) } Action
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
Workflow (State) Rendering Actions
View & View Model View View Model Workflow
View Model class LoginViewModel : ViewModel() { val renderings: StateFlow<LoginScreen>
by lazy { renderWorkflowIn( workflow = LoginWorkflow, scope = viewModelScope, savedStateHandle = savedState ) } }
View Model class LoginViewModel : ViewModel() { val renderings: StateFlow<LoginScreen>
by lazy { renderWorkflowIn( workflow = LoginWorkflow, scope = viewModelScope, savedStateHandle = savedState ) } }
View class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) val model: LoginViewModel by viewModels() setContentView( WorkflowLayout(this).apply { start(model.renderings) } ) } }
View class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) val model: LoginViewModel by viewModels() setContentView( WorkflowLayout(this).apply { start(model.renderings) } ) } }
View class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) setContentView( WorkflowLayout(this).apply { start(model.renderings) } ) } }
Workflow Layout Runner Screen
9:41 Login Username Password
Action UI State Rendering
Navigation & Props
Workflow B Workflow A Props
9:41 Login Username Password
9:41 Login Username Password 9:41 Username
Todo Workflow Login Workflow Root 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 Workflow object RootWorkflow : StatefulWorkflow { }
Root Workflow object RootWorkflow : StatefulWorkflow { fun render( renderProps:
Unit, renderState: State, context: RenderContext ): BackStackScreen<Any> }
Root Workflow fun render( ... ): BackStackScreen<Any> { val
backstackScreens = mutableListOf<Any>() }
Root Workflow fun render( ... ): BackStackScreen<Any> { val
loginScreen = context.renderChild(LoginWorkflow) }
Root Workflow fun render( ... ): BackStackScreen<Any> { val
loginScreen = context.renderChild(LoginWorkflow) backstackScreens += loginScreen }
Root Workflow fun render( ... ): BackStackScreen<Any> { when
(renderState) { is Todo -> { } } }
Root Workflow fun render( ... ): BackStackScreen<Any> { when
(renderState) { is Todo -> { val todoScreen = context.renderChild(TodoWorkflow) backstackScreens += todoScreen } } }
Todo Workflow Login Workflow Root Workflow
Todo List Screen data class TodoListScreen( val username: String, val
todoTitles: List<String> )
Todo Workflow State data class State( val todos: List<TodoModel> )
Todo Workflow State fun initialState() = State( listOf( TodoModel( title
= "Workout", note = "Workout" ) ) )
Todo Workflow Login Workflow Props Username
Props object TodoListWorkflow : StatefulWorkflow() { data class ListProps(val username:
String) }
Todo Workflow Login Workflow Root Workflow
Root Workflow fun render( ... ): BackStackScreen<Any> { when (renderState)
{ is Todo -> { val todoScreen = context.renderChild(TodoWorkflow) backstackScreens += todoScreen } } }
Root Workflow fun render( ... ): BackStackScreen<Any> { when (renderState)
{ is Todo -> { val todoScreen = context.renderChild( TodoWorkflow, ListProps(username = renderState.username) ) } } }
View Model View
View Model class LoginViewModel : ViewModel() { val renderings: StateFlow<LoginScreen>
by lazy { renderWorkflowIn( workflow = LoginWorkflow, scope = viewModelScope, savedStateHandle = savedState ) } }
View Model class LoginViewModel : ViewModel() { val renderings: StateFlow<LoginScreen>
by lazy { renderWorkflowIn( workflow = RootWorkflow, scope = viewModelScope, savedStateHandle = savedState ) } }
Todo Layout Runner Login Layout Runner
View Registry val viewRegistry = ViewRegistry( BackStackContainer, LoginLayoutRunner, TodoListLayoutRunner )
View Registry setContentView( WorkflowLayout(this).apply { start(model.renderings, viewRegistry) } )
Todo Workflow Login Workflow Root Workflow
9:41 Login Username Password 9:41 Username
Square Workflow with Jetpack Compose
9:41 Hello 9:41 Goodbye
Workflow Binding
Rendering data class Rendering( val message: String, val onClick: ()
-> Unit )
Binding composeScreenViewFactory<HelloWorkflow.Rendering> { rendering, _ -> Text( rendering.message, modifier =
Modifier .clickable(onClick = rendering.onClick) .fillMaxSize() .wrapContentSize(Alignment.Center) ) }
Binding composeScreenViewFactory<HelloWorkflow.Rendering> { rendering, _ -> Text( rendering.message, modifier =
Modifier .clickable(onClick = rendering.onClick) .fillMaxSize() .wrapContentSize(Alignment.Center) ) }
State enum class State { Hello, Goodbye; fun theOtherState(): State
= when (this) { Hello -> Goodbye Goodbye -> Hello } }
Action val helloAction = action { state = state.theOtherState() }
Workflow object HelloWorkflow : StatefulWorkflow { }
Workflow object HelloWorkflow : StatefulWorkflow { override fun render():
Rendering = Rendering( message = renderState.name, onClick = { context.actionSink.send(helloAction) } ) }
Action UI State Rendering
9:41 Hello 9:41 Goodbye
None
None
Thank You! www.codingwithmohit.com @heyitsmohit