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';
import 'package:flutter/widgets.dart';
import 'bottom_app_bar_theme.dart';
import 'elevation_overlay.dart';
import 'material.dart';
import 'scaffold.dart';
import 'theme.dart';
......@@ -127,10 +128,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,
......
// 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.
// 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/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'constants.dart';
import 'elevation_overlay.dart';
import 'theme.dart';
/// Signature for the callback used by ink effects to obtain the rectangle for the effect.
......@@ -317,24 +316,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');
......@@ -405,7 +386,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,
......@@ -773,7 +754,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),
);
}
......
......@@ -617,6 +617,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,
......
......@@ -139,6 +139,24 @@ 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: 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
// would crash, as the shouldReclip method of ShapeBorderClipper or
// _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