Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update BottomAppBar to use elevation overlays when in a dark theme #41864

Merged
merged 4 commits into from Oct 14, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/flutter/lib/material.dart
Expand Up @@ -77,7 +77,7 @@ export 'src/material/ink_well.dart';
export 'src/material/input_border.dart';
export 'src/material/input_decorator.dart';
export 'src/material/list_tile.dart';
export 'src/material/material.dart';
export 'src/material/material.dart' hide ElevationOverlay;
darrenaustin marked this conversation as resolved.
Show resolved Hide resolved
export 'src/material/material_button.dart';
export 'src/material/material_localizations.dart';
export 'src/material/material_state.dart';
Expand Down
7 changes: 5 additions & 2 deletions packages/flutter/lib/src/material/bottom_app_bar.dart
Expand Up @@ -127,10 +127,13 @@ class _BottomAppBarState extends State<BottomAppBar> {
notchMargin: widget.notchMargin,
)
: const ShapeBorderClipper(shape: RoundedRectangleBorder());
final double elevation = widget.elevation ?? babTheme.elevation ?? _defaultElevation;
final Color color = widget.color ?? babTheme.color ?? Theme.of(context).bottomAppBarColor;
final Color effectiveColor = ElevationOverlay.applyOverlay(context, color, elevation);
return PhysicalShape(
clipper: clipper,
elevation: widget.elevation ?? babTheme.elevation ?? _defaultElevation,
color: widget.color ?? babTheme.color ?? Theme.of(context).bottomAppBarColor,
elevation: elevation,
color: effectiveColor,
clipBehavior: widget.clipBehavior,
child: Material(
type: MaterialType.transparency,
Expand Down
75 changes: 55 additions & 20 deletions packages/flutter/lib/src/material/material.dart
Expand Up @@ -317,24 +317,6 @@ class Material extends StatefulWidget {
static const double defaultSplashRadius = 35.0;
}

// Apply a semi-transparent colorScheme.onSurface to surface colors to
// indicate the level of elevation.
Color _elevationOverlayColor(BuildContext context, Color background, double elevation) {
final ThemeData theme = Theme.of(context);
if (elevation > 0.0 &&
theme.applyElevationOverlayColor &&
background == theme.colorScheme.surface) {

// Compute the opacity for the given elevation
// This formula matches the values in the spec:
// https://material.io/design/color/dark-theme.html#properties
final double opacity = (4.5 * math.log(elevation + 1) + 2) / 100.0;
final Color overlay = theme.colorScheme.onSurface.withOpacity(opacity);
return Color.alphaBlend(overlay, background);
}
return background;
}

class _MaterialState extends State<Material> with TickerProviderStateMixin {
final GlobalKey _inkFeatureRenderer = GlobalKey(debugLabel: 'ink renderer');

Expand Down Expand Up @@ -405,7 +387,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
clipBehavior: widget.clipBehavior,
borderRadius: BorderRadius.zero,
elevation: widget.elevation,
color: _elevationOverlayColor(context, backgroundColor, widget.elevation),
color: ElevationOverlay.applyOverlay(context, backgroundColor, widget.elevation),
shadowColor: widget.shadowColor,
animateColor: false,
child: contents,
Expand Down Expand Up @@ -773,7 +755,7 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior>
),
clipBehavior: widget.clipBehavior,
elevation: elevation,
color: _elevationOverlayColor(context, widget.color, elevation),
color: ElevationOverlay.applyOverlay(context, widget.color, elevation),
shadowColor: _shadowColor.evaluate(animation),
);
}
Expand Down Expand Up @@ -815,3 +797,56 @@ class _ShapeBorderPainter extends CustomPainter {
return oldDelegate.border != border;
}
}

/// A simple utility class for dealing with the elevation overlay color needed
/// to indicate elevation for dark theme widgets.
///
/// This is an internal implementation class and should not be exported out of
/// the material package.
class ElevationOverlay {

ElevationOverlay._();

/// Applies an elevation overlay to a surface color to indicate the level of
/// elevation in a dark theme.
///
/// If the surrounding [Theme.applyElevationOverlayColor] is true, and
/// [color] is [Theme.colorScheme.surface] then this will return
/// a version of the given color with a semi-transparent [Theme.colorScheme.onSurface]
/// overlaid on top of it. The opacity of the overlay is controlled by the
/// [elevation].
///
/// Otherwise it will just return the [color] unmodified.
///
/// See also:
///
/// * [ThemeData.applyElevationOverlayColor] which controls the whether
/// an overlay color will be applied to indicate elevation.
/// * [overlayColor] which computes the needed overlay color.
static Color applyOverlay(BuildContext context, Color color, double elevation) {
final ThemeData theme = Theme.of(context);
if (elevation > 0.0 &&
theme.applyElevationOverlayColor &&
color == theme.colorScheme.surface) {

return Color.alphaBlend(overlayColor(context, elevation), color);
}
return color;
}

/// Computes the appropriate overlay color used to indicate elevation in
/// dark themes.
///
/// See also:
///
/// * https://material.io/design/color/dark-theme.html#properties which
/// specifies the exact overlay values for a given elevation.
static Color overlayColor(BuildContext context, double elevation) {
final ThemeData theme = Theme.of(context);
// Compute the opacity for the given elevation
// This formula matches the values in the spec:
// https://material.io/design/color/dark-theme.html#properties
final double opacity = (4.5 * math.log(elevation + 1) + 2) / 100.0;
return theme.colorScheme.onSurface.withOpacity(opacity);
}
}
1 change: 1 addition & 0 deletions packages/flutter/lib/src/material/theme_data.dart
Expand Up @@ -603,6 +603,7 @@ class ThemeData extends Diagnosticable {
accentColor: colorScheme.secondary,
accentColorBrightness: ThemeData.estimateBrightnessForColor(colorScheme.secondary),
scaffoldBackgroundColor: colorScheme.background,
bottomAppBarColor: colorScheme.surface,
cardColor: colorScheme.surface,
dividerColor: colorScheme.onSurface.withOpacity(0.12),
backgroundColor: colorScheme.background,
Expand Down
20 changes: 20 additions & 0 deletions packages/flutter/test/material/bottom_app_bar_test.dart
Expand Up @@ -139,6 +139,26 @@ void main() {
expect(physicalShape.color, const Color(0xff0000ff));
});

testWidgets('dark theme applies an elevation overlay color', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.from(colorScheme: const ColorScheme.dark()),
home: const Scaffold(
floatingActionButton: FloatingActionButton(
darrenaustin marked this conversation as resolved.
Show resolved Hide resolved
onPressed: null,
),
bottomNavigationBar: BottomAppBar(),
),
)
);

final PhysicalShape physicalShape =
darrenaustin marked this conversation as resolved.
Show resolved Hide resolved
tester.widget(find.byType(PhysicalShape).at(0));

// For the default dark theme the overlay color for elevation 8 is 0xFF2D2D2D
expect(physicalShape.color, const Color(0xFF2D2D2D));
});

// This is a regression test for a bug we had where toggling the notch on/off
// would crash, as the shouldReclip method of ShapeBorderClipper or
// _BottomAppBarClipper would try an illegal downcast.
Expand Down