Unverified Commit 9bce7901 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Updated the `ToggleButtons` API doc to link to `SegmentedButton` (#127021)

fixes https://github.com/flutter/flutter/issues/124884
parent 0b9cd865
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
/// Flutter code sample for migrating from [ToggleButtons] to [SegmentedButton].
void main() {
runApp(const ToggleButtonsApp());
}
class ToggleButtonsApp extends StatelessWidget {
const ToggleButtonsApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(useMaterial3: true),
home: const Scaffold(
body: ToggleButtonsExample(),
),
);
}
}
enum ShirtSize { extraSmall, small, medium, large, extraLarge }
const List<(ShirtSize, String)> shirtSizeOptions = <(ShirtSize, String)>[
(ShirtSize.extraSmall, 'XS'),
(ShirtSize.small, 'S'),
(ShirtSize.medium, 'M'),
(ShirtSize.large, 'L'),
(ShirtSize.extraLarge, 'XL'),
];
class ToggleButtonsExample extends StatefulWidget {
const ToggleButtonsExample({super.key});
@override
State<ToggleButtonsExample> createState() => _ToggleButtonsExampleState();
}
class _ToggleButtonsExampleState extends State<ToggleButtonsExample> {
final List<bool> _toggleButtonsSelection = ShirtSize.values.map((ShirtSize e) => e == ShirtSize.medium).toList();
Set<ShirtSize> _segmentedButtonSelection = <ShirtSize>{ShirtSize.medium};
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('ToggleButtons'),
const SizedBox(height: 10),
// This ToggleButtons allows multiple or no selection.
ToggleButtons(
// ToggleButtons uses a List<bool> to track its selection state.
isSelected: _toggleButtonsSelection,
// This callback return the index of the child that was pressed.
onPressed: (int index) {
setState(() {
_toggleButtonsSelection[index] = !_toggleButtonsSelection[index];
});
},
// Constraints are used to determine the size of each child widget.
constraints: const BoxConstraints(
minHeight: 32.0,
minWidth: 56.0,
),
// ToggleButtons uses a List<Widget> to build its children.
children: shirtSizeOptions
.map(((ShirtSize, String) shirt) => Text(shirt.$2))
.toList(),
),
const SizedBox(height: 20),
const Text('SegmentedButton'),
const SizedBox(height: 10),
SegmentedButton<ShirtSize>(
// ToggleButtons above allows multiple or no selection.
// Set `multiSelectionEnabled` and `emptySelectionAllowed` to true
// to match the behavior of ToggleButtons.
multiSelectionEnabled: true,
emptySelectionAllowed: true,
// Hide the selected icon to match the behavior of ToggleButtons.
showSelectedIcon: false,
// SegmentedButton uses a Set<T> to track its selection state.
selected: _segmentedButtonSelection,
// This callback updates the set of selected segment values.
onSelectionChanged: (Set<ShirtSize> newSelection) {
setState(() {
_segmentedButtonSelection = newSelection;
});
},
// SegmentedButton uses a List<ButtonSegment<T>> to build its children
// instead of a List<Widget> like ToggleButtons.
segments: shirtSizeOptions
.map<ButtonSegment<ShirtSize>>(((ShirtSize, String) shirt) {
return ButtonSegment<ShirtSize>(value: shirt.$1, label: Text(shirt.$2));
})
.toList(),
),
],
),
);
}
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_api_samples/material/toggle_buttons/toggle_buttons.1.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('ToggleButtons allows multiple or no selection', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true);
Finder findButton(String text) {
return find.descendant(
of: find.byType(ToggleButtons),
matching: find.widgetWithText(TextButton, text),
);
}
await tester.pumpWidget(const example.ToggleButtonsApp());
TextButton toggleMButton = tester.widget<TextButton>(findButton('M'));
TextButton toggleXLButton = tester.widget<TextButton>(findButton('XL'));
// Initially, only M is selected.
expect(
toggleMButton.style!.backgroundColor!.resolve(enabled),
theme.colorScheme.primary.withOpacity(0.12),
);
expect(
toggleXLButton.style!.backgroundColor!.resolve(enabled),
theme.colorScheme.surface.withOpacity(0.0),
);
// Tap on XL.
await tester.tap(findButton('XL'));
await tester.pumpAndSettle();
// Now both M and XL are selected.
toggleMButton = tester.widget<TextButton>(findButton('M'));
toggleXLButton = tester.widget<TextButton>(findButton('XL'));
expect(
toggleMButton.style!.backgroundColor!.resolve(enabled),
theme.colorScheme.primary.withOpacity(0.12),
);
expect(
toggleXLButton.style!.backgroundColor!.resolve(enabled),
theme.colorScheme.primary.withOpacity(0.12),
);
// Tap M to deselect it.
await tester.tap(findButton('M'));
await tester.pumpAndSettle();
// Tap XL to deselect it.
await tester.tap(findButton('XL'));
await tester.pumpAndSettle();
// Now neither M nor XL are selected.
toggleMButton = tester.widget<TextButton>(findButton('M'));
toggleXLButton = tester.widget<TextButton>(findButton('XL'));
expect(
toggleMButton.style!.backgroundColor!.resolve(enabled),
theme.colorScheme.surface.withOpacity(0.0),
);
expect(
toggleXLButton.style!.backgroundColor!.resolve(enabled),
theme.colorScheme.surface.withOpacity(0.0),
);
});
testWidgets('SegmentedButton allows multiple or no selection', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true);
Finder findButton(String text) {
return find.descendant(
of: find.byType(SegmentedButton<example.ShirtSize>),
matching: find.widgetWithText(TextButton, text),
);
}
await tester.pumpWidget(const example.ToggleButtonsApp());
Material segmentMButton = tester.widget<Material>(find.descendant(
of: findButton('M'),
matching: find.byType(Material),
));
Material segmentXLButton = tester.widget<Material>(find.descendant(
of: findButton('XL'),
matching: find.byType(Material),
));
// Initially, only M is selected.
expect(segmentMButton.color, theme.colorScheme.secondaryContainer);
expect(segmentXLButton.color, Colors.transparent);
// Tap on XL.
await tester.tap(findButton('XL'));
await tester.pumpAndSettle();
// // Now both M and XL are selected.
segmentMButton = tester.widget<Material>(find.descendant(
of: findButton('M'),
matching: find.byType(Material),
));
segmentXLButton = tester.widget<Material>(find.descendant(
of: findButton('XL'),
matching: find.byType(Material),
));
expect(segmentMButton.color, theme.colorScheme.secondaryContainer);
expect(segmentXLButton.color, theme.colorScheme.secondaryContainer);
// Tap M to deselect it.
await tester.tap(findButton('M'));
await tester.pumpAndSettle();
// Tap XL to deselect it.
await tester.tap(findButton('XL'));
await tester.pumpAndSettle();
// Now neither M nor XL are selected.
segmentMButton = tester.widget<Material>(find.descendant(
of: findButton('M'),
matching: find.byType(Material),
));
segmentXLButton = tester.widget<Material>(find.descendant(
of: findButton('XL'),
matching: find.byType(Material),
));
expect(segmentMButton.color, Colors.transparent);
expect(segmentXLButton.color, Colors.transparent);
});
}
Set<MaterialState> enabled = <MaterialState>{ };
......@@ -30,8 +30,35 @@ import 'toggle_buttons_theme.dart';
/// correlated by their index in the list. The length of [isSelected] has to
/// match the length of the [children] list.
///
/// There is a Material 3 version of this component, [SegmentedButton],
/// that's preferred for applications that are configured for Material 3
/// (see [ThemeData.useMaterial3]).
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=kVEguaQWGAY}
///
/// ## Updating to [SegmentedButton]
///
/// There is a Material 3 version of this component, [SegmentedButton],
/// that's preferred for applications that are configured for Material 3
/// (see [ThemeData.useMaterial3]). The [SegmentedButton] widget's visuals
/// are a little bit different, see the Material 3 spec at
/// <https://m3.material.io/components/segmented-buttons/overview> for
/// more details. The [SegmentedButton] widget's API is also slightly different.
/// While the [ToggleButtons] widget can have list of widgets, the
/// [SegmentedButton] widget has a list of [ButtonSegment]s with
/// a type value. While the [ToggleButtons] uses a list of boolean values
/// to determine the selection state of each button, the [SegmentedButton]
/// uses a set of type values to determine the selection state of each segment.
/// The [SegmentedButton.style] is a [ButtonStyle] style field, which can be
/// used to customize the entire segmented button and the individual segments.
///
/// {@tool dartpad}
/// This sample shows how to migrate [ToggleButtons] that allows multiple
/// or no selection to [SegmentedButton] that allows multiple or no selection.
///
/// ** See code in examples/api/lib/material/toggle_buttons/toggle_buttons.1.dart **
/// {@end-tool}
///
/// {@tool dartpad}
/// This example showcase [ToggleButtons] in various configurations.
///
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment