slackhq / circuit Goto Github PK
View Code? Open in Web Editor NEW⚡️ A Compose-driven architecture for Kotlin and Android applications.
Home Page: https://slackhq.github.io/circuit/
License: Apache License 2.0
⚡️ A Compose-driven architecture for Kotlin and Android applications.
Home Page: https://slackhq.github.io/circuit/
License: Apache License 2.0
We should be able to make these parts easy to spin up in a DI-agnostic fashion. It shouldn't assume dagger usage at the bones-level
Maybe a picture of scooby doo?
Currently we always derive state from the underlying data layers, but maybe we should have a pattern for making Presenter
instances persist across configuration changes instead. One benefit is that we could allow presenters to better cache their current state rather than rely on solutions like rememberSaveable
to cache the current state or to require the repository to cache the current state.
The go-to solution here is to hoist them into a ViewModel. Ray Ryan had a an interesting "Continuity" example in his Droidcon NYC talk below that we could draw inspiration from.
What's our testing story look like?
Use Turbine/molecule to poke along presenters and UIs? Use snapshot APIs directly? Some mix?
UI tests for UI, Turbine/snapshot tests for presenters?
Currently event and state interfaces can be any type. Should we require them all to implement empty UiState
/UiEvent
interfaces? The benefit is we can offer extension functions that are slightly more targeted, such as the current collectEvents()
API. Downsides are we add slightly more requirements and limits users from using types they don't own (String
, Instant
, ¯_(ツ)_/¯) as state.
If you scroll the main pet list feed, go to the about tab, then go back, it starts at the top of the feed again
Can borrow heavily from what KotlinPoet, LeakCanary, Worfklows, etc do
Things we'd wanna have
For some reason the CircularProgressIndicator in use on PetList isn't animating. You can see this more clearly by inserting a delay in PetListPresenter before returning any state from produceState
#AndroidDevChallenge
demos
Presenters should be able to compose and present other presenters (like the diagram in #7). Let's make sure this works and have some examples
We've got a single screen going, let's continue prototyping and make sure that multi-screen navigation works the way we expect.
Let's do it with a transition/animation too!
It would be nice if Circuit didn't depend on Android. This would simplify testing and enable JVM tests that don't require Robolectric.
Inspiration: https://twitter.com/jakewharton/status/1562424596082503680?s=21&t=btTMP94Uy5N5p2rGn58lqA
How it might be done: JakeWharton/mosaic#58
Podcast discussing this: https://www.youtube.com/watch?v=-ZExs9Gncic
This view would be focusing on a single animal
They may not all be dogs!
Swipe to refresh in the main adoption page.
Doesn't need to be in circuit core
Let's make sure we're not seeing any leaks. Ideally we should also try to run it in UI tests.
It'd be great if we could also offer some sort of strict guidance on what could be watched for leaks (maybe Ui
instances when composables are abandoned/forgotten?)
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
Renovate tried to run on this repository, but found these problems.
These updates are awaiting their schedule. Click on a checkbox to get an update now.
Warning
Renovate failed to look up the following dependencies: Failed to look up maven package com.gradle.enterprise:com.gradle.enterprise.gradle.plugin
, Failed to look up maven package com.diffplug.spotless:com.diffplug.spotless.gradle.plugin
, Failed to look up maven package com.github.ben-manes.versions:com.github.ben-manes.versions.gradle.plugin
.
Files affected: settings.gradle.kts
, gradle/libs.versions.toml
These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.
com.squareup.anvil
, com.squareup.anvil:annotations-optional
, com.squareup.anvil:annotations
)Gemfile
fastlane undefined
fastlane-plugin-swiftformat undefined
.github/workflows/benchmark.yml
actions/checkout v4
gradle/wrapper-validation-action v2
actionsdesk/lfs-warning v3.2
actions/setup-java v4
gradle/actions v3
actions/cache v4
reactivecircus/android-emulator-runner v2
reactivecircus/android-emulator-runner v2
actions/upload-artifact v4
.github/workflows/ci.yml
actions/checkout v4
actions/setup-java v4
gradle/actions v3
reactivecircus/android-emulator-runner v2
actions/upload-artifact v4
actions/checkout v4
actions/setup-java v4
ruby/setup-ruby v1
maxim-lobanov/setup-xcode v1
gradle/actions v3
actions/checkout v4
actionsdesk/lfs-warning v3.2
actions/setup-java v4
gradle/actions v3
actions/upload-artifact v4
actions/checkout v4
actions/setup-java v4
gradle/actions v3
macos 14
.github/workflows/gradle-wrapper.yml
actions/checkout v4
gradle/wrapper-validation-action v2
.github/workflows/renovate.yml
actions/checkout v4
renovatebot/github-action v39.0.5@3cef36a9aba515d8726b491905b3bc766832e221
.github/workflows/update-baseline-profiles.yml
actions/checkout v4
actions/setup-java v4
gradle/actions v3
actions/upload-artifact v4
peter-evans/create-pull-request v6
gradle.properties
settings.gradle.kts
com.gradle.enterprise 3.15.1
build.gradle.kts
backstack/gradle.properties
backstack/build.gradle.kts
circuit-codegen/gradle.properties
circuit-codegen/build.gradle.kts
circuit-codegen-annotations/gradle.properties
circuit-codegen-annotations/build.gradle.kts
circuit-foundation/gradle.properties
circuit-foundation/build.gradle.kts
circuit-overlay/gradle.properties
circuit-overlay/build.gradle.kts
circuit-retained/gradle.properties
circuit-retained/build.gradle.kts
circuit-runtime/gradle.properties
circuit-runtime/build.gradle.kts
circuit-runtime-presenter/gradle.properties
circuit-runtime-presenter/build.gradle.kts
circuit-runtime-screen/gradle.properties
circuit-runtime-screen/build.gradle.kts
circuit-runtime-ui/gradle.properties
circuit-runtime-ui/build.gradle.kts
circuit-test/gradle.properties
circuit-test/build.gradle.kts
circuitx/android/gradle.properties
circuitx/android/build.gradle.kts
circuitx/effects/gradle.properties
circuitx/effects/build.gradle.kts
circuitx/gesture-navigation/gradle.properties
circuitx/gesture-navigation/build.gradle.kts
circuitx/overlays/gradle.properties
circuitx/overlays/build.gradle.kts
gradle/libs.versions.toml
com.android.tools.build:gradle 8.2.2
androidx.activity:activity 1.8.2
androidx.activity:activity-ktx 1.8.2
androidx.activity:activity-compose 1.8.2
androidx.annotation:annotation 1.7.1
androidx.appcompat:appcompat 1.6.1
androidx.benchmark:benchmark-macro-junit4 1.2.3
androidx.browser:browser 1.7.0
com.google.accompanist:accompanist-appcompat-theme 0.34.0
com.google.accompanist:accompanist-flowlayout 0.34.0
com.google.accompanist:accompanist-pager 0.34.0
com.google.accompanist:accompanist-pager-indicators 0.34.0
com.google.accompanist:accompanist-permissions 0.34.0
com.google.accompanist:accompanist-placeholder 0.34.0
com.google.accompanist:accompanist-swiperefresh 0.34.0
com.google.accompanist:accompanist-systemuicontroller 0.34.0
androidx.compose.animation:animation 1.6.2
androidx.compose:compose-bom 2024.02.01
androidx.compose.compiler:compiler 1.5.10
androidx.compose.foundation:foundation 1.6.2
androidx.activity:activity-compose 1.8.2
androidx.constraintlayout:constraintlayout-compose 1.0.1
com.google.android.material:compose-theme-adapter 1.2.1
androidx.compose.runtime:runtime-rxjava3 1.6.2
androidx.compose.material:material-icons-core 1.6.2
androidx.compose.material:material 1.6.2
androidx.compose.material3:material3 1.2.0
androidx.compose.runtime:runtime 1.6.2
androidx.compose.runtime:runtime-livedata 1.6.2
androidx.compose.ui:ui-graphics 1.6.2
androidx.compose.ui:ui-test-junit4 1.6.2
androidx.compose.ui:ui-test-manifest 1.6.2
androidx.compose.ui:ui-text 1.6.2
androidx.compose.ui:ui-tooling 1.6.2
androidx.compose.ui:ui-tooling-data 1.6.2
androidx.compose.ui:ui-tooling-preview 1.6.2
androidx.compose.ui:ui 1.6.2
androidx.compose.ui:ui-unit 1.6.2
androidx.compose.ui:ui-util 1.6.2
androidx.compose.ui:ui-viewbinding 1.6.2
androidx.core:core-ktx 1.12.0
androidx.datastore:datastore-preferences 1.1.0-beta01
androidx.lifecycle:lifecycle-viewmodel 2.7.0
androidx.lifecycle:lifecycle-viewmodel-compose 2.7.0
androidx.loader:loader 1.1.0
androidx.profileinstaller:profileinstaller 1.3.1
androidx.test.espresso:espresso-core 3.5.1
androidx.test.ext:junit 1.1.5
androidx.test:monitor 1.6.1
androidx.test.uiautomator:uiautomator 2.3.0
com.squareup.anvil:annotations 2.4.9
com.squareup.anvil:annotations-optional 2.4.9
net.harawata:appdirs 1.2.2
org.jetbrains.kotlinx:atomicfu 0.23.2
com.google.auto.service:auto-service-annotations 1.1.1
dev.zacsweers.autoservice:auto-service-ksp 1.1.0
androidx.benchmark:benchmark-baseline-profile-gradle-plugin 1.2.3
com.github.ajalt.clikt:clikt 4.2.2
io.coil-kt:coil 2.6.0
io.coil-kt:coil-compose 2.6.0
io.coil-kt:coil-test 2.6.0
io.coil-kt.coil3:coil 3.0.0-alpha04
io.coil-kt.coil3:coil-compose-core 3.0.0-alpha04
io.coil-kt.coil3:coil-network-ktor 3.0.0-alpha04
io.coil-kt.coil3:coil-network-okhttp 3.0.0-alpha04
io.coil-kt.coil3:coil-test 3.0.0-alpha04
org.jetbrains.compose.compiler:compiler 1.5.8.1
org.jetbrains.compose.runtime:runtime 1.5.12
org.jetbrains.compose.runtime:runtime-saveable 1.5.12
org.jetbrains.compose.ui:ui 1.5.12
org.jetbrains.compose.ui:ui-util 1.5.12
org.jetbrains.compose.ui:ui-test-junit4 1.5.12
org.jetbrains.compose.ui:ui-tooling 1.5.12
org.jetbrains.compose.ui:ui-tooling-data 1.5.12
org.jetbrains.compose.ui:ui-tooling-preview 1.5.12
org.jetbrains.compose.foundation:foundation 1.5.12
org.jetbrains.compose.material:material-icons-core 1.5.12
org.jetbrains.compose.material:material-icons-extended 1.5.12
org.jetbrains.compose.material:material 1.5.12
org.jetbrains.compose.material3:material3 1.5.12
org.jetbrains.kotlinx:kotlinx-coroutines-core 1.8.0
org.jetbrains.kotlinx:kotlinx-coroutines-android 1.8.0
org.jetbrains.kotlinx:kotlinx-coroutines-swing 1.8.0
org.jetbrains.kotlinx:kotlinx-coroutines-rx3 1.8.0
org.jetbrains.kotlinx:kotlinx-coroutines-test 1.8.0
com.google.dagger:dagger-compiler 2.50
com.google.dagger:dagger 2.50
com.twitter.compose.rules:detekt 0.0.26
com.android.tools:desugar_jdk_libs 2.0.4
com.slack.eithernet:eithernet 1.8.1
com.google.dagger:hilt-core 2.50
org.jline:jline 3.25.1
org.jsoup:jsoup 1.17.2
junit:junit 4.13.2
dev.zacsweers.kctfork:core 0.4.0
dev.zacsweers.kctfork:ksp 0.4.0
org.jetbrains.kotlinx:kotlinx-datetime 0.5.0
org.jetbrains.kotlinx:kotlinx-collections-immutable 0.3.7
com.squareup:kotlinpoet 1.16.0
com.squareup:kotlinpoet-ksp 1.16.0
org.jetbrains.kotlin:kotlin-bom 1.9.22
org.jetbrains.kotlin:kotlin-compiler-embeddable 1.9.22
org.jetbrains.kotlin:kotlin-gradle-plugins-bom 1.9.22
org.jetbrains.kotlin:kotlin-test 1.9.22
com.google.devtools.ksp:symbol-processing 1.9.22-1.0.17
com.google.devtools.ksp:symbol-processing-api 1.9.22-1.0.17
com.facebook:ktfmt 0.47
io.ktor:ktor-client-core 2.3.8
io.ktor:ktor-client-content-negotiation 2.3.8
io.ktor:ktor-client-okhttp 2.3.8
io.ktor:ktor-client-js 2.3.8
io.ktor:ktor-serialization-kotlinx-json 2.3.8
com.squareup.leakcanary:leakcanary-android 2.13
com.squareup.leakcanary:leakcanary-android-instrumentation 2.13
com.slack.lint.compose:compose-lint-checks 1.3.1
com.google.android.material:material 1.11.0
app.cash.molecule:molecule-runtime 1.3.2
com.squareup.moshi:moshi 1.15.1
com.squareup.moshi:moshi-kotlin 1.15.1
com.squareup.okhttp3:okhttp 5.0.0-alpha.12
com.squareup.okhttp3:okhttp-bom 5.0.0-alpha.12
com.squareup.okhttp3:logging-interceptor 5.0.0-alpha.12
com.squareup.okio:okio 3.8.0
com.squareup.okio:okio-fakefilesystem 3.8.0
com.jakewharton.picnic:picnic 0.7.0
com.squareup.retrofit2:retrofit 2.9.0
com.squareup.retrofit2:converter-moshi 2.9.0
com.squareup.retrofit2:converter-scalars 2.9.0
org.robolectric:robolectric 4.11.1
io.github.takahirom.roborazzi:roborazzi 1.10.1
io.github.takahirom.roborazzi:roborazzi-compose 1.10.1
io.github.takahirom.roborazzi:roborazzi-junit-rule 1.10.1
io.reactivex.rxjava3:rxjava 3.1.8
app.cash.sqldelight:android-driver 2.0.1
app.cash.sqldelight:sqlite-driver 2.0.1
app.cash.sqldelight:coroutines-extensions-jvm 2.0.1
app.cash.sqldelight:primitive-adapters 2.0.1
me.saket.telephoto:zoomable-image-coil 0.8.0
com.willowtreeapps.assertk:assertk 0.28.0
androidx.test.espresso:espresso-core 3.5.1
com.google.testparameterinjector:test-parameter-injector 1.15
com.google.truth:truth 1.4.1
app.cash.turbine:turbine 1.0.0
com.benasher44:uuid 0.8.2
dev.chrisbanes.material3:material3-window-size-class-multiplatform 0.3.2
com.android.application 8.2.2
com.android.library 8.2.2
com.android.test 8.2.2
com.squareup.anvil 2.4.9
androidx.baselineprofile 1.2.3
org.jetbrains.compose 1.5.12
com.dropbox.dependency-guard 0.5.0
io.gitlab.arturbosch.detekt 1.23.5
org.jetbrains.dokka 1.9.10
wtf.emulator.gradle 0.16.2
org.jetbrains.kotlin.android 1.9.22
org.jetbrains.kotlin.plugin.atomicfu 1.9.22
org.jetbrains.kotlin.jvm 1.9.22
org.jetbrains.kotlin.kapt 1.9.22
org.jetbrains.kotlin.multiplatform 1.9.22
org.jetbrains.kotlin.plugin.parcelize 1.9.22
com.google.devtools.ksp 1.9.22-1.0.17
com.vanniktech.maven.publish 0.27.0
app.cash.molecule 1.3.2
com.jakewharton.mosaic 0.10.0
dev.zacsweers.moshix 0.25.1
app.cash.paparazzi 1.3.2
io.github.takahirom.roborazzi 1.10.1
co.touchlab.skie 0.6.1
com.diffplug.spotless 6.23.3
app.cash.sqldelight 2.0.1
com.github.ben-manes.versions 0.49.0
internal-test-utils/build.gradle.kts
samples/counter/gradle.properties
samples/counter/build.gradle.kts
samples/counter/apps/gradle.properties
samples/counter/apps/build.gradle.kts
samples/counter/mosaic/build.gradle.kts
samples/interop/build.gradle.kts
samples/star/build.gradle.kts
samples/star/apk/gradle.properties
samples/star/apk/build.gradle.kts
samples/star/benchmark/build.gradle.kts
samples/star/coil-rule/gradle.properties
samples/star/coil-rule/build.gradle.kts
samples/tacos/build.gradle.kts
samples/tutorial/build.gradle.kts
gradle/wrapper/gradle-wrapper.properties
gradle 8.6
.github/workflows/mkdocs-requirements.txt
click ==8.1.7
future ==1.0.0
Jinja2 ==3.1.3
livereload ==2.6.3
lunr ==0.7.0.post1
Markdown ==3.5.2
MarkupSafe ==2.1.5
mkdocs ==1.5.3
mkdocs-macros-plugin ==1.0.5
mkdocs-material ==9.5.11
mkdocs-material-extensions ==1.3.1
Pygments ==2.17.2
pymdown-extensions ==10.7
python-dateutil ==2.8.2
PyYAML ==6.0.1
repackage ==0.7.3
six ==1.16.0
termcolor ==2.4.0
tornado ==6.4
Better matches compose naming semantics
This is an idea for instrumentation and potentially recording+replay. It would be super cool if we could have a recording mode where someone could, say, start recording and then save the emitted states and events for a screen and then replay them for an e2e test or debuggability.
Consider this following situation.
We have an index-detail UI for the Star sample on tablets, where the left side is a grid of pets and the right side is the detail view of the currently selected pet.
In theory we could write something like this
@Composable
fun Render(state, events) {
val currentId = remember { mutableStateOf<Int?>(null) }
TwoPaneLayout(
first = {
CircuitContent(PetListScreen(...))
},
second = {
CircuitContent(PetListDetail(currentId.value))
}
)
}
How do we get the currentId state into the list UI and listen to selection changes?
currentId
state? How is that plumbed down? Is it in the screen? That's no longer parcelable then.Could be a good demo for a transition with a hero element (photo animates to full screen)
Related: #50
This would let us push some more stuff into the presenter
interface PresenterScope<UiEvent> {
fun onEvent(body: (UiEvent) -> Unit)
}
interface Presenter<UiState, UiEvent> {
@Composable fun PresenterScope<UiEvent>.present(): UiState
}
Then a presenter looks like this
@Composable
override fun PresenterScope<Event>.present(): State {
val state = ...
onEvent { event ->
// ...
}
return state
}
Background is white with dark text in both day and night. We should make this use a dark theme when in dark mode
We have a few needs in this space! Currently, we offer some animation APIs via the backstack APIs, but let's try to flush this out into a full API.
AnimationFactory
API to allow providing animations between two given screens. This would allow us to contribute a transition for these screens as keys.CrossFade
, let's make a few more available like PushLeft
, PushRight
, etc.AnimationFactory
API? What about looking at what https://github.com/rjrjr/compose-backstack does?A good example for this would be to make the petlist -> detail transition use the iamge as a shared hero element
If you open a detail page and swipe through a couple of photos, then rotate, it starts back at the first index
Currently all our examples are 1:1 between UI and the "whole screen". Let's prototype some stuff with nested UIs, like this example. Maybe #4 would be a good spot for this
class ProfilePresenterFactory @Inject constructor(
val headerPresenter: ProfilerHeaderPresenter.Factory,
val actionsPresenter: ProfilerActionsPresenter.Factory,
val detailsPresenter: ProfilerDetailsPresenter.Factory,
val callScreenRouter: CallScreenRouter.Factory
) : PresenterFactory {
override fun create(screen: Screen, navigator: Navigator): Presenter<*, *>? {
return when (screen) {
is ProfileHeader -> headerPresenter.create(screen)
is ProfileActions -> actionsPresenter.create(screen, callScreenRouter.create(navigator))
is ProfileDetails -> detailsPresenter.create(screen)
else -> null
}
}
}
Are there some common boilerplate areas we should try to generate away?
For Uis with no events, we should just use Nothing
. Should we just document this or offer a StaticUi<UiState>
+ StaticPresenter<UiState>
function combo?
interface StaticUi<UiState> : Ui<UiState, Nothing>
interface StaticPresenter<UiState> : Presenter<UiState, Nothing>
TODO
Currently we have two navigators - one in circuit core and one in the backstack. We should dedupe these. Maybe rename the core one to just Circuit
, kinda similar to how moshi and retrofit name their core class the name itself?
circuit.goTo(...)
circuit.pop()
Currently we're hardcoding in some stub data, let's make this look a little more realistic
It would be good to have a sample that shows a different UI in landscape and portrait.
Do we make two different UIs? Dynamically configure in the single UI impl?
Can we build in support for tracing? This would help adoption (devs would get tracing for free) and help when debugging.
What's our way of returning results from request flows?
What's it look like to jump from/to the standard view system?
From is currently like this
val circuit = ...
val composeView = ComposeView(context)
composeView.setContent {
ProvideCircuit(circuit) {
CircuitContent(FavoritesScreen())
}
}
Do we want to offer something more? Or is this trivial enough to be left to the above?
class CircuitView(context, circuit)
val circuitView = CircuitView(...)
circuitView.setContent { ... }
Similarly, we want to be able to power view-based UIs from a circuit presenter? Current interop would just look like this, do we want to do anything more?
class ExistingCustomViewUi : Ui<State, Event> {
@Composable
fun Render(state: State, events: (Event) -> Unit) {
AndroidView(
modifier = ...
factory = { context ->
ExistingCustomView(context).apply {
setOnClickListener { events(Event.Click) }
}
},
update = { view ->
view.setState(state)
}
}
}
What guidelines do we want to offer for performance considerations?
Just some basic stuff for contributing to scopes
Intent as a starting point, but what happens from there? Store Screen
requests in the bundles?
Related to #9. Requires splitting our sample app into an app shell + library though due to cashapp/paparazzi#107.
Currently we do this so they can go in rememberSaveable
and survive config changes, is there an alternative solution?
We want to do this as an alternative, sharper solution to retaining on configuration state that's more flexible than parcelable/rememberSaveable
.
We'll have multiple subprojects, let's get this configuration de-duped
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.