@@ -96,7 +96,7 @@ class ButtonSegment<T> {
96
96
/// [ToggleButtons].
97
97
/// * [Radio] , an alternative way to present the user with a mutually exclusive set of options.
98
98
/// * [FilterChip] , [ChoiceChip] , which can be used when you need to show more than five options.
99
- class SegmentedButton <T > extends StatelessWidget {
99
+ class SegmentedButton <T > extends StatefulWidget {
100
100
/// Creates a const [SegmentedButton] .
101
101
///
102
102
/// [segments] must contain at least one segment, but it is recommended
@@ -235,27 +235,33 @@ class SegmentedButton<T> extends StatelessWidget {
235
235
/// Defaults to an [Icon] with [Icons.check] .
236
236
final Widget ? selectedIcon;
237
237
238
- bool get _enabled => onSelectionChanged != null ;
238
+ @override
239
+ State <SegmentedButton <T >> createState () => _SegmentedButtonState <T >();
240
+ }
241
+
242
+ class _SegmentedButtonState <T > extends State <SegmentedButton <T >> {
243
+ bool get _enabled => widget.onSelectionChanged != null ;
244
+ final Map <ButtonSegment <T >, MaterialStatesController > _statesControllers = < ButtonSegment <T >, MaterialStatesController > {};
239
245
240
246
void _handleOnPressed (T segmentValue) {
241
247
if (! _enabled) {
242
248
return ;
243
249
}
244
- final bool onlySelectedSegment = selected.length == 1 && selected.contains (segmentValue);
245
- final bool validChange = emptySelectionAllowed || ! onlySelectedSegment;
250
+ final bool onlySelectedSegment = widget. selected.length == 1 && widget. selected.contains (segmentValue);
251
+ final bool validChange = widget. emptySelectionAllowed || ! onlySelectedSegment;
246
252
if (validChange) {
247
- final bool toggle = multiSelectionEnabled || (emptySelectionAllowed && onlySelectedSegment);
253
+ final bool toggle = widget. multiSelectionEnabled || (widget. emptySelectionAllowed && onlySelectedSegment);
248
254
final Set <T > pressedSegment = < T > {segmentValue};
249
255
late final Set <T > updatedSelection;
250
256
if (toggle) {
251
- updatedSelection = selected.contains (segmentValue)
252
- ? selected.difference (pressedSegment)
253
- : selected.union (pressedSegment);
257
+ updatedSelection = widget. selected.contains (segmentValue)
258
+ ? widget. selected.difference (pressedSegment)
259
+ : widget. selected.union (pressedSegment);
254
260
} else {
255
261
updatedSelection = pressedSegment;
256
262
}
257
- if (! setEquals (updatedSelection, selected)) {
258
- onSelectionChanged !(updatedSelection);
263
+ if (! setEquals (updatedSelection, widget. selected)) {
264
+ widget. onSelectionChanged !(updatedSelection);
259
265
}
260
266
}
261
267
}
@@ -271,7 +277,7 @@ class SegmentedButton<T> extends StatelessWidget {
271
277
final Set <MaterialState > currentState = _enabled ? enabledState : disabledState;
272
278
273
279
P ? effectiveValue <P >(P ? Function (ButtonStyle ? style) getProperty) {
274
- late final P ? widgetValue = getProperty (style);
280
+ late final P ? widgetValue = getProperty (widget. style);
275
281
late final P ? themeValue = getProperty (theme.style);
276
282
late final P ? defaultValue = getProperty (defaults.style);
277
283
return widgetValue ?? themeValue ?? defaultValue;
@@ -305,25 +311,24 @@ class SegmentedButton<T> extends StatelessWidget {
305
311
);
306
312
}
307
313
308
- final ButtonStyle segmentStyle = segmentStyleFor (style);
314
+ final ButtonStyle segmentStyle = segmentStyleFor (widget. style);
309
315
final ButtonStyle segmentThemeStyle = segmentStyleFor (theme.style).merge (segmentStyleFor (defaults.style));
310
- final Widget ? selectedIcon = showSelectedIcon
311
- ? this .selectedIcon ?? theme.selectedIcon ?? defaults.selectedIcon
316
+ final Widget ? selectedIcon = widget. showSelectedIcon
317
+ ? widget .selectedIcon ?? theme.selectedIcon ?? defaults.selectedIcon
312
318
: null ;
313
319
314
320
Widget buttonFor (ButtonSegment <T > segment) {
315
321
final Widget label = segment.label ?? segment.icon ?? const SizedBox .shrink ();
316
- final bool segmentSelected = selected.contains (segment.value);
317
- final Widget ? icon = (segmentSelected && showSelectedIcon)
322
+ final bool segmentSelected = widget. selected.contains (segment.value);
323
+ final Widget ? icon = (segmentSelected && widget. showSelectedIcon)
318
324
? selectedIcon
319
325
: segment.label != null
320
326
? segment.icon
321
327
: null ;
322
- final MaterialStatesController controller = MaterialStatesController (
323
- < MaterialState > {
328
+ final MaterialStatesController controller = _statesControllers. putIfAbsent (segment, () => MaterialStatesController ());
329
+ controller.value = < MaterialState > {
324
330
if (segmentSelected) MaterialState .selected,
325
- }
326
- );
331
+ };
327
332
328
333
final Widget button = icon != null
329
334
? TextButton .icon (
@@ -350,7 +355,7 @@ class SegmentedButton<T> extends StatelessWidget {
350
355
return MergeSemantics (
351
356
child: Semantics (
352
357
checked: segmentSelected,
353
- inMutuallyExclusiveGroup: multiSelectionEnabled ? null : true ,
358
+ inMutuallyExclusiveGroup: widget. multiSelectionEnabled ? null : true ,
354
359
child: buttonWithTooltip,
355
360
),
356
361
);
@@ -363,7 +368,7 @@ class SegmentedButton<T> extends StatelessWidget {
363
368
final OutlinedBorder enabledBorder = resolvedEnabledBorder.copyWith (side: enabledSide);
364
369
final OutlinedBorder disabledBorder = resolvedDisabledBorder.copyWith (side: disabledSide);
365
370
366
- final List <Widget > buttons = segments.map (buttonFor).toList ();
371
+ final List <Widget > buttons = widget. segments.map (buttonFor).toList ();
367
372
368
373
return Material (
369
374
type: MaterialType .transparency,
@@ -374,7 +379,7 @@ class SegmentedButton<T> extends StatelessWidget {
374
379
child: TextButtonTheme (
375
380
data: TextButtonThemeData (style: segmentThemeStyle),
376
381
child: _SegmentedButtonRenderWidget <T >(
377
- segments: segments,
382
+ segments: widget. segments,
378
383
enabledBorder: _enabled ? enabledBorder : disabledBorder,
379
384
disabledBorder: disabledBorder,
380
385
direction: direction,
@@ -383,6 +388,14 @@ class SegmentedButton<T> extends StatelessWidget {
383
388
),
384
389
);
385
390
}
391
+
392
+ @override
393
+ void dispose () {
394
+ for (final MaterialStatesController controller in _statesControllers.values) {
395
+ controller.dispose ();
396
+ }
397
+ super .dispose ();
398
+ }
386
399
}
387
400
class _SegmentedButtonRenderWidget <T > extends MultiChildRenderObjectWidget {
388
401
const _SegmentedButtonRenderWidget ({
0 commit comments