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
1
430
Using Square Workflow for Android & iOS
Mohit S
May 18, 2022
Tweet
Share
More Decks by Mohit S
See All by Mohit S
Guide to Improving Compose Performance
heyitsmohit
0
230
Building Shared UIs across Platforms with Compose
heyitsmohit
1
630
Building Multiplatform Apps with Compose
heyitsmohit
2
510
Building StateFlows with Jetpack Compose
heyitsmohit
6
1.9k
Building Android Testing Infrastructure
heyitsmohit
1
490
Migrating to Kotlin State & Shared Flows
heyitsmohit
1
790
Building Android Infrastructure Teams at Scale
heyitsmohit
3
320
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
560
Challenges of Building Kotlin Multiplatform Libraries
heyitsmohit
1
440
Other Decks in Programming
See All in Programming
実践 Dev Containers × Claude Code
touyu
1
120
What's new in Adaptive Android development
fornewid
0
130
CIを整備してメンテナンスを生成AIに任せる
hazumirr
0
520
Claude Code派?Gemini CLI派? みんなで比較LT会!_20250716
junholee
1
800
Dart 参戦!!静的型付き言語界の隠れた実力者
kno3a87
0
160
Strands Agents で実現する名刺解析アーキテクチャ
omiya0555
1
110
Comparing decimals in Swift Testing
417_72ki
0
160
構文解析器入門
ydah
7
2k
[Codecon - 2025] Como não odiar seus testes
camilacampos
0
100
NEWT Backend Evolution
xpromx
1
170
JetBrainsのAI機能の紹介 #jjug
yusuke
0
180
Gemini CLIの"強み"を知る! Gemini CLIとClaude Codeを比較してみた!
kotahisafuru
3
920
Featured
See All Featured
The Pragmatic Product Professional
lauravandoore
36
6.8k
A Modern Web Designer's Workflow
chriscoyier
695
190k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.5k
Designing Experiences People Love
moore
142
24k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
53
2.9k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
8
750
Into the Great Unknown - MozCon
thekraken
40
2k
A better future with KSS
kneath
238
17k
Facilitating Awesome Meetings
lara
54
6.5k
Measuring & Analyzing Core Web Vitals
bluesmoon
7
540
We Have a Design System, Now What?
morganepeng
53
7.7k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
44
2.4k
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