Skip to content

Commit

Permalink
Add ability to customize drawer shape and color as well as theme draw…
Browse files Browse the repository at this point in the history
…er properties (flutter#89237)
  • Loading branch information
rami-a authored Sep 1, 2021
1 parent 947034e commit 069699e
Show file tree
Hide file tree
Showing 7 changed files with 438 additions and 11 deletions.
1 change: 1 addition & 0 deletions packages/flutter/lib/material.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export 'src/material/divider.dart';
export 'src/material/divider_theme.dart';
export 'src/material/drawer.dart';
export 'src/material/drawer_header.dart';
export 'src/material/drawer_theme.dart';
export 'src/material/dropdown.dart';
export 'src/material/elevated_button.dart';
export 'src/material/elevated_button_theme.dart';
Expand Down
53 changes: 43 additions & 10 deletions packages/flutter/lib/src/material/drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:flutter/widgets.dart';

import 'colors.dart';
import 'debug.dart';
import 'drawer_theme.dart';
import 'list_tile.dart';
import 'material.dart';
import 'material_localizations.dart';
Expand Down Expand Up @@ -139,19 +140,36 @@ class Drawer extends StatelessWidget {
/// The [elevation] must be non-negative.
const Drawer({
Key? key,
this.elevation = 16.0,
this.backgroundColor,
this.elevation,
this.shape,
this.child,
this.semanticLabel,
}) : assert(elevation != null && elevation >= 0.0),
}) : assert(elevation == null || elevation >= 0.0),
super(key: key);

/// Sets the color of the [Material] that holds all of the [Drawer]'s
/// contents.
///
/// If this is null, then [DrawerThemeData.backgroundColor] is used. If that
/// is also null, then it falls back to [Material]'s default.
final Color? backgroundColor;

/// The z-coordinate at which to place this drawer relative to its parent.
///
/// This controls the size of the shadow below the drawer.
///
/// Defaults to 16, the appropriate elevation for drawers. The value is
/// always non-negative.
final double elevation;
/// If this is null, then [DrawerThemeData.elevation] is used. If that
/// is also null, then it defaults to 16.0.
final double? elevation;

/// The shape of the drawer.
///
/// Defines the drawer's [Material.shape].
///
/// If this is null, then [DrawerThemeData.shape] is used. If that
/// is also null, then it falls back to [Material]'s default.
final ShapeBorder? shape;

/// The widget below this widget in the tree.
///
Expand All @@ -175,6 +193,7 @@ class Drawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final DrawerThemeData drawerTheme = DrawerTheme.of(context);
String? label = semanticLabel;
switch (Theme.of(context).platform) {
case TargetPlatform.iOS:
Expand All @@ -194,7 +213,9 @@ class Drawer extends StatelessWidget {
child: ConstrainedBox(
constraints: const BoxConstraints.expand(width: _kWidth),
child: Material(
elevation: elevation,
color: backgroundColor ?? drawerTheme.backgroundColor,
elevation: elevation ?? drawerTheme.elevation ?? 16.0,
shape: shape ?? drawerTheme.shape,
child: child,
),
),
Expand Down Expand Up @@ -277,9 +298,11 @@ class DrawerController extends StatefulWidget {
/// {@endtemplate}
final DragStartBehavior dragStartBehavior;

/// The color to use for the scrim that obscures primary content while a drawer is open.
/// The color to use for the scrim that obscures the underlying content while
/// a drawer is open.
///
/// By default, the color used is [Colors.black54]
/// If this is null, then [DrawerThemeData.scrimColor] is used. If that
/// is also null, then it defaults to [Colors.black54].
final Color? scrimColor;

/// Determines if the [Drawer] can be opened with a drag gesture.
Expand Down Expand Up @@ -317,7 +340,6 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
@override
void initState() {
super.initState();
_scrimColorTween = _buildScrimColorTween();
_controller = AnimationController(
value: widget.isDrawerOpen ? 1.0 : 0.0,
duration: _kBaseSettleDuration,
Expand All @@ -335,6 +357,12 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
super.dispose();
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
_scrimColorTween = _buildScrimColorTween();
}

@override
void didUpdateWidget(DrawerController oldWidget) {
super.didUpdateWidget(oldWidget);
Expand Down Expand Up @@ -492,7 +520,12 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
final GlobalKey _gestureDetectorKey = GlobalKey();

ColorTween _buildScrimColorTween() {
return ColorTween(begin: Colors.transparent, end: widget.scrimColor ?? Colors.black54);
return ColorTween(
begin: Colors.transparent,
end: widget.scrimColor
?? DrawerTheme.of(context).scrimColor
?? Colors.black54,
);
}

AlignmentDirectional get _drawerOuterAlignment {
Expand Down
161 changes: 161 additions & 0 deletions packages/flutter/lib/src/material/drawer_theme.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// 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:ui' show lerpDouble;

import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

import 'theme.dart';

/// Defines default property values for descendant [Drawer] widgets.
///
/// Descendant widgets obtain the current [DrawerThemeData] object
/// using `DrawerTheme.of(context)`. Instances of [DrawerThemeData] can be
/// customized with [DrawerThemeData.copyWith].
///
/// Typically a [DrawerThemeData] is specified as part of the
/// overall [Theme] with [ThemeData.drawerTheme].
///
/// All [DrawerThemeData] properties are `null` by default.
///
/// See also:
///
/// * [DrawerTheme], an [InheritedWidget] that propagates the theme down its
/// subtree.
/// * [ThemeData], which describes the overall theme information for the
/// application and can customize a drawer using [ThemeData.drawerTheme].
@immutable
class DrawerThemeData with Diagnosticable {
/// Creates a theme that can be used for [ThemeData.drawerTheme] and
/// [DrawerTheme].
const DrawerThemeData({
this.backgroundColor,
this.scrimColor,
this.elevation,
this.shape,
});

/// Overrides the default value of [Drawer.backgroundColor].
final Color? backgroundColor;

/// Overrides the default value of [DrawerController.scrimColor].
final Color? scrimColor;

/// Overrides the default value of [Drawer.elevation].
final double? elevation;

/// Overrides the default value of [Drawer.shape].
final ShapeBorder? shape;

/// Creates a copy of this object with the given fields replaced with the
/// new values.
DrawerThemeData copyWith({
Color? backgroundColor,
Color? scrimColor,
double? elevation,
ShapeBorder? shape,
}) {
return DrawerThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
scrimColor: scrimColor ?? this.scrimColor,
elevation: elevation ?? this.elevation,
shape: shape ?? this.shape,
);
}

/// Linearly interpolate between two drawer themes.
///
/// If both arguments are null then null is returned.
///
/// {@macro dart.ui.shadow.lerp}
static DrawerThemeData? lerp(DrawerThemeData? a, DrawerThemeData? b, double t) {
assert(t != null);
if (a == null && b == null)
return null;
return DrawerThemeData(
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
scrimColor: Color.lerp(a?.scrimColor, b?.scrimColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
);
}

@override
int get hashCode {
return hashValues(
backgroundColor,
scrimColor,
elevation,
shape,
);
}

@override
bool operator ==(Object other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
return other is DrawerThemeData
&& other.backgroundColor == backgroundColor
&& other.scrimColor == scrimColor
&& other.elevation == elevation
&& other.shape == shape;
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
properties.add(ColorProperty('scrimColor', scrimColor, defaultValue: null));
properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
}
}

/// An inherited widget that defines visual properties for [Drawer]s in this
/// widget's subtree.
///
/// Values specified here are used for [Drawer] properties that are not
/// given an explicit non-null value.
///
/// Using this would allow you to override the [ThemeData.drawerTheme].
class DrawerTheme extends InheritedTheme {
/// Creates a theme that defines the [DrawerThemeData] properties for a
/// [Drawer].
const DrawerTheme({
Key? key,
required this.data,
required Widget child,
}) : assert(data != null), super(key: key, child: child);

/// Specifies the background color, scrim color, elevation, and shape for
/// descendant [Drawer] widgets.
final DrawerThemeData data;

/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [DrawerTheme] widget, then
/// [ThemeData.drawerTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// DrawerTheme theme = DrawerTheme.of(context);
/// ```
static DrawerThemeData of(BuildContext context) {
final DrawerTheme? drawerTheme = context.dependOnInheritedWidgetOfExactType<DrawerTheme>();
return drawerTheme?.data ?? Theme.of(context).drawerTheme;
}

@override
Widget wrap(BuildContext context, Widget child) {
return DrawerTheme(data: data, child: child);
}

@override
bool updateShouldNotify(DrawerTheme oldWidget) => data != oldWidget.data;
}
3 changes: 2 additions & 1 deletion packages/flutter/lib/src/material/scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1619,7 +1619,8 @@ class Scaffold extends StatefulWidget {

/// The color to use for the scrim that obscures primary content while a drawer is open.
///
/// By default, the color is [Colors.black54]
/// If this is null, then [DrawerThemeData.scrimColor] is used. If that
/// is also null, then it defaults to [Colors.black54].
final Color? drawerScrimColor;

/// The color of the [Material] widget that underlies the entire Scaffold.
Expand Down
Loading

0 comments on commit 069699e

Please sign in to comment.