Bruno Leroux 9d4db1d5dd
Fix SegmentedButton focus issue (#173953)
## Description

This PR fixes SegmentedButton focus traversal.

# Before

When a focused segment (with no icon) is selected or unselected, the
focus moves to another segment.

[Capture vidéo du 2025-08-11
15-28-03.webm](https://github.com/user-attachments/assets/11c29194-6b96-4ea4-9245-dcbd36dc424f)

In this recording, tab key is used to set the focus on 'Month' segment,
then enter is used to select the segment. The focus move unexpectedly to
'Week' segment.

# After

When a focused segment (with no icon) is selected or unselected, the
focus stays on this same segment and the InkWell animations are
correctly painted.

[Capture vidéo du 2025-08-11
15-27-30.webm](https://github.com/user-attachments/assets/e09056d0-b572-462b-8ad8-c23eddcc4bfd)


<details><summary>Code sample for recordings</summary>

```dart
import 'package:flutter/material.dart';

/// Flutter code sample for [SegmentedButton].

void main() {
  runApp(const SegmentedButtonApp());
}

class SegmentedButtonApp extends StatelessWidget {
  const SegmentedButtonApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(body: Center(child: SingleChoice())),
    );
  }
}

enum Calendar { day, week, month, year }

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

  @override
  State<SingleChoice> createState() => _SingleChoiceState();
}

class _SingleChoiceState extends State<SingleChoice> {
  Calendar calendarView = Calendar.day;

  @override
  Widget build(BuildContext context) {
    return SegmentedButton<Calendar>(
      segments: const <ButtonSegment<Calendar>>[
        ButtonSegment<Calendar>(value: Calendar.day, label: Text('Day')),
        ButtonSegment<Calendar>(value: Calendar.week, label: Text('Week')),
        ButtonSegment<Calendar>(value: Calendar.month, label: Text('Month')),
        ButtonSegment<Calendar>(value: Calendar.year, label: Text('Year')),
      ],
      selected: <Calendar>{calendarView},
      onSelectionChanged: (Set<Calendar> newSelection) {
        setState(() {
          // By default there is only a single segment that can be
          // selected at one time, so its value is always the first
          // item in the selected set.
          calendarView = newSelection.first;
        });
      },
    );
  }
}

``` 
</details> 

## Related Issue

Fixes [SegmentedButton keyboard navigation incorrectly moves focus on
click](https://github.com/flutter/flutter/issues/161922)


## Implementation choice

The root of this issue is TextButton.icon which creates a different
widget tree depending on the icon.
See https://github.com/flutter/flutter/issues/173944 for more context.

The proposed solution is to maintain the same widget tree when possible.
To do TextButton.icon is not used. This PR uses on TextButton and build
a child containing both the icon (when present) and the label.
In order to keep the same rendering as before, some internal logic from
TextButton.icon is duplicated.

## Tests

Adds 1 test.
Updates 1 test.
2025-08-20 06:10:07 +00:00
..
2025-07-22 17:51:20 +00:00

Flutter

Flutter is a new way to build high-performance, cross-platform mobile, web, and desktop apps. Flutter is optimized for today's — and tomorrow's — mobile and desktop devices. We are focused on low-latency input and high frame rates on all platforms.

See the getting started guide for information about using Flutter.