Unverified Commit 9fdb1671 authored by Matheus Kirchesch's avatar Matheus Kirchesch Committed by GitHub

Added option to disable [NavigationDestination]s ([NavigationBar] destination widget) (#132361)

This PR adds a new option in the NavigationDestination api (the destination widget for the NavigationBar) allowing it to be disabled.

As the issue states this PR is the NavigationBar's version of these two PRs (https://github.com/flutter/flutter/pull/132349 and https://github.com/flutter/flutter/pull/127113)

* https://github.com/flutter/flutter/issues/132359
parent f76c150c
......@@ -295,6 +295,7 @@ class NavigationDestination extends StatelessWidget {
this.selectedIcon,
required this.label,
this.tooltip,
this.enabled = true,
});
/// The [Widget] (usually an [Icon]) that's displayed for this
......@@ -333,11 +334,17 @@ class NavigationDestination extends StatelessWidget {
/// Defaults to null, in which case the [label] text will be used.
final String? tooltip;
/// Indicates that this destination is selectable.
///
/// Defaults to true.
final bool enabled;
@override
Widget build(BuildContext context) {
final _NavigationDestinationInfo info = _NavigationDestinationInfo.of(context);
const Set<MaterialState> selectedState = <MaterialState>{MaterialState.selected};
const Set<MaterialState> unselectedState = <MaterialState>{};
const Set<MaterialState> disabledState = <MaterialState>{MaterialState.disabled};
final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context);
final NavigationBarThemeData defaults = _defaultsFor(context);
......@@ -346,15 +353,24 @@ class NavigationDestination extends StatelessWidget {
return _NavigationDestinationBuilder(
label: label,
tooltip: tooltip,
enabled: enabled,
buildIcon: (BuildContext context) {
final IconThemeData selectedIconTheme =
navigationBarTheme.iconTheme?.resolve(selectedState)
?? defaults.iconTheme!.resolve(selectedState)!;
final IconThemeData unselectedIconTheme =
navigationBarTheme.iconTheme?.resolve(unselectedState)
?? defaults.iconTheme!.resolve(unselectedState)!;
final IconThemeData disabledIconTheme =
navigationBarTheme.iconTheme?.resolve(disabledState)
?? defaults.iconTheme!.resolve(disabledState)!;
final Widget selectedIconWidget = IconTheme.merge(
data: navigationBarTheme.iconTheme?.resolve(selectedState)
?? defaults.iconTheme!.resolve(selectedState)!,
data: enabled ? selectedIconTheme : disabledIconTheme,
child: selectedIcon ?? icon,
);
final Widget unselectedIconWidget = IconTheme.merge(
data: navigationBarTheme.iconTheme?.resolve(unselectedState)
?? defaults.iconTheme!.resolve(unselectedState)!,
data: enabled ? unselectedIconTheme : disabledIconTheme,
child: icon,
);
......@@ -382,7 +398,15 @@ class NavigationDestination extends StatelessWidget {
?? defaults.labelTextStyle!.resolve(selectedState);
final TextStyle? effectiveUnselectedLabelTextStyle = navigationBarTheme.labelTextStyle?.resolve(unselectedState)
?? defaults.labelTextStyle!.resolve(unselectedState);
final TextStyle? textStyle = _isForwardOrCompleted(animation) ? effectiveSelectedLabelTextStyle : effectiveUnselectedLabelTextStyle;
final TextStyle? effectiveDisabledLabelTextStyle = navigationBarTheme.labelTextStyle?.resolve(disabledState)
?? defaults.labelTextStyle!.resolve(disabledState);
final TextStyle? textStyle = enabled
? _isForwardOrCompleted(animation)
? effectiveSelectedLabelTextStyle
: effectiveUnselectedLabelTextStyle
: effectiveDisabledLabelTextStyle;
return Padding(
padding: const EdgeInsets.only(top: 4),
child: MediaQuery.withClampedTextScaling(
......@@ -416,6 +440,7 @@ class _NavigationDestinationBuilder extends StatefulWidget {
required this.buildLabel,
required this.label,
this.tooltip,
this.enabled = true,
});
/// Builds the icon for a destination in a [NavigationBar].
......@@ -454,6 +479,11 @@ class _NavigationDestinationBuilder extends StatefulWidget {
/// Defaults to null, in which case the [label] text will be used.
final String? tooltip;
/// Indicates that this destination is selectable.
///
/// Defaults to true.
final bool enabled;
@override
State<_NavigationDestinationBuilder> createState() => _NavigationDestinationBuilderState();
}
......@@ -474,7 +504,7 @@ class _NavigationDestinationBuilderState extends State<_NavigationDestinationBui
iconKey: iconKey,
labelBehavior: info.labelBehavior,
customBorder: navigationBarTheme.indicatorShape ?? defaults.indicatorShape,
onTap: info.onTap,
onTap: widget.enabled ? info.onTap : null,
child: Row(
children: <Widget>[
Expanded(
......@@ -1319,9 +1349,11 @@ class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
return IconThemeData(
size: 24.0,
color: states.contains(MaterialState.selected)
? _colors.onSecondaryContainer
: _colors.onSurfaceVariant,
color: states.contains(MaterialState.disabled)
? _colors.onSurfaceVariant.withOpacity(0.38)
: states.contains(MaterialState.selected)
? _colors.onSecondaryContainer
: _colors.onSurfaceVariant,
);
});
}
......@@ -1332,9 +1364,11 @@ class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
@override MaterialStateProperty<TextStyle?>? get labelTextStyle {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
final TextStyle style = _textTheme.labelMedium!;
return style.apply(color: states.contains(MaterialState.selected)
? _colors.onSurface
: _colors.onSurfaceVariant
return style.apply(color: states.contains(MaterialState.disabled)
? _colors.onSurfaceVariant.withOpacity(0.38)
: states.contains(MaterialState.selected)
? _colors.onSurface
: _colors.onSurfaceVariant
);
});
}
......
......@@ -977,6 +977,43 @@ void main() {
expect(_getIndicatorDecoration(tester)?.shape, shape);
});
testWidgetsWithLeakTracking('Destinations respect their disabled state', (WidgetTester tester) async {
int selectedIndex = 0;
await tester.pumpWidget(
_buildWidget(
NavigationBar(
destinations: const <Widget>[
NavigationDestination(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
NavigationDestination(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
NavigationDestination(
icon: Icon(Icons.bookmark),
label: 'Bookmark',
enabled: false,
),
],
onDestinationSelected: (int i) => selectedIndex = i,
selectedIndex: selectedIndex,
),
)
);
await tester.tap(find.text('AC'));
expect(selectedIndex, 0);
await tester.tap(find.text('Alarm'));
expect(selectedIndex, 1);
await tester.tap(find.text('Bookmark'));
expect(selectedIndex, 1);
});
group('Material 2', () {
// These tests are only relevant for Material 2. Once Material 2
// support is deprecated and the APIs are removed, these tests
......
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