Skip to content

feat!: release 4.0.0 with split view and desktop styles - #210

Merged
yadaniyil merged 110 commits into
masterfrom
release/4.0.0
Sep 25, 2026
Merged

yadaniyil merged 110 commits into
masterfrom
release/4.0.0

Conversation

@yadaniyil

Copy link
Copy Markdown
Contributor

settings_ui 4.0.0. This includes #209 (the 2026 look and the new iOS switch), so merging this closes it too.

What's in it

  • Built on material_ui / cupertino_ui (fix: migrate to material_ui and cupertino_ui packages #207). Needs Flutter 3.44+. Apps still on package:flutter/material.dart stay on ^3.0.1.
  • 2026 look for iOS 26/27, Android 16/17 and Chrome, plus CupertinoSettingsSwitch, an iOS 26 switch drawn in Flutter (feat!: match the 2026 iOS, Android and Chrome settings look #209).
  • New desktop styles: macOS System Settings (MacosSettingsSwitch), Windows 11 Settings (FluentSettingsSwitch) and GNOME Settings for Linux (AdwaitaSettingsSwitch, AdwaitaPanDownIcon). They were checked against real screenshots of each app.
  • Split view: SettingsSplitView puts the list and the selected page side by side on iPad, tablets, foldables, desktop and the web, with a native sidebar per style. SettingsTile.navigation(destination: SettingsDestination(...)) also opens pages on phones without any Navigator code.
  • Bug fixes:
    • forced brightness works in every style
    • alignment follows the list's own width
    • paddings are wired through
    • empty sections render nothing
    • disabled tiles ignore the keyboard
    • screen readers read every tile as its own item
  • Docs:
    • the README now opens with an agent prompt, with the other prompts in doc/agent-prompts.md
    • a new llms.txt with a build recipe
    • the agent instructions moved to AGENTS.md
    • new README images
  • Example app:
    • showcase, split view demo and desktop replica screens
    • a macOS and a Linux runner
    • integration tests for the gallery and split view flows

Checks

  • 733 tests on Flutter 3.47.2 and 3.44.6; format and analyze are clean on both.
  • Integration tests on the iPhone 17 Pro and iPad simulators, the Android phone and foldable emulators, and macOS.
  • Headless Chrome smoke test of every example screen in every style.
  • flutter pub publish --dry-run: 0 warnings, 2 MB.

Known issue: on iOS, VoiceOver frames can be off after the split view changes layout (rotate or resize). This is a Flutter engine bug, reproduced without this package, and it's noted in the README.

🤖 Generated with Claude Code

yadaniyil and others added 30 commits September 24, 2026 19:34
Measured against iOS 27 (simulator), Android 17 (Pixel 10) and Chrome 153:

- iOS/macOS/Windows: 26pt continuous-corner cards, 20pt margins, 52pt
  rows with 17pt text, 17pt semibold section headers, secondary grey
- Android/Linux/Fuchsia: each tile on its own card (20dp group ends,
  4dp inner corners, 2dp gaps) on a surfaceContainer page, 16sp titles,
  tighter rows, check/cross switch thumbs
- Web: white 8px cards with a light shadow, 14/13px text, 20px icons,
  chevrons on navigation tiles, near-black section titles, 680px column

Adds tests for the new layout and updates the README screenshots and
the example's iOS headers to sentence case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds CupertinoSettingsSwitch, a pure-Flutter copy of the iOS 26/27
UISwitch, measured on the iOS 27 simulator, and uses it in iOS-style
switch tiles instead of CupertinoSwitch.

- Rest: 63x28 continuous-corner track, 37x24 white pill thumb, 2pt
  inset, 22pt travel. OFF #C5C5C7/#5A5A5E, ON system green.
- Pressed or dragged: the thumb springs into a 59.33x39.67 glass lens
  that overflows the track. It is painted with a CustomPainter only:
  a squeezed, lifted track image with flares, refracted bands, a
  hairline rim, a specular line and a soft shadow below.
- Motion: spring press with a small overshoot, frosted phases, and a
  tinted thumb that fades to white after release. Drags follow the
  finger with rubber-banding and flip the value 1pt past the far end.
  Reduced motion skips the lens.
- Horizontal drags only, so lists still scroll. Light haptic on iOS and
  macOS. Toggle semantics, keyboard activation, 50% opacity when
  disabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
flutter create picked up an unrelated development team when the iOS
runner was regenerated. Leave it unset so contributors choose their own
team in Xcode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CLAUDE.md now only imports AGENTS.md, so Claude and Codex read the same file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- SettingsList.brightness now overrides the app brightness.
- ApplicationType.both decides by the platform the app runs on (iOS and
  macOS read the CupertinoTheme), not by the platform field, which is
  null when auto-detecting.
- crossAxisAlignment: start puts the content column at the start edge
  (RTL-aware); the spare width goes to the end.
- The default padding uses the width the list gets (LayoutBuilder), not
  the screen width, so a list in a narrow pane fits the pane.
- An empty SettingsSection renders nothing instead of crashing on iOS.
- SettingsTile passes titleDescriptionPadding (not titlePadding) on iOS,
  and trailingPadding/descriptionPadding on iOS. Android and web now
  apply titlePadding.
- Android and web disabled switches honor inactiveSwitchColor in every
  layout; the web switch next to a trailing widget drops the hard-coded
  blue active color.
- Example: Flutter >=3.44 / Dart >=3.12, material_ui and cupertino_ui as
  direct dependencies.
- CHANGELOG: 3.0.1 entry and the fixes above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README: prompts for coding agents (a hero prompt plus four task
  prompts) that point at llms.txt, and the 2026 look for each style.
  Correct brightness, crossAxisAlignment and split-pane padding,
  applicationType.both, where value/description/titleDescription show
  per style, switch-row taps, iOS system colors, the material_ui and
  cupertino_ui direct dependency note, SettingsSection.titlePadding,
  the SettingsTile padding params and CupertinoSettingsSwitch.
- llms.txt: install and version choice, a minimal example that
  compiles, the full parameter reference, the platform to style
  table, SettingsThemeData, CupertinoSettingsSwitch, testing notes and
  rules for agents.
- AGENTS.md: 4.0.0, Flutter 3.44 with material_ui/cupertino_ui
  imports, dart format, the 60% coverage gate, the public exports
  (with CupertinoSettingsSwitch), design specs per style, the test
  layout and the on-device integration test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DevicePlatform.linux now renders like GNOME Settings 51 (libadwaita 1.10)
instead of the Android style. Fuchsia stays on the Android style.

- Boxed lists: one card per section with 12px corners and the libadwaita
  3-layer soft shadow (painted outside the card only, like CSS, so the
  translucent dark card does not show it), 54px rows, full-width 1px
  separators, text 14px in, 16px symbolic leading icons 12px before the
  title.
- Page: AdwClamp-style content width (600sp maximum, easing in from the
  400sp tightening threshold), 24px above the first group and between
  groups, 12px side margins.
- Group titles in bold 14.67px, 34px tall, 6px above the card; 14.67px row
  titles and 12.22px subtitles at 55% opacity. titleDescription and
  description are both row subtitles.
- Navigation rows end with a painted go-next-symbolic arrow (mirrored in
  RTL); values are dimmed labels before it.
- Hover (3%) and pressed (8%) highlights with the 200ms libadwaita
  transition, a 2px accent keyboard focus ring that follows the card
  corners, Enter/Space activation, and merged row semantics.
- Switch rows toggle from anywhere in the row, like AdwSwitchRow, and never
  call onPressed.
- New public AdwaitaSettingsSwitch: the 46x26 GtkSwitch with a round 20px
  knob, #3584E4 accent, hover/pressed track colors, the dark-mode grey
  knob, drag, keyboard, focus ring, reduce motion and RTL.
- Colors follow libadwaita: #FAFAFB/#FFFFFF light, #222226/white 8% dark,
  all text and icon colors derived from the GNOME foreground.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New gallery screen: the Power panel of GNOME Settings 51 in the Linux
  style (power mode radio rows, switch, combo and navigation rows, flat
  header bar).
- The web build reads `?platform=`, `?screen=gnome-power|cross-platform`
  and `?theme=light|dark` from the URL, so screenshots can open a style
  and screen directly.
- The integration test accepts the Linux switch (AdwaitaSettingsSwitch)
  and covers the new screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DevicePlatform.macOS now renders like the grouped form of macOS 26/27
System Settings instead of the iOS style: #F7F7F7/#252525 cards with 12pt
continuous corners on a white/#1E1E1E page, 36pt rows with 1pt separators
inset 10pt, 13pt text, 13pt semibold headers, 11pt footers, a drawn
chevron.right and the new public MacosSettingsSwitch (36x16, or 44x20 with
MacosSettingsSwitchSize.large). The content column is at most 640pt wide.

Windows keeps the iOS style.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README, llms.txt, CHANGELOG and AGENTS.md: Linux now maps to the GNOME
style, where value, description and titleDescription show in it, which
theme fields it uses, and the new public AdwaitaSettingsSwitch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A Notifications pane replica in the macOS style, with MacosIconBadge
  (colored squircle icons) and MacosPopupValue (pop-up button value) as
  example widgets, reachable from the gallery.
- ?screen=macos&theme=dark (web) or --dart-define=SCREEN/THEME opens a
  screen directly in light or dark mode, for screenshots.
- The example now has a macOS runner (flutter create --platforms=macos).
- The integration test knows MacosSettingsSwitch and scrolls in short
  desktop windows; it passes on macOS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Windows used the iOS style. It now gets its own style, modelled on
Windows 11 Settings (WinUI 3 theme resources and the Community Toolkit
SettingsCard):

- One card per tile: 4px corners, a 1px border, 4px apart, at least 68px
  tall (52 compact) with 16px padding; 20px icons, Body 14/20 titles,
  Caption 12/16 descriptions, the value in secondary text and a thin
  13px chevron at the end.
- Section headers in BodyStrong with the 1,30,0,6 margin; a 1000px
  column with 36px margins (16 below 641px) and 36 at the bottom.
- Below 476px the content moves under the header, below 286px the icon
  hides, as the Toolkit does.
- Clickable cards get the WinUI hover and pressed colors, the elevation
  border and the 2px + 1px keyboard focus ring; Enter and Space activate.
- FluentSettingsSwitch, a public WinUI ToggleSwitch drawn in Flutter:
  40x20, knob 12/14/17x14, accent #005FB8 / #60CDFF, drag, keyboard,
  focus ring, semantics, RTL and reduce motion.

macOS stays on the iOS style.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- "Windows Display Settings" in the gallery: System > Display in the
  Windows style, with the page breadcrumb, Night light, Scale, resolution
  and orientation rows, and an integration test for it.
- On the web, URL options open a screen directly for screenshots:
  ?screen=windows-display|cross-platform, ?platform=<DevicePlatform>
  and ?theme=light|dark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README, llms.txt and AGENTS.md: Windows now has its own style (platform
table, one line on the look, colors, where value and description show,
switch-tile taps, theme fields) and FluentSettingsSwitch is exported.
CHANGELOG: one entry for the new style.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README, llms.txt and AGENTS.md: macOS now has its own style, Windows
keeps the iOS one. Adds the MacosSettingsSwitch API and a CHANGELOG entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pressing the switch (or a trailing widget) of a row that also has
onPressed showed the row's pressed tint until release. Also document that
a disabled macOS switch keeps a paler accent unless inactiveSwitchColor is
set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved:
- theme_provider.dart: keep both _adwaitaTheme and _fluentTheme.
- settings_list.dart (calculateDefaultPadding): keep the Linux AdwClamp
  column and the Windows 1000 column with 36/16 margins and 32 bottom
  padding; both branches had split top/bottom padding, unified on the
  contentWidth = min(width, max) form.
- Tests: Linux and Windows each get their own dispatch expectations,
  with cross-checks that the other style is absent; widget_test.dart
  wires adwaitaStyleTests, fluentStyleTests and fluentSwitchTests.
- Example: gallery lists both replicas; one LaunchOptions
  (?platform=, ?screen=, ?theme=) and a launchScreens map in main.dart
  that can open every gallery screen; integration test covers both
  replicas and knows all four switch types.
- CHANGELOG, README, llms.txt, AGENTS.md: merged into one text with
  iOS/macOS -> iOS, Windows -> Windows 11, Linux -> GNOME,
  Android/Fuchsia -> Android, web -> Chrome.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved:
- Dispatchers (settings_section, settings_tile, theme_provider): macOS,
  Linux, iOS and Windows each get their own case; iOS no longer shares
  a case with macOS or Windows. Exports list all four switches.
- settings_list.dart (calculateDefaultPadding): macOS 640 column with
  12/0 top and 10 bottom padding added next to the Linux AdwClamp and
  Windows 1000/36 rules, on the shared contentWidth form.
- Tests: re-applied the macOS expectations on top of the Linux and
  Windows ones, with cross-checks; widget_test.dart wires
  macosStyleTests too.
- Example: `?screen=macos` joins the launchScreens map; LaunchOptions
  now also reads --dart-define SCREEN/PLATFORM/THEME, so one mechanism
  works on the web and on desktop. example/README documents it.
  Integration test helpers know MacosSettingsSwitch.
- CHANGELOG, README, llms.txt, AGENTS.md: one text with iOS -> iOS,
  macOS -> macOS System Settings, Windows -> Windows 11, Linux -> GNOME,
  Android/Fuchsia -> Android, web -> Chrome.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dart 3.13's formatter (Flutter 3.47, what CI's stable channel uses)
keeps a closure or list argument as a block when named arguments follow
it; Dart 3.12's (Flutter 3.44) splits every argument instead, so the
same code passed one check and failed the other. Put `variant:` before
the test body and move the tile list into a local, so both formatters
leave the files alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An iOS tile's description footer was as wide as the screen, so in a
list narrower than the screen (a pane of a wide window) it overflowed
the card column. It now takes the tile's own width and falls back to the
screen width only when the width is unbounded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the platform, brightness and theme resolution out of SettingsList
into SettingsStyleConfig (internal), and publish the resolved config in a
SettingsStyleScope. A page opened from a tile can then let the
SettingsList in its body take the inputs it leaves unset from the list
that opened it. A list's own scope doesn't inherit, so nested lists keep
detecting their style. calculateBrightness keeps its behavior.

Add SettingsContentColumnHint (internal) so a pane can tell the list that
fills it to use the whole width or to keep space free at the end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SettingsThemeData gets selectedTileColor, selectedTileTextColor,
selectedTileIconColor and listPaneBackground, with defaults per style:
iPad sidebar colors (#E2E6F0/#181D20, #0080F5/#13A4FF capsule with white
text), Android surfaceDim list pane with the selected card in
surfaceContainer, and Chrome's tinted primary menu pill. They are merged
and copied like the other fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SettingsTile.navigation(destination:) opens a SettingsDestination
without a Navigator.push of your own. In a SettingsList it pushes a
Cupertino or Material route with the style's page header: the iOS 26
inline title and glass back button, Android's collapsing large title, or
Chrome's page title. The list in the page takes the opener's platform,
brightness and themes.

SettingsSplitView shows the list and the selected page side by side, and
a list with pushed pages when there is no room:

- Two panes on iPads from 600pt wide, Android at 720dp wide with a 600dp
  smallest width, desktops by width, and the web above 980px. A
  breakpoint, a list pane width and a forced layout can override this.
- List pane per style: iPad 320pt sidebar with capsule selection, the
  Android phone cards on surfaceDim (36.36%, no icons under 380dp), and
  Chrome's 266px menu. Detail pages fill the pane on iPad and Android
  and place the 680px column like Chrome on the web.
- One detail Navigator and the list pane live under GlobalKeys, so the
  page, its state and pushed sub-pages survive folding, unfolding and
  rotation. The chosen page stays shown when the view collapses; the
  default one doesn't.
- Back and Android predictive back go through the detail navigator
  first. The shown page is restored, the panes sit on each side of a
  vertical hinge, and the layout mirrors in RTL.
- SettingsSplitController selects pages, clears the selection and
  reports whether two panes show; onDestinationChanged reports changes.

Tests cover breakpoints for real devices, pane geometry and colors per
style, selection, layout changes, back handling and its races,
the controller, hinges, RTL, restoration and pages pushed from a plain
SettingsList.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A "Split view" entry in the gallery (new "New in v4" section) shows
SettingsSplitView with an iPad Settings, Android Settings or Chrome
settings tree for the resolved style, and a Style tile that opens the
platform picker to force another one. The app also takes
--route '/split-view?style=android&page=display&theme=dark' to open the
demo directly (the web uses #/split-view?...). The integration test
opens the demo, selects a page through the controller and goes back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README: a "Pages and split view" section with the per-platform layout
table and a controller example, API tables for SettingsDestination and
SettingsSplitView, the destination parameter and the new theme fields.
llms.txt gets the same API and do/don't rules. AGENTS.md lists the new
exports and describes how lib/src/split works.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved:
- lib/settings_ui.dart: export the four switches and the split view API.
- settings_list.dart: the branch's SettingsStyleConfig resolution and
  content column hint on top of the per-style calculateDefaultPadding
  (macOS, Windows, GNOME rules and _startsWithHeader kept).
- settings_tile.dart: destinations and the list-pane selection work in
  the dispatcher that has a case for every style.
- Example: one launch mechanism. LaunchOptions reads screen, platform,
  page and theme from the web URL's query, the initial route (--route or
  #/..., whose path names the screen, so the branch's
  '/split-view?...' routes keep working with `platform` instead of
  `style`) or --dart-define. `split-view` joins launchScreens (now
  builders), and SplitViewScreen.fromQuery is gone.
- CHANGELOG, README, llms.txt, AGENTS.md: split view docs merged with
  the macOS, Windows and GNOME styles; the next commit makes the split
  view work with those styles as documented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yadaniyil and others added 27 commits September 25, 2026 05:57
example/linux/ only had the generated plugin files, so `flutter run -d
linux` couldn't work although the GNOME style is new in this release,
and linux/flutter/ephemeral/ showed up in the package. Generated with
`flutter create --platforms=linux --org com.example .` (application id
com.example.example, like the other runners); the template's
test/widget_test.dart is left out. Note the example changes in the
CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Up and Down only move between the sidebar's rows and stop at the first
  and last ones (NSTableView, NavigationView and GtkListBox do); Down on
  the last row no longer jumps into the page (and, on macOS, no longer
  selects a control there).
- A click on a sidebar row gives it the keyboard focus, so Tab and the
  arrow keys go on from the clicked row instead of the first one. Its
  focus ring stays hidden until a key is pressed.
- A switch tile without onPressed in the macOS sidebar or the expanded
  Windows pane can be reached with Tab and toggled with Space: its switch
  takes the focus when the row can't.
- The macOS sidebar list reaches 3pt up under the transparent toolbar
  strip, so the first row's focus ring isn't clipped; the rows stay
  where they were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Switching between one and two panes rebuilds the navigators around the
panes, and the new route or detail navigator took the focus: the next
Tab started over from the first row. After the switch the focus goes
back to the row it was on, to the card of the same tile when the new
layout draws the row as a card (macOS, Windows), or to the tile of a
page that one pane no longer shows.

In one pane, Tab from a page opened over the list no longer jumps to the
rows of the hidden list; it stays in the page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`flutter test --coverage`, as CI runs it, leaves coverage/ untracked.
.pubignore already keeps it out of the package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GNOME sidebar rows and Windows pane items showed their pressed fill from
pointer down to pointer up. When the sidebar won the gesture before the
tap's deadline no tap cancel came, so the row stayed pressed for the
whole scroll. Like the GNOME and Windows cards, a row now lets go when a
finger moves past the touch slop, or when the pointer leaves it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- After a round trip through one pane, the accent pill slid in from the
  item selected before it. A selected item that is built anew now tells
  the pane that the pill is there.
- The page title and its breadcrumb wrapped without a limit, so a long
  or localized title in a narrow window at large text overflowed the
  page. They now stay on one line like WinUI's BreadcrumbBar: the
  crumbs nearest the start collapse into a "…" crumb that goes back to
  the last collapsed page (and reads its title to screen readers), and
  a title too long for the line ends in an ellipsis.
- The compact rail's pane header added a side safe-area inset (a display
  cutout in phone landscape) inside the fixed 48px rail, which pushed
  the menu button out of the rail where it could not be seen or tapped.
  The header now only keeps clear of the top inset, like the items.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew flows

