Skip to content

Commit 549a127

Browse files
committed
wip: Modernize tutorial final code
Long lived branch (which I will totally rebase like mad) accumulating incremental tutorial updates. - `tutorial-final` is updated (especially `WelcomeWorkflow`) and its tests pass - `tutorial-base` done - `README.md` done - `Tutorial1.md` done - `tutorial-1-complete` done It's time to get the Tutorial caught up with undeprecated API: - Use `AndroidScreen` and drop `ViewRegistry` - Better, more consistent use of `BackStackScreen` - Use `View.setBackHandler` - Use `TextController` - Use `RequestContext.eventHandler` - Delete a lot of `// Exactly what the function name and the code say` comments - Hint at the existance of `ComposeWorkflow` without turning this into a complete rewrite - More consistent naming, code style for actions and event handlers in `Screen` renderings - Event handler fields are named `onVerbPhrase`, like `onBackPressed` - Action names are verb phrases describing the action that is being taken, not the event that is being handled - Output names are generally in terms of the semantic event being reported to the parent, rather than describing what the parent will do Thus: ```kotlin return TodoListScreen( onRowPressed = { context.actionSink.send(reportSelection(it)) } ``` ```kotlin private fun reportSelection(index: Int) = action { // Tell our parent that a todo item was selected. setOutput(TodoSelected(index)) } ``` (Although most of these will be inlined as `eventHandler {}` calls.) When the rest of the `tutorial-N` modules have been updated I'll put this up for review. I've mainly focussed on updating what exists, not extending its coverage further. Once this is merged I'd like to follow and do more: - Introduce `Overlay`. - How about move `TodoEditScreen` to a `BottomSheetDialog`? - Could be too noisy, `BottomSheetDialog` is a bastard. - If that's the case create a separate sample for it. - Introduce `Compose`. - I think the way to go is add a fifth step that updates `TodoListScreen`, talk about getting rid of that `RecyclerView`. - Should also mention at the top that people can use `ComposeScreen` instead of `AndroidScreen`; and on the `TextController` page call out `asMutableState()`
1 parent 4216d38 commit 549a127

File tree

68 files changed

+1031
-1134
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1031
-1134
lines changed

samples/tutorial/README.md

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,9 @@
11
# Tutorial
22

