Unverified Commit a4947055 authored by Darren Austin's avatar Darren Austin Committed by GitHub

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

* Updated the BottomAppBar to use elevation overlays when a dark theme is used.

* Moved the code for calculating the overlay color to a new utility
ElevationOverlay class and made it private by not including in the material package.
parent dd43da71
...@@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart'; ...@@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'bottom_app_bar_theme.dart'; import 'bottom_app_bar_theme.dart';
import 'elevation_overlay.dart';
import 'material.dart'; import 'material.dart';
import 'scaffold.dart'; import 'scaffold.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -127,10 +128,13 @@ class _BottomAppBarState extends State<BottomAppBar> { ...@@ -127,10 +128,13 @@ class _BottomAppBarState extends State<BottomAppBar> {
notchMargin: widget.notchMargin, notchMargin: widget.notchMargin,
) )
: const ShapeBorderClipper(shape: RoundedRectangleBorder()); : 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( return PhysicalShape(
clipper: clipper, clipper: clipper,
elevation: widget.elevation ?? babTheme.elevation ?? _defaultElevation, elevation: elevation,
color: widget.color ?? babTheme.color ?? Theme.of(context).bottomAppBarColor, color: effectiveColor,
clipBehavior: widget.clipBehavior, clipBehavior: widget.clipBehavior,
child: Material( child: Material(
type: MaterialType.transparency, type: MaterialType.transparency,
......
// Copyright 2019 The Chromium 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 'dart:math' as math;
import 'package:flutter/widgets.dart';
import 'theme.dart';
/// A simple utility class for dealing with the overlay color needed
/// to indicate elevation for dark theme widgets.
///
/// This is an internal implementation class and should not be exported by
/// the material package.
class ElevationOverlay {
ElevationOverlay._();
/// Applies an elevation overlay color 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);
}
}
// Copyright 2015 The Chromium Authors. All rights reserved. // Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'constants.dart'; import 'constants.dart';
import 'elevation_overlay.dart';
import 'theme.dart'; import 'theme.dart';
/// Signature for the callback used by ink effects to obtain the rectangle for the effect. /// Signature for the callback used by ink effects to obtain the rectangle for the effect.
...@@ -317,24 +316,6 @@ class Material extends StatefulWidget { ...@@ -317,24 +316,6 @@ class Material extends StatefulWidget {
static const double defaultSplashRadius = 35.0; 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 { class _MaterialState extends State<Material> with TickerProviderStateMixin {
final GlobalKey _inkFeatureRenderer = GlobalKey(debugLabel: 'ink renderer'); final GlobalKey _inkFeatureRenderer = GlobalKey(debugLabel: 'ink renderer');
...@@ -405,7 +386,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin { ...@@ -405,7 +386,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
clipBehavior: widget.clipBehavior, clipBehavior: widget.clipBehavior,
borderRadius: BorderRadius.zero, borderRadius: BorderRadius.zero,
elevation: widget.elevation, elevation: widget.elevation,
color: _elevationOverlayColor(context, backgroundColor, widget.elevation), color: ElevationOverlay.applyOverlay(context, backgroundColor, widget.elevation),
shadowColor: widget.shadowColor, shadowColor: widget.shadowColor,
animateColor: false, animateColor: false,
child: contents, child: contents,
...@@ -773,7 +754,7 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior> ...@@ -773,7 +754,7 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior>
), ),
clipBehavior: widget.clipBehavior, clipBehavior: widget.clipBehavior,
elevation: elevation, elevation: elevation,
color: _elevationOverlayColor(context, widget.color, elevation), color: ElevationOverlay.applyOverlay(context, widget.color, elevation),
shadowColor: _shadowColor.evaluate(animation), shadowColor: _shadowColor.evaluate(animation),
); );
} }
......
...@@ -617,6 +617,7 @@ class ThemeData extends Diagnosticable { ...@@ -617,6 +617,7 @@ class ThemeData extends Diagnosticable {
accentColor: colorScheme.secondary, accentColor: colorScheme.secondary,
accentColorBrightness: ThemeData.estimateBrightnessForColor(colorScheme.secondary), accentColorBrightness: ThemeData.estimateBrightnessForColor(colorScheme.secondary),
scaffoldBackgroundColor: colorScheme.background, scaffoldBackgroundColor: colorScheme.background,
bottomAppBarColor: colorScheme.surface,
cardColor: colorScheme.surface, cardColor: colorScheme.surface,
dividerColor: colorScheme.onSurface.withOpacity(0.12), dividerColor: colorScheme.onSurface.withOpacity(0.12),
backgroundColor: colorScheme.background, backgroundColor: colorScheme.background,
......
...@@ -139,6 +139,24 @@ void main() { ...@@ -139,6 +139,24 @@ void main() {
expect(physicalShape.color, const Color(0xff0000ff)); 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: Scaffold(
bottomNavigationBar: BottomAppBar(
color: const ColorScheme.dark().surface,
),
),
)
);
final PhysicalShape physicalShape = 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 // 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 // would crash, as the shouldReclip method of ShapeBorderClipper or
// _BottomAppBarClipper would try an illegal downcast. // _BottomAppBarClipper would try an illegal downcast.
......
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