Skip to content

[BUG]: genui's CheckBox ignores a literal value and renders unchecked #2736

Closed
flutter/genui
#1038

Description

@diegolopezrm
  • I have searched the existing issues to make sure this bug has not already been reported.

Describe the Bug

CheckBox.value is a booleanReference, so a literal, a {"path": ...} binding and a {"call": ...} are all valid. A literal is dropped: the component renders unchecked and announces itself unchecked, with nothing logged. The agent says the setting is on, the user sees it off, and neither side is told.

check_box.dart binds the widget to a data model path and never consults what the agent sent:

final path = (valueRef is Map && valueRef.containsKey('path'))
    ? valueRef['path'] as String
    : '${itemContext.id}.value';
...
BoundBool(
  dataContext: itemContext.dataContext,
  value: {'path': path},            // the literal is never read
  builder: (context, value) => CheckboxListTile(value: value ?? false, ...),
)

slider.dart has exactly the fallback this is missing:

var effectiveValue = value;
if (effectiveValue == null) {
  if (valueRef is num) {
    effectiveValue = valueRef;
  }
}

Steps to Reproduce

import 'package:a2ui_core/a2ui_core.dart' as core;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:genui/genui.dart';

void main() {
  testWidgets('CheckBox reflects a literal value', (tester) async {
    final catalog = BasicCatalogItems.asCatalog().copyWith(catalogId: 'repro');
    final controller = SurfaceController(catalogs: [catalog]);
    controller.handleMessage(core.UpdateComponentsMessage(surfaceId: 's', components: [
      {'id': 'root', 'component': 'CheckBox', 'label': 'Wi-Fi', 'value': true},
    ]));
    controller.handleMessage(
        core.CreateSurfaceMessage(surfaceId: 's', catalogId: 'repro'));

    await tester.pumpWidget(MaterialApp(
        home: Scaffold(body: Surface(surfaceContext: controller.contextFor('s')))));
    await tester.pumpAndSettle();

    expect(tester.widget<Checkbox>(find.byType(Checkbox)).value, isTrue);
  });
}
Expected: true
  Actual: <false>

The same surface with "value": {"path": "/on"} and an updateDataModel of {"on": true} renders checked, so only the literal is affected. In the semantics tree the node reads checked=false, so a screen reader is told the same wrong thing the screen shows.

Expected Behavior

A literal value is what the checkbox shows until something writes to the path, the way Slider and TextField already behave.

Environment Details

  • OS: macOS 26.5.2
  • Browser/Platform: Flutter 3.44.6 stable, Dart 3.10
  • SDK/Package Name & Version: genui 0.10.3 (main, d88aa76), a2ui_core 0.1.1
  • Protocol Version: v0.9.1

Additional Context

Found while prototyping a rendering conformance suite that states what a surface must expose as semantics — role, name, value, state, actions — so the same cases can be run against Flutter, SwiftUI, Compose and the web. The case declared checked: true and this renderer reported checked=false.

Happy to send the PR.

Activity

  1. added
    status: needs-triageauto-managed: https://github.com/a2ui-project/a2ui/blob/main/scripts/triage.mjs
    on Sep 22, 2026
  2. diegolopezrm commented on Sep 22, 2026

    @diegolopezrm
    ContributorAuthor

    The prototype this came out of is written up in #2738, in case the shape of the suite is worth discussing separately from the fix.

  3. josemontespg commented on Sep 22, 2026

    @josemontespg
    Collaborator

    A2UI Triage: Confirmed on genui main (d88aa76): check_box.dart always binds BoundBool to {path} and never reads a literal, while slider.dart and text_field.dart fall back to the literal when the path is empty. The spec's DynamicBoolean allows a literal and genui's own booleanReference doc says so, so this is a bug.

    Marked P2 and size: small. A PR is welcome: mirror the Slider fallback (use the literal until the path has a value) and add a widget test like the one in your report. Same area as #2737, so both could land together. cc @gspencergoog to decide if we'd like to accept a PR from the community or assign the issue to the a2ui team.

  4. removed
    status: needs-triageauto-managed: https://github.com/a2ui-project/a2ui/blob/main/scripts/triage.mjs
    on Sep 22, 2026
  5. moved this from Todo to In Progress in A2UIon Sep 22, 2026
  6. diegolopezrm commented on Sep 22, 2026

    @diegolopezrm
    ContributorAuthor

    PR is up: flutter/genui#1038. It mirrors the Slider fallback as suggested, with a test for the literal and one for a binding still taking over from it.

    @gspencergoog, same question as on #2737 — happy for you to take it instead if the team would rather own the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions