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
340
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
43
Building Shared UIs across Platforms with Compose
heyitsmohit
1
430
Building Multiplatform Apps with Compose
heyitsmohit
2
340
Building StateFlows with Jetpack Compose
heyitsmohit
6
1.6k
Building Android Testing Infrastructure
heyitsmohit
1
350
Migrating to Kotlin State & Shared Flows
heyitsmohit
1
610
Building Android Infrastructure Teams at Scale
heyitsmohit
2
250
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
460
Challenges of Building Kotlin Multiplatform Libraries
heyitsmohit
1
380
Other Decks in Programming
See All in Programming
日付と正規化
megmogmog1965
0
140
3 Effective Rules for Success with Signals in Angular
manfredsteyer
PRO
0
120
DMMプラットフォームにおけるTiDBの導入から運用まで
pospome
7
3k
スクラムマスターって孤独じゃないですか?
yoshitaroyoyo
1
140
20240706_CDKConf
takuyay0ne
0
1.2k
From Spring Boot 2 to Spring Boot 3 with Java 22 and Jakarta EE
ivargrimstad
0
1.9k
Composing an API the *right* way (Droidcon Berlin 2024)
zsmb
1
450
実用的かつリーズナブルな 「Azure × Gemini × LINE」~キャラクターBot 実装ライブデモ~
tomodo_ysys
1
170
AWS初心者ってどうやってAWSを学ぶ?〜アプリエンジニアがやってよかったアーキテクチャ学習方法〜
yamanashi_ren01
0
190
最古の関数型言語「Lisp」ことはじめ / lisp_in_kamiyama
uhooi
1
190
大規模マルチテナントを解決するYugabyteDBという選択肢
nnaka2992
1
250
「2024年版 Kotlin サーバーサイドプログラミング実践開発」の補講 〜O/Rマッパー編〜
n_takehata
2
260
Featured
See All Featured
10 Git Anti Patterns You Should be Aware of
lemiorhan
652
58k
Six Lessons from altMBA
skipperchong
24
3.2k
Music & Morning Musume
bryan
43
5.9k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
248
20k
Making the Leap to Tech Lead
cromwellryan
127
8.7k
How to Ace a Technical Interview
jacobian
274
23k
Atom: Resistance is Futile
akmur
261
25k
Building a Modern Day E-commerce SEO Strategy
aleyda
25
6.7k
Principles of Awesome APIs and How to Build Them.
keavy
124
16k
The Cost Of JavaScript in 2023
addyosmani
31
4.7k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
155
14k
Docker and Python
trallard
37
2.9k
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