Skip to content

Custom UIBuilder does not preserve widget state #2545

Open
@fa0311

Description

@fa0311

Have you checked for an existing issue?

Flutter Quill Version

11.2.0

Steps to Reproduce

     QuillEditorConfig(
        customStyles: DefaultStyles(
          lists: DefaultListBlockStyle(
            const TextStyle(color: Colors.red),
            HorizontalSpacing.zero,
            const VerticalSpacing(4, 4),
            const VerticalSpacing(4, 4),
            null,
            _CustomCheckBox(), // <-- custom builder
          ),
        )
class _CustomCheckBox extends QuillCheckboxBuilder {
  @override
  Widget build({
    required BuildContext context,
    required bool isChecked,
    required ValueChanged<bool> onChanged,
  }) {
    return GestureDetector(
      onTap: () => onChanged(!isChecked),
      child: const Padding(
        padding: EdgeInsets.symmetric(horizontal: 4),
        child: CountStateWidget(),
      ),
    );
  }
}

int count = 0;

class CountStateWidget extends StatefulWidget {
  const CountStateWidget({super.key});

  @override
  State<CountStateWidget> createState() => _CountStateWidgetState();
}

class _CountStateWidgetState extends State<CountStateWidget> {
  late final int i;

  @override
  void initState() {
    super.initState();
    count++;
    i = count;
  }

  @override
  Widget build(BuildContext context) => Text(i.toString());
}

Observe the numbers (0 1 2 …) next to each line: they do not stay fixed; the same line may rebuild with a new value.

Expected results

CountStateWidget should have its initState called exactly once for each checklist line, retaining its internal data as long as that line exists. Tapping the checkbox should not create a brand‑new CountStateWidget.

Actual results

initState is called multiple times for the same logical item, so the counter keeps changing unexpectedly.

Additional Context

Add a stable Key when bulletPointLeading returns QuillBulletPoint:

Widget bulletPointLeading(LeadingConfig config) => QuillBulletPoint(
  key: ...,
  style:   config.style!,
  width:   config.width!,
  padding: config.padding!,
);

With the key in place, Flutter can match the widget across rebuilds and your nested StatefulWidget keeps its state.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood first issueGood for newcomersminorMinimal impact or cosmetic issue. Can be resolved at a later time without affecting overall function

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions