Skip to content

Render ProgressView and Toggle#12

Merged
colemancda merged 9 commits into
masterfrom
feature/form-controls
Jul 22, 2026
Merged

Render ProgressView and Toggle#12
colemancda merged 9 commits into
masterfrom
feature/form-controls

Conversation

@colemancda

@colemancda colemancda commented Jul 22, 2026

Copy link
Copy Markdown
Member

First half of the form controls work: ProgressView and Toggle. Stepper, Slider, TextField, and Picker follow separately.

Rebased onto master now that PureSwift/Android#45 has merged — the ProgressBar binding fix this depends on is upstream, so this no longer carries any external blocker.

ProgressView

_FractionalProgressView and _IndeterminateProgressView are the renderer hooks the default progress style already emits, so both get a primitive conformance backed by ProgressBar.

Determinate and indeterminate progress are separate widgets rather than one widget toggling setIndeterminate, because the horizontal track is only selectable through a style attribute at construction time — which is what needed the upstream binding fix, since ProgressBar had no constructors at all. Progress is reported on a fixed integer scale since ProgressBar works in whole steps while ProgressView reports a fraction.

Toggle

Rendered as a Material switch, bound to its Bool.

This is supplied as the default toggle style rather than as a primitive conformance on Toggle. A primitive conformance looks like it should work, but Toggle declares @Environment(\.toggleStyle), which is read during environment injection whether or not the body is ever evaluated — and _ToggleStyleKey traps unless the renderer supplies a default. That crashed on launch. Going through the style system fixes the crash and has the side benefit that .toggleStyle(_:) keeps working.

The checked-change callback needs a new JNI bridge, CompoundButtonOnCheckedChangeListener, following ViewOnClickListener exactly. The listener guards against writing back when the value already matches, so the reconciler setting the checked state during an update doesn't churn the binding.

Build script

build-swift.sh copied only libSwiftAndroidApp.so into jniLibs. After the upstream linking changes the app also loads libSwiftJava.so at runtime and dies on startup with UnsatisfiedLinkError without it, so the script now copies every built shared library. This is unrelated to the controls themselves but breaks any fresh build, so it is fixed here.

Verification

On the emulator, against the merged upstream bindings, via a new Controls gallery screen:

  • The switch flips and drives its bound @State, with a label reflecting it.
  • The indeterminate spinner animates.
  • The determinate bar tracks its value across repeated updates (25% → 75%).

The gallery screen is a flat eleven-child block, which also exercises the view builder change from #11.

Determinate progress uses the horizontal track and indeterminate progress the
platform spinner; the two are separate widgets because the track is only
selectable through a style attribute at construction.
Supplied as the default toggle style rather than a primitive conformance, so
the binding round-trips and .toggleStyle(_:) keeps working.
The key traps without one, and is read whenever a toggle is in the view tree
rather than only when its body is evaluated.
The view builder no longer caps a block at ten children, so the sections no
longer need to be split into separate views.
The app links against `libSwiftJava.so` at runtime and fails to start when
only `libSwiftAndroidApp.so` is copied.
@colemancda
colemancda force-pushed the feature/form-controls branch from fb90a59 to 4cc8229 Compare July 22, 2026 14:30
@colemancda
colemancda merged commit ae23813 into master Jul 22, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant