From f27690c7faeead3b0f7438f0b4c323732b41d395 Mon Sep 17 00:00:00 2001 From: Nate Wilson Date: Fri, 26 Apr 2024 12:50:47 -0600 Subject: [PATCH] refactor `Material` build method (rebase) --- .../flutter/lib/src/material/material.dart | 129 +++++------------- 1 file changed, 37 insertions(+), 92 deletions(-) diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index 7cdb16255012..87d5e27e8db9 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart @@ -431,31 +431,16 @@ class Material extends StatefulWidget { class _MaterialState extends State with TickerProviderStateMixin { final GlobalKey _inkFeatureRenderer = GlobalKey(debugLabel: 'ink renderer'); - Color? _getBackgroundColor(BuildContext context) { - final ThemeData theme = Theme.of(context); - Color? color = widget.color; - if (color == null) { - switch (widget.type) { - case MaterialType.canvas: - color = theme.canvasColor; - case MaterialType.card: - color = theme.cardColor; - case MaterialType.button: - case MaterialType.circle: - case MaterialType.transparency: - break; - } - } - return color; - } - @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); - final Color? backgroundColor = _getBackgroundColor(context); - final Color modelShadowColor = widget.shadowColor ?? (theme.useMaterial3 ? theme.colorScheme.shadow : theme.shadowColor); - // If no shadow color is specified, use 0 for elevation in the model so a drop shadow won't be painted. - final double modelElevation = widget.elevation; + final Color? backgroundColor = widget.color ?? switch (widget.type) { + MaterialType.canvas => theme.canvasColor, + MaterialType.card => theme.cardColor, + MaterialType.button || MaterialType.circle || MaterialType.transparency => null, + }; + final Color modelShadowColor = widget.shadowColor + ?? (theme.useMaterial3 ? theme.colorScheme.shadow : theme.shadowColor); assert( backgroundColor != null || widget.type == MaterialType.transparency, 'If Material type is not MaterialType.transparency, a color must ' @@ -463,6 +448,7 @@ class _MaterialState extends State with TickerProviderStateMixin { 'in the theme (ex. canvasColor != null if type is set to ' 'MaterialType.canvas)', ); + Widget? contents = widget.child; if (contents != null) { contents = AnimatedDefaultTextStyle( @@ -486,6 +472,10 @@ class _MaterialState extends State with TickerProviderStateMixin { ), ); + ShapeBorder? shape = widget.borderRadius != null + ? RoundedRectangleBorder(borderRadius: widget.borderRadius!) + : widget.shape; + // PhysicalModel has a temporary workaround for a performance issue that // speeds up rectangular non transparent material (the workaround is to // skip the call to ui.Canvas.saveLayer if the border radius is 0). @@ -495,8 +485,8 @@ class _MaterialState extends State with TickerProviderStateMixin { // specified rectangles (e.g shape RoundedRectangleBorder with radius 0, but // we choose not to as we want the change from the fast-path to the // slow-path to be noticeable in the construction site of Material. - if (widget.type == MaterialType.canvas && widget.shape == null && widget.borderRadius == null) { - final Color color = Theme.of(context).useMaterial3 + if (widget.type == MaterialType.canvas && shape == null) { + final Color color = theme.useMaterial3 ? ElevationOverlay.applySurfaceTint(backgroundColor!, widget.surfaceTintColor, widget.elevation) : ElevationOverlay.applyOverlay(context, backgroundColor!, widget.elevation); @@ -504,7 +494,7 @@ class _MaterialState extends State with TickerProviderStateMixin { curve: Curves.fastOutSlowIn, duration: widget.animationDuration, clipBehavior: widget.clipBehavior, - elevation: modelElevation, + elevation: widget.elevation, color: color, shadowColor: modelShadowColor, animateColor: false, @@ -512,14 +502,22 @@ class _MaterialState extends State with TickerProviderStateMixin { ); } - final ShapeBorder shape = _getShape(); + shape ??= switch (widget.type) { + MaterialType.circle => const CircleBorder(), + MaterialType.canvas || MaterialType.transparency => const RoundedRectangleBorder(), + MaterialType.card || MaterialType.button => const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(2.0)), + ), + }; if (widget.type == MaterialType.transparency) { - return _transparentInterior( - context: context, - shape: shape, + return ClipPath( + clipper: ShapeBorderClipper( + shape: shape, + textDirection: Directionality.maybeOf(context), + ), clipBehavior: widget.clipBehavior, - contents: contents, + child: _ShapeBorderPaint(shape: shape, child: contents), ); } @@ -536,56 +534,6 @@ class _MaterialState extends State with TickerProviderStateMixin { child: contents, ); } - - static Widget _transparentInterior({ - required BuildContext context, - required ShapeBorder shape, - required Clip clipBehavior, - required Widget contents, - }) { - final _ShapeBorderPaint child = _ShapeBorderPaint( - shape: shape, - child: contents, - ); - return ClipPath( - clipper: ShapeBorderClipper( - shape: shape, - textDirection: Directionality.maybeOf(context), - ), - clipBehavior: clipBehavior, - child: child, - ); - } - - // Determines the shape for this Material. - // - // If a shape was specified, it will determine the shape. - // If a borderRadius was specified, the shape is a rounded - // rectangle. - // Otherwise, the shape is determined by the widget type as described in the - // Material class documentation. - ShapeBorder _getShape() { - if (widget.shape != null) { - return widget.shape!; - } - if (widget.borderRadius != null) { - return RoundedRectangleBorder(borderRadius: widget.borderRadius!); - } - switch (widget.type) { - case MaterialType.canvas: - case MaterialType.transparency: - return const RoundedRectangleBorder(); - - case MaterialType.card: - case MaterialType.button: - return RoundedRectangleBorder( - borderRadius: widget.borderRadius ?? kMaterialEdges[widget.type]!, - ); - - case MaterialType.circle: - return const CircleBorder(); - } - } } class _RenderInkFeatures extends RenderProxyBox implements MaterialInkController { @@ -899,7 +847,7 @@ class _MaterialInterior extends ImplicitlyAnimatedWidget { final Color color; /// The target shadow color. - final Color? shadowColor; + final Color shadowColor; /// The target surface tint color. final Color? surfaceTintColor; @@ -930,13 +878,11 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior> widget.elevation, (dynamic value) => Tween(begin: value as double), ) as Tween?; - _shadowColor = widget.shadowColor != null - ? visitor( - _shadowColor, - widget.shadowColor, - (dynamic value) => ColorTween(begin: value as Color), - ) as ColorTween? - : null; + _shadowColor = visitor( + _shadowColor, + widget.shadowColor, + (dynamic value) => ColorTween(begin: value as Color), + ) as ColorTween?; _surfaceTintColor = widget.surfaceTintColor != null ? visitor( _surfaceTintColor, @@ -958,16 +904,15 @@ class _MaterialInteriorState extends AnimatedWidgetBaseState<_MaterialInterior> final Color color = Theme.of(context).useMaterial3 ? ElevationOverlay.applySurfaceTint(widget.color, _surfaceTintColor?.evaluate(animation), elevation) : ElevationOverlay.applyOverlay(context, widget.color, elevation); - // If no shadow color is specified, use 0 for elevation in the model so a drop shadow won't be painted. - final double modelElevation = widget.shadowColor != null ? elevation : 0; - final Color shadowColor = _shadowColor?.evaluate(animation) ?? const Color(0x00000000); + final Color shadowColor = _shadowColor!.evaluate(animation)!; + return PhysicalShape( clipper: ShapeBorderClipper( shape: shape, textDirection: Directionality.maybeOf(context), ), clipBehavior: widget.clipBehavior, - elevation: modelElevation, + elevation: elevation, color: color, shadowColor: shadowColor, child: _ShapeBorderPaint(