Skip to content

Commit

Permalink
fix: [MDS-407] Fix Chip not handling some colors with opacity properly (
Browse files Browse the repository at this point in the history
  • Loading branch information
Kypsis authored Feb 28, 2023
1 parent c941a4a commit a62ba9b
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 35 deletions.
12 changes: 6 additions & 6 deletions example/lib/src/storybook/stories/linear_progress.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class LinearProgressStory extends Story {
: super(
name: "Progress/LinearProgress",
builder: (context) {
final loaderSizesKnob = context.knobs.options(
final progressSizesKnob = context.knobs.options(
label: "MoonLinearProgressSize",
description: "LinearProgress size variants.",
initial: MoonLinearProgressSize.x4s,
Expand All @@ -21,23 +21,23 @@ class LinearProgressStory extends Story {
],
);

final loaderColorKnob = context.knobs.options(
final progressColorKnob = context.knobs.options(
label: "color",
description: "MoonColors variants for LinearProgress color.",
initial: 0, // piccolo
options: colorOptions,
);

final color = colorTable(context)[loaderColorKnob];
final color = colorTable(context)[progressColorKnob];

final loaderBackgroundColorKnob = context.knobs.options(
final progressBackgroundColorKnob = context.knobs.options(
label: "backgroundColor",
description: "MoonColors variants for LinearProgress background.",
initial: 6, // trunks
options: colorOptions,
);

final backgroundColor = colorTable(context)[loaderBackgroundColorKnob];
final backgroundColor = colorTable(context)[progressBackgroundColorKnob];

final borderRadiusValueKnob = context.knobs.sliderInt(
label: "bordeRadiusValue",
Expand All @@ -59,7 +59,7 @@ class LinearProgressStory extends Story {
const SizedBox(height: 64),
MoonLinearProgress(
value: linearProgressValueKnob,
loaderSize: loaderSizesKnob,
progressSize: progressSizesKnob,
color: color,
backgroundColor: backgroundColor,
borderRadiusValue: borderRadiusValueKnob.toDouble(),
Expand Down
61 changes: 61 additions & 0 deletions lib/src/utils/corrected_color_container.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'dart:ui';

import 'package:flutter/widgets.dart';

/// This is a workaround for visual bug in Flutter caused by the Color.lerp method which is using straight alpha instead
/// of premultiplied alpha leading to incorrect color lerping. Visually this manifests as a darker colored "hump" in
/// the middle of the lerp when any of the lerped colors has low enough alpha on a light background. The lower the alpha
/// value, the more pronounced the effect.
///
/// This is a known issue in Flutter and is being tracked here: https://github.com/flutter/flutter/issues/48534
class ColorWithPremulAlphaTween extends Tween<Color> {
ColorWithPremulAlphaTween({required Color begin, required Color end}) : super(begin: begin, end: end);

Color lerpWithPremul(Color a, Color b, double t) {
final w1 = (1 - t) * a.opacity;
final w2 = t * b.opacity;
final n = w1 + w2;
final w = n > 0.000001 ? w2 / n : 0.5;

return Color.fromARGB(
lerpDouble(a.alpha, b.alpha, t)!.toInt().clamp(0, 255),
lerpDouble(a.red, b.red, w)!.toInt().clamp(0, 255),
lerpDouble(a.green, b.green, w)!.toInt().clamp(0, 255),
lerpDouble(a.blue, b.blue, w)!.toInt().clamp(0, 255),
);
}

@override
Color lerp(double t) => lerpWithPremul(begin!, end!, t);
}

class CorrectedColorContainer extends StatelessWidget {
final Color color;
final EdgeInsetsDirectional? padding;
final Widget child;

/// This is a temporary fix widget to ensure colors with alpha get lerped correctly (Skia seems to be using buggy
/// straight alpha implementation but Impeller the correct premultiplied alpha implementation).
const CorrectedColorContainer({
required this.color,
required this.padding,
required this.child,
});

@override
Widget build(BuildContext context) {
return TweenAnimationBuilder(
duration: const Duration(milliseconds: 150),
curve: Curves.easeInOutCubic,
tween: ColorWithPremulAlphaTween(begin: color, end: color),
builder: (context, color, child) {
return Container(
color: color,
padding: padding,
child: child,
);
},
child: child,
);
}
}
56 changes: 31 additions & 25 deletions lib/src/widgets/chips/chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:moon_design/src/theme/colors.dart';
import 'package:moon_design/src/theme/effects/hover_effects.dart';
import 'package:moon_design/src/theme/theme.dart';
import 'package:moon_design/src/utils/animated_icon_theme.dart';
import 'package:moon_design/src/utils/corrected_color_container.dart';
import 'package:moon_design/src/widgets/common/base_control.dart';

enum MoonChipSize {
Expand Down Expand Up @@ -261,14 +262,13 @@ class MoonChip extends StatelessWidget {
);

return AnimatedContainer(
clipBehavior: Clip.antiAlias,
duration: effectiveHoverEffectDuration,
curve: effectiveHoverEffectCurve,
width: width,
height: effectiveHeight,
padding: correctedPadding,
constraints: BoxConstraints(minWidth: effectiveHeight),
decoration: ShapeDecoration(
color: canAnimate ? effectiveHoverEffectColor : effectiveBackgroundColor,
shape: SmoothRectangleBorder(
side: BorderSide(
color: effectiveBorderColor,
Expand All @@ -295,31 +295,37 @@ class MoonChip extends StatelessWidget {
),
),
),
child: AnimatedIconTheme(
duration: effectiveHoverEffectDuration,
curve: effectiveHoverEffectCurve,
color: effectiveTextColor,
size: effectiveMoonChipSize.iconSizeValue,
child: AnimatedDefaultTextStyle(
// TODO: Remove this once Impeller becomes the default render engine (see CorrectedColorContainer doc comment
// for more info).
child: CorrectedColorContainer(
color: canAnimate ? effectiveHoverEffectColor : effectiveBackgroundColor,
padding: correctedPadding,
child: AnimatedIconTheme(
duration: effectiveHoverEffectDuration,
curve: effectiveHoverEffectCurve,
style: TextStyle(fontSize: effectiveMoonChipSize.textStyle.fontSize, color: effectiveTextColor),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (leftIcon != null)
Padding(
padding: EdgeInsets.symmetric(horizontal: effectiveGap),
child: leftIcon,
),
if (label != null) label!,
if (rightIcon != null)
Padding(
padding: EdgeInsets.symmetric(horizontal: effectiveGap),
child: rightIcon,
),
],
color: effectiveTextColor,
size: effectiveMoonChipSize.iconSizeValue,
child: AnimatedDefaultTextStyle(
duration: effectiveHoverEffectDuration,
curve: effectiveHoverEffectCurve,
style: TextStyle(fontSize: effectiveMoonChipSize.textStyle.fontSize, color: effectiveTextColor),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (leftIcon != null)
Padding(
padding: EdgeInsets.symmetric(horizontal: effectiveGap),
child: leftIcon,
),
if (label != null) label!,
if (rightIcon != null)
Padding(
padding: EdgeInsets.symmetric(horizontal: effectiveGap),
child: rightIcon,
),
],
),
),
),
),
Expand Down
8 changes: 4 additions & 4 deletions lib/src/widgets/progress/linear_progress.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ enum MoonLinearProgressSize {
}

class MoonLinearProgress extends StatelessWidget {
final MoonLinearProgressSize? loaderSize;
final MoonLinearProgressSize? progressSize;
final double value;
final double? height;
final Color? color;
Expand All @@ -23,15 +23,15 @@ class MoonLinearProgress extends StatelessWidget {

const MoonLinearProgress({
super.key,
this.loaderSize,
this.progressSize,
required this.value,
this.height,
this.color,
this.backgroundColor,
this.borderRadiusValue,
});

MoonLinearProgressSizes _getMoonLoaderSize(BuildContext context, MoonLinearProgressSize? moonProgressSize) {
MoonLinearProgressSizes _getMoonProgressSize(BuildContext context, MoonLinearProgressSize? moonProgressSize) {
switch (moonProgressSize) {
case MoonLinearProgressSize.x6s:
return context.moonTheme?.linearProgressTheme.x6s ?? MoonLinearProgressSizes.x6s;
Expand All @@ -51,7 +51,7 @@ class MoonLinearProgress extends StatelessWidget {

@override
Widget build(BuildContext context) {
final MoonLinearProgressSizes effectiveProgressSize = _getMoonLoaderSize(context, loaderSize);
final MoonLinearProgressSizes effectiveProgressSize = _getMoonProgressSize(context, progressSize);
final double effectiveBorderRadiusValue = borderRadiusValue ?? effectiveProgressSize.borderRadiusValue;
final double effectiveHeight = height ?? effectiveProgressSize.progressHeight;
final Color effectiveColor = color ?? context.moonColors?.hit ?? MoonColors.light.hit;
Expand Down

0 comments on commit a62ba9b

Please sign in to comment.