blob: cd2e77aa69effd762cf67ff0f513db306b13dc83 [file] [log] [blame]
// 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 'dart:math' as math;
import 'package:flutter/widgets.dart';
import 'theme.dart';
/// A utility class for dealing with the overlay color needed
/// to indicate elevation of surfaces in a dark theme.
class ElevationOverlay {
// This class is not meant to be instantiated or extended; this constructor
// prevents instantiation and extension.
ElevationOverlay._();
/// Applies an overlay color to a surface color to indicate
/// the level of its elevation in a dark theme.
///
/// Material drop shadows can be difficult to see in a dark theme, so the
/// elevation of a surface should be portrayed with an "overlay" in addition
/// to the shadow. As the elevation of the component increases, the
/// overlay increases in opacity. This function computes and applies this
/// overlay to a given color as needed.
///
/// If the ambient theme is dark ([ThemeData.brightness] is [Brightness.dark]),
/// and [ThemeData.applyElevationOverlayColor] is true, and the given
/// [color] is [ColorScheme.surface] then this will return a version of
/// the [color] with a semi-transparent [ColorScheme.onSurface] overlaid
/// on top of it. The opacity of the overlay is computed based on 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.
/// * [Material] which uses this to apply an elevation overlay to its surface.
/// * <https://material.io/design/color/dark-theme.html>, which specifies how
/// the overlay should be applied.
static Color applyOverlay(BuildContext context, Color color, double elevation) {
final ThemeData theme = Theme.of(context);
if (elevation > 0.0 &&
theme.applyElevationOverlayColor &&
theme.brightness == Brightness.dark &&
color.withOpacity(1.0) == theme.colorScheme.surface.withOpacity(1.0)) {
return colorWithOverlay(color, theme.colorScheme.onSurface, elevation);
}
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);
return _overlayColor(theme.colorScheme.onSurface, elevation);
}
/// Returns a color blended by laying a semi-transparent overlay (using the
/// [overlay] color) on top of a surface (using the [surface] color).
///
/// The opacity of the overlay depends on [elevation]. As [elevation]
/// increases, the opacity will also increase.
///
/// See https://material.io/design/color/dark-theme.html#properties.
static Color colorWithOverlay(Color surface, Color overlay, double elevation) {
return Color.alphaBlend(_overlayColor(overlay, elevation), surface);
}
/// Applies an opacity to [color] based on [elevation].
static Color _overlayColor(Color color, double elevation) {
// 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 color.withOpacity(opacity);
}
}