3-
## Stale Docs Warning
4-
5-
**This tutorial is tied to an older version of Workflow, and relies on API that has been deprecated or deleted.**
6-
The general concepts are the same, and refactoring to the current API is straightforward,
7-
so it is still worthwhile to work through the tutorial in its current state until we find time to update it.
8-
(Track that work [here](https://github.com/square/workflow-kotlin/issues/905)
9-
and [here](https://github.com/square/workflow-kotlin/issues/884).)
10-
11-
Here's a summary of what has changed, and what replaces what:
12-
13-
- Use of `ViewRegistry` is now optional, and rare.
14-
Have your renderings implement `AndroidScreen` or `ComposeScreen` to avoid it.
15-
- The API for binding a rendering to UI code has changed as follows, and can all
16-
be avoided if you use `ComposeScreen`:
17-
- `ViewFactory<in RenderingT : Any>` is replaced by `ScreenViewFactory<in ScreenT : Screen>`.
18-
-`LayoutRunner<RenderingT : Any>` is replaced by `ScreenViewRunner<in ScreenT : Screen>`.
19-
- `LayoutRunner.bind` is replaced by `ScreenViewFactory.fromViewBinding`.
20-
- `BackStackScreen` has been moved to package `com.squareup.workflow1.ui.navigation`.
21-
- `EditText.updateText` and `EditText.setTextChangedListener` are replaced by `TextController`
22-
233
## Overview
244

255
Oh hi! Looks like you want build some software with Workflows! It's a bit different from traditional
26-
Android development, so let's go through building a simple little TODO app to get the basics down.
6+
Android development, so let's go through building a simple little To-Do app to get the basics down.
277

288
## Layout
299

@@ -33,7 +13,7 @@ To help with the setup, we have created a few helper modules:
3313

3414
- `tutorial-views`: A set of 3 views for the 3 screens we will be building, `Welcome`, `TodoList`,
3515
and `TodoEdit`.
36-
- `tutorial-base`: This is the starting point to build out the tutorial. It contains layouts that host the views from `TutorialViews` to see how they display.
16+
- `tutorial-base`: This is the starting point to build out the tutorial.
3717
- `tutorial-final`: This is an example of the completed tutorial - could be used as a reference if
3818
you get stuck.
3919

samples/tutorial/Tutorial1.md

Lines changed: 296 additions & 157 deletions
Large diffs are not rendered by default.

samples/tutorial/Tutorial2.md

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,6 @@
22

33
_Multiple Screens and Navigation_
44

5-
## Stale Docs Warning
6-
7-
**This tutorial is tied to an older version of Workflow, and relies on API that has been deprecated or deleted.**
8-
The general concepts are the same, and refactoring to the current API is straightforward,
9-
so it is still worthwhile to work through the tutorial in its current state until we find time to update it.
10-
(Track that work [here](https://github.com/square/workflow-kotlin/issues/905)
11-
and [here](https://github.com/square/workflow-kotlin/issues/884).)
12-
13-
Here's a summary of what has changed, and what replaces what:
14-
15-
- Use of `ViewRegistry` is now optional, and rare.
16-
Have your renderings implement `AndroidScreen` or `ComposeScreen` to avoid it.
17-
- The API for binding a rendering to UI code has changed as follows, and can all
18-
be avoided if you use `ComposeScreen`:
19-
- `ViewFactory<in RenderingT : Any>` is replaced by `ScreenViewFactory<in ScreenT : Screen>`.
20-
-`LayoutRunner<RenderingT : Any>` is replaced by `ScreenViewRunner<in ScreenT : Screen>`.
21-
- `LayoutRunner.bind` is replaced by `ScreenViewFactory.fromViewBinding`.
22-
- `BackStackScreen` has been moved to package `com.squareup.workflow1.ui.navigation`.
23-
- `EditText.updateText` and `EditText.setTextChangedListener` are replaced by `TextController`
24-
255
## Setup
266

277
To follow this tutorial, launch Android Studio and open this folder (`samples/tutorial`).
@@ -539,29 +519,18 @@ class BackStackScreen<StackedT : Any>(
539519
}
540520
```
541521

542-
The `BackStackScreen` contains a list of all screens in the back stack that are specified on each render pass. `BackStackScreen` is part of the `workflow-ui-container-android` artifact. Update `build.gradle` to include this dependency:
543-
544-
```groovy
545-
dependencies {
546-
// ...
547-
implementation deps.workflow.container_android
548-
implementation deps.workflow.core_android
549-
}
550-
```
551-
552522
Update the `RootWorkflow` to return a `BackStackScreen` with a list of back stack items:
553523

554524
```kotlin
555-
object RootWorkflow : StatefulWorkflow<Unit, State, Nothing, BackStackScreen<Any>>() {
525+
object RootWorkflow : StatefulWorkflow<Unit, State, Nothing, BackStackScreen<*>>() {
556526

557527
//
558528

559-
@OptIn(WorkflowUiExperimentalApi::class)
560-
override fun render(
529+
override fun render(
561530
renderProps: Unit,
562531
renderState: State,
563532
context: RenderContext
564-
): BackStackScreen<Any> {
533+
): BackStackScreen<*> {
565534

566535
// Our list of back stack items. Will always include the "WelcomeScreen".
567536
val backstackScreens = mutableListOf<Any>()

samples/tutorial/Tutorial3.md

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,6 @@
11
# Step 3
22

3-
## Stale Docs Warning
4-
5-
**This tutorial is tied to an older version of Workflow, and relies on API that has been deprecated or deleted.**
6-
The general concepts are the same, and refactoring to the current API is straightforward,
7-
so it is still worthwhile to work through the tutorial in its current state until we find time to update it.
8-
(Track that work [here](https://github.com/square/workflow-kotlin/issues/905)
9-
and [here](https://github.com/square/workflow-kotlin/issues/884).)
10-
11-
Here's a summary of what has changed, and what replaces what:
12-
13-
- Use of `ViewRegistry` is now optional, and rare.
14-
Have your renderings implement `AndroidScreen` or `ComposeScreen` to avoid it.
15-
- The API for binding a rendering to UI code has changed as follows, and can all
16-
be avoided if you use `ComposeScreen`:
17-
- `ViewFactory<in RenderingT : Any>` is replaced by `ScreenViewFactory<in ScreenT : Screen>`.
18-
-`LayoutRunner<RenderingT : Any>` is replaced by `ScreenViewRunner<in ScreenT : Screen>`.
19-
- `LayoutRunner.bind` is replaced by `ScreenViewFactory.fromViewBinding`.
20-
- `BackStackScreen` has been moved to package `com.squareup.workflow1.ui.navigation`.
21-
- `EditText.updateText` and `EditText.setTextChangedListener` are replaced by `TextController`
3+
_State throughout a tree of workflows_
224

235
## Setup
246

@@ -284,15 +266,15 @@ object TodoListWorkflow : StatefulWorkflow<ListProps, State, Back, List<Any>>()
284266
Now that `TodoListWorkflow` renders a `List<Any>` we need to update its parent `RootWorkflow` to accept this list:
285267

286268
```kotlin
287-
object RootWorkflow : StatefulWorkflow<Unit, State, Nothing, BackStackScreen<Any>>() {
269+
object RootWorkflow : StatefulWorkflow<Unit, State, Nothing, BackStackScreen<*>>() {
288270

289271
// ...
290272

291273
override fun render(
292274
renderProps: Unit,
293275
renderState: State,
294276
context: RenderContext
295-
): BackStackScreen<Any> {
277+
): BackStackScreen<*> {
296278
// ..
297279
// When the state is Todo, defer to the TodoListWorkflow.
298280
is Todo -> {

samples/tutorial/Tutorial4.md

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,6 @@
22

33
_Refactoring and rebalancing a tree of Workflows_
44

5-
## Stale Docs Warning
6-
7-
**This tutorial is tied to an older version of Workflow, and relies on API that has been deprecated or deleted.**
8-
The general concepts are the same, and refactoring to the current API is straightforward,
9-
so it is still worthwhile to work through the tutorial in its current state until we find time to update it.
10-
(Track that work [here](https://github.com/square/workflow-kotlin/issues/905)
11-
and [here](https://github.com/square/workflow-kotlin/issues/884).)
12-
13-
Here's a summary of what has changed, and what replaces what:
14-
15-
- Use of `ViewRegistry` is now optional, and rare.
16-
Have your renderings implement `AndroidScreen` or `ComposeScreen` to avoid it.
17-
- The API for binding a rendering to UI code has changed as follows, and can all
18-
be avoided if you use `ComposeScreen`:
19-
- `ViewFactory<in RenderingT : Any>` is replaced by `ScreenViewFactory<in ScreenT : Screen>`.
20-
- `LayoutRunner<RenderingT : Any>` is replaced by `ScreenViewRunner<in ScreenT : Screen>`.
21-
- `LayoutRunner.bind` is replaced by `ScreenViewFactory.fromViewBinding`.
22-
- `BackStackScreen` has been moved to package `com.squareup.workflow1.ui.navigation`.
23-
- `EditText.updateText` and `EditText.setTextChangedListener` are replaced by `TextController`
24-
255
## Setup
266

277
To follow this tutorial, launch Android Studio and open this folder (`samples/tutorial`).
@@ -284,15 +264,15 @@ object TodoWorkflow : StatefulWorkflow<TodoProps, State, Back, List<Any>>() {
284264
So far `RootWorkflow` is still deferring to the `TodoListWorkflow`. Update the `RootWorkflow` to defer to the `TodoWorkflow` for rendering the `Todo` state. This will get us back into a state where we can build again (albeit without editing support):
285265

286266
```kotlin
287-
object RootWorkflow : StatefulWorkflow<Unit, State, Nothing, BackStackScreen<Any>>() {
267+
object RootWorkflow : StatefulWorkflow<Unit, State, Nothing, BackStackScreen<*>>() {
288268

289269
//
290270

291271
override fun render(
292272
renderProps: Unit,
293273
renderState: State,
294274
context: RenderContext
295-
): BackStackScreen<Any> {
275+
): BackStackScreen<*> {
296276

297277
//
298278

samples/tutorial/Tutorial5.md

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,6 @@
22

33
_Unit and Integration Testing Workflows_
44

5-
## Stale Docs Warning
6-
7-
**This tutorial is tied to an older version of Workflow, and relies on API that has been deprecated or deleted.**
8-
The general concepts are the same, and refactoring to the current API is straightforward,
9-
so it is still worthwhile to work through the tutorial in its current state until we find time to update it.
10-
(Track that work [here](https://github.com/square/workflow-kotlin/issues/905)
11-
and [here](https://github.com/square/workflow-kotlin/issues/884).)
12-
13-
Here's a summary of what has changed, and what replaces what:
14-
15-
- Use of `ViewRegistry` is now optional, and rare.
16-
Have your renderings implement `AndroidScreen` or `ComposeScreen` to avoid it.
17-
- The API for binding a rendering to UI code has changed as follows, and can all
18-
be avoided if you use `ComposeScreen`:
19-
- `ViewFactory<in RenderingT : Any>` is replaced by `ScreenViewFactory<in ScreenT : Screen>`.
20-
- `LayoutRunner<RenderingT : Any>` is replaced by `ScreenViewRunner<in ScreenT : Screen>`.
21-
- `LayoutRunner.bind` is replaced by `ScreenViewFactory.fromViewBinding`.
22-
- `BackStackScreen` has been moved to package `com.squareup.workflow1.ui.navigation`.
23-
- `EditText.updateText` and `EditText.setTextChangedListener` are replaced by `TextController`
24-
255
## Setup
266

277
To follow this tutorial, launch Android Studio and open this folder (`samples/tutorial`).

samples/tutorial/build.gradle

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
33
// Top-level build file where you can add configuration options common to all sub-projects/modules.
44
buildscript {
55
ext {
6-
kotlin_version = '1.8.10'
7-
workflow_version = "1.12.1-beta04"
6+
kotlin_version = '1.9.24'
7+
// TODO NO SNAPSHOT!
8+
workflow_version = "1.19.0-SNAPSHOT"
89

910
deps = [
1011
activityktx: 'androidx.activity:activity-ktx:1.3.0',
@@ -23,7 +24,8 @@ buildscript {
2324
viewmodelsavedstate: 'androidx.lifecycle:lifecycle-viewmodel-savedstate:1.1.0',
2425
workflow: [
2526
core_android: "com.squareup.workflow1:workflow-ui-core-android:$workflow_version",
26-
container_android: "com.squareup.workflow1:workflow-ui-container-android:$workflow_version",
27+
compose: "com.squareup.workflow1:workflow-ui-compose:$workflow_version",
28+
compose_tooling: "com.squareup.workflow1:workflow-ui-compose-tooling:$workflow_version",
2729
testing: "com.squareup.workflow1:workflow-testing-jvm:$workflow_version",
2830
],
2931
]
-58 KB
Binary file not shown.
-379 KB
Loading
135 KB
Loading

samples/tutorial/images/welcome.png

5.26 KB
Loading
112 KB
Loading

samples/tutorial/tutorial-1-complete/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ android {
88

99
defaultConfig {
1010
applicationId "com.squareup.workflow.tutorial"
11-
minSdk = 21
11+
minSdk = 24
1212
targetSdk = 33
1313
versionCode 1
1414
versionName "1.0"

samples/tutorial/tutorial-1-complete/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
android:label="@string/app_name"
88
android:roundIcon="@mipmap/ic_launcher_round"
99
android:supportsRtl="true"
10-
android:theme="@style/Theme.WorkflowTutorial">
10+
android:theme="@style/Theme.WorkflowTutorial"
11+
>
1112
<activity
1213
android:name=".TutorialActivity"
1314
android:exported="true">
Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
@file:OptIn(WorkflowUiExperimentalApi::class)
2-
31
package workflow.tutorial
42

53
import android.os.Bundle
@@ -8,36 +6,47 @@ import androidx.appcompat.app.AppCompatActivity
86
import androidx.lifecycle.SavedStateHandle
97
import androidx.lifecycle.ViewModel
108
import androidx.lifecycle.viewModelScope
11-
import com.squareup.workflow1.ui.ViewRegistry
9+
import com.squareup.workflow1.RuntimeConfigOptions
10+
import com.squareup.workflow1.WorkflowExperimentalRuntime
11+
import com.squareup.workflow1.ui.Screen
1212
import com.squareup.workflow1.ui.WorkflowLayout
13-
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
1413
import com.squareup.workflow1.ui.renderWorkflowIn
15-
import kotlinx.coroutines.flow.StateFlow
16-
17-
// This doesn't look like much right now, but we'll add more layout runners shortly.
18-
private val viewRegistry = ViewRegistry(WelcomeLayoutRunner)
14+
import kotlinx.coroutines.flow.Flow
1915

2016
class TutorialActivity : AppCompatActivity() {
2117

2218
override fun onCreate(savedInstanceState: Bundle?) {
2319
super.onCreate(savedInstanceState)
2420

25-
// Use an AndroidX ViewModel to start and host an instance of the workflow runtime that runs
26-
// the WelcomeWorkflow and sets the activity's content view using our view factories.
21+
// We use an AndroidX `ViewModel` and its CoroutineScope to start and host
22+
// an instance of the workflow runtime that runs the WelcomeWorkflow.
23+
// This ensures that our runtime will survive as new `Activity` instances
24+
// are created for configuration changes.
2725
val model: TutorialViewModel by viewModels()
2826

2927
setContentView(
30-
WorkflowLayout(this).apply { start(model.renderings, viewRegistry) }
28+
WorkflowLayout(this).apply {
29+
take(lifecycle, model.renderings)
30+
}
3131
)
3232
}
33-
}
3433

35-
class TutorialViewModel(savedState: SavedStateHandle) : ViewModel() {
36-
val renderings: StateFlow<WelcomeScreen> by lazy {
37-
renderWorkflowIn(
38-
workflow = WelcomeWorkflow,
39-
scope = viewModelScope,
40-
savedStateHandle = savedState,
41-
)
34+
class TutorialViewModel(savedState: SavedStateHandle) : ViewModel() {
35+
36+
// We opt in to WorkflowExperimentalRuntime in order turn on all the
37+
// optimizations controlled by the runtimeConfig.
38+
//
39+
// They are in production use at Square, will not be listed as
40+
// experimental much longer, and will soon be enabled by default.
41+
// In the meantime it is much easier to use them from the start
42+
// than to turn them on down the road.
43+
@OptIn(WorkflowExperimentalRuntime::class)
44+
val renderings: Flow<Screen> by lazy {
45+
renderWorkflowIn(
46+
workflow = RootNavigationWorkflow,
47+
scope = viewModelScope,
48+
savedStateHandle = savedState,
49+
runtimeConfig = RuntimeConfigOptions.ALL
50+
)
4251
}
4352
}

samples/tutorial/tutorial-1-complete/src/main/java/workflow/tutorial/WelcomeLayoutRunner.kt

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)