integration_test.dart failed on phones and the 800x600 macOS window
(taps on tiles below the fold) and in every split view style but iPad and
Android (it selected a 'display' page the other trees don't have). Scroll
targets into view first, pick each style's page, check the gallery's new
entries, and don't assume the Material 3 demo starts light.

split_view_flows_test.dart runs the split view demo and the showcase in
all six styles: pages, a nested page, back, switches, 2x text in dark
mode, and resizing across the breakpoints with a nested page open (24
tests, about 4 minutes on macOS). A missed tap fails the test instead of
letting back quit the app.

AGENTS.md and the example README say how to run them: one file at a
time on macOS, and not on web devices.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GNOME sidebar width (180–280sp) and the collapse width (550sp)
scaled those sizes with TextScaler.scale directly. Android 14 and later
scale text non-linearly, so large sizes barely grew: at font scale 1.3
the sidebar stayed 25% of the window and its labels were cut off. The
sp sizes now grow as much as GNOME's body text does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ssed

The ring of a row focused by a click came back when the focus returned
to it without the keyboard (after going back from the page it opened in
one pane). It now stays hidden until any key is pressed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Screen readers read the switch of a macOS sidebar row with onPressed, and
of every expanded Windows pane item, as just "switch, on": the switch was
a node of its own with no label. Like the list tiles:

- A row with onPressed keeps its switch as a separate node, labelled
  with the tile title (labelTileSwitch).
- A Windows item without onPressed is one merged node, "Title, switch,
  on", like the macOS and GNOME sidebar rows already were.
- A switch item in the Windows compact rail, which toggles on a tap and
  shows no switch, now says it is a switch and whether it is on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GNOME content column scaled its 600sp maximum and 400sp threshold
with TextScaler.scale directly. Android 14 and later scale text
non-linearly, so those large sizes barely grew: at font scale 1.3 the
column stayed about 600 wide while the text grew 30%. Like the GNOME
split view, the sp sizes now grow as much as GNOME's body text does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The view closes the page of a SettingsSection tile that goes away, but
it only learns about a tile in a CustomSettingsSection once that tile
has built, so it can't tell such a tile was removed from one that
scrolled out of a lazily built list. Document that, and that
SettingsSplitController.clearSelection() closes the page, with a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Sidebar keyboard: Up and Down stop at the first and last rows, a
  click focuses the row and its ring shows only after a key press.
- Back: the split view's PopScope, the page's own PopScope for the back
  swipe, and routes pushed from the list pane in one pane (which keep
  one pane while open).
- GNOME sp sizes follow the body text size; the Windows breadcrumb stays
  on one line and collapses its first crumbs into "…".
- Switch tiles read as one node, "Title, switch, on"; iOS rows expose
  a tap only when they do something; a forced brightness works in all
  six styles; removed pages, and the CustomSettingsSection exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes to 3.x behavior go under Bug fixes (forced brightness in every
style, disabled tiles and the keyboard, switch and row semantics, the
iOS tile timer in widget tests). Fixes to the new split view and
desktop styles are folded into their feature descriptions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The demo's Style tile pushes its own route and re-creates the split view
with the same controller. In every style, in one pane and with two, back
now closes only the picker, and picking a style doesn't trip the
controller's one-view assert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…up to date

- Desktop sidebars: Up and Down stop at the first and last rows, a click
  focuses the row (its ring shows after a key press), Tab reaches switch
  rows in the macOS sidebar and the expanded Windows pane, and screen
  readers hear the selected row.
- Back also covers the iOS back swipe, which a PopScope can veto; in one
  pane a route a list tile pushed itself closes first, and the view stays
  in one pane until it has closed.
- A shown page closes when its tile goes away; tiles in a
  CustomSettingsSection need controller.clearSelection().
- The Windows breadcrumb stays on one line ("…" and an ellipsis).
- GNOME sizes are sp values that grow like body text, also with Android
  14+ non-linear font scaling.
- A forced brightness works in every style; switch tiles read as
  "Title, switch, on"; disabled tiles ignore the keyboard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tiles that were not semantics containers merged into their section's
node whenever their semantics didn't clash. On Android a section whose
only tappable row sat among other rows was one node, so TalkBack read
"Plain, Value, Disabled, Active" as one button; with no tappable row the
whole section, title included, was one item. The web split view's menu
did the same, and iOS and web cards only escaped it through their inner
ListView.

The Android, iOS and web tiles and the web menu item are now semantics
containers: a button when they have onPressed (dimmed when disabled), a
merged "Title, switch, on" node for switch tiles, plain text otherwise.
They carry their own selected flag in a split view's list pane, which
the dispatcher passes as semanticsSelected instead of wrapping them.

Section titles are header nodes in every style (Windows' header was not
a container, so its section node was the heading). Custom tiles get a
node of their own (tileSemanticsNode), and iOS and macOS footers too:
they were read before the section title, or before a switch row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README's short agent prompt points here, so the full steps live in
one place: inspect the app first, propose sections and wait for the
user's OK, build with settings_ui (split view on tablets, desktop and
the web), wire rows to persisted state, add the store-required rows
without inventing URLs, test every shipped style, run and screenshot,
then summarize.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README opens with two images (phones and a tablet split view; the
macOS, Windows, GNOME and web styles) and a short prompt that points
coding agents at the recipe in llms.txt. The other task prompts move to
doc/agent-prompts.md. pub.dev screenshots now show the 4.0 look, and the
old v2/v3 README images stay in the repo but leave the package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yadaniyil
yadaniyil merged commit 4d23233 into master Sep 25, 2026
2 checks passed
@yadaniyil
yadaniyil deleted the release/4.0.0 branch September 25, 2026 14:11
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