mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Refactor: Move sliders value indicator shape to seperate file (#162858)
Refactor: Move sliders value indicator shape to seperate file Part of: #162510 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
This commit is contained in:
parent
082a761570
commit
413fc7575f
@ -159,6 +159,7 @@ export 'src/material/selection_area.dart';
|
||||
export 'src/material/shadows.dart';
|
||||
export 'src/material/slider.dart';
|
||||
export 'src/material/slider_theme.dart';
|
||||
export 'src/material/slider_value_indicator_shape.dart';
|
||||
export 'src/material/snack_bar.dart';
|
||||
export 'src/material/snack_bar_theme.dart';
|
||||
export 'src/material/spell_check_suggestions_toolbar.dart';
|
||||
|
||||
@ -27,6 +27,7 @@ import 'constants.dart';
|
||||
import 'debug.dart';
|
||||
import 'material_state.dart';
|
||||
import 'slider_theme.dart';
|
||||
import 'slider_value_indicator_shape.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
// Examples can assume:
|
||||
|
||||
@ -26,6 +26,7 @@ import 'debug.dart';
|
||||
import 'material.dart';
|
||||
import 'material_state.dart';
|
||||
import 'slider_theme.dart';
|
||||
import 'slider_value_indicator_shape.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
// Examples can assume:
|
||||
|
||||
@ -2957,280 +2957,6 @@ class RoundSliderOverlayShape extends SliderComponentShape {
|
||||
}
|
||||
}
|
||||
|
||||
/// The default shape of a [Slider]'s value indicator.
|
||||
///
|
||||
/// 
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Slider], which includes a value indicator defined by this shape.
|
||||
/// * [SliderTheme], which can be used to configure the slider value indicator
|
||||
/// of all sliders in a widget subtree.
|
||||
class RectangularSliderValueIndicatorShape extends SliderComponentShape {
|
||||
/// Create a slider value indicator that resembles a rectangular tooltip.
|
||||
const RectangularSliderValueIndicatorShape();
|
||||
|
||||
static const _RectangularSliderValueIndicatorPathPainter _pathPainter =
|
||||
_RectangularSliderValueIndicatorPathPainter();
|
||||
|
||||
@override
|
||||
Size getPreferredSize(
|
||||
bool isEnabled,
|
||||
bool isDiscrete, {
|
||||
TextPainter? labelPainter,
|
||||
double? textScaleFactor,
|
||||
}) {
|
||||
assert(labelPainter != null);
|
||||
assert(textScaleFactor != null && textScaleFactor >= 0);
|
||||
return _pathPainter.getPreferredSize(labelPainter!, textScaleFactor!);
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(
|
||||
PaintingContext context,
|
||||
Offset center, {
|
||||
required Animation<double> activationAnimation,
|
||||
required Animation<double> enableAnimation,
|
||||
required bool isDiscrete,
|
||||
required TextPainter labelPainter,
|
||||
required RenderBox parentBox,
|
||||
required SliderThemeData sliderTheme,
|
||||
required TextDirection textDirection,
|
||||
required double value,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
}) {
|
||||
final Canvas canvas = context.canvas;
|
||||
final double scale = activationAnimation.value;
|
||||
_pathPainter.paint(
|
||||
parentBox: parentBox,
|
||||
canvas: canvas,
|
||||
center: center,
|
||||
scale: scale,
|
||||
labelPainter: labelPainter,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: sizeWithOverflow,
|
||||
backgroundPaintColor: sliderTheme.valueIndicatorColor!,
|
||||
strokePaintColor: sliderTheme.valueIndicatorStrokeColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The default shape of a [RangeSlider]'s value indicators.
|
||||
///
|
||||
/// 
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [RangeSlider], which includes value indicators defined by this shape.
|
||||
/// * [SliderTheme], which can be used to configure the range slider value
|
||||
/// indicator of all sliders in a widget subtree.
|
||||
class RectangularRangeSliderValueIndicatorShape extends RangeSliderValueIndicatorShape {
|
||||
/// Create a range slider value indicator that resembles a rectangular tooltip.
|
||||
const RectangularRangeSliderValueIndicatorShape();
|
||||
|
||||
static const _RectangularSliderValueIndicatorPathPainter _pathPainter =
|
||||
_RectangularSliderValueIndicatorPathPainter();
|
||||
|
||||
@override
|
||||
Size getPreferredSize(
|
||||
bool isEnabled,
|
||||
bool isDiscrete, {
|
||||
required TextPainter labelPainter,
|
||||
required double textScaleFactor,
|
||||
}) {
|
||||
assert(textScaleFactor >= 0);
|
||||
return _pathPainter.getPreferredSize(labelPainter, textScaleFactor);
|
||||
}
|
||||
|
||||
@override
|
||||
double getHorizontalShift({
|
||||
RenderBox? parentBox,
|
||||
Offset? center,
|
||||
TextPainter? labelPainter,
|
||||
Animation<double>? activationAnimation,
|
||||
double? textScaleFactor,
|
||||
Size? sizeWithOverflow,
|
||||
}) {
|
||||
return _pathPainter.getHorizontalShift(
|
||||
parentBox: parentBox!,
|
||||
center: center!,
|
||||
labelPainter: labelPainter!,
|
||||
textScaleFactor: textScaleFactor!,
|
||||
sizeWithOverflow: sizeWithOverflow!,
|
||||
scale: activationAnimation!.value,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(
|
||||
PaintingContext context,
|
||||
Offset center, {
|
||||
Animation<double>? activationAnimation,
|
||||
Animation<double>? enableAnimation,
|
||||
bool? isDiscrete,
|
||||
bool? isOnTop,
|
||||
TextPainter? labelPainter,
|
||||
double? textScaleFactor,
|
||||
Size? sizeWithOverflow,
|
||||
RenderBox? parentBox,
|
||||
SliderThemeData? sliderTheme,
|
||||
TextDirection? textDirection,
|
||||
double? value,
|
||||
Thumb? thumb,
|
||||
}) {
|
||||
final Canvas canvas = context.canvas;
|
||||
final double scale = activationAnimation!.value;
|
||||
_pathPainter.paint(
|
||||
parentBox: parentBox!,
|
||||
canvas: canvas,
|
||||
center: center,
|
||||
scale: scale,
|
||||
labelPainter: labelPainter!,
|
||||
textScaleFactor: textScaleFactor!,
|
||||
sizeWithOverflow: sizeWithOverflow!,
|
||||
backgroundPaintColor: sliderTheme!.valueIndicatorColor!,
|
||||
strokePaintColor:
|
||||
isOnTop!
|
||||
? sliderTheme.overlappingShapeStrokeColor
|
||||
: sliderTheme.valueIndicatorStrokeColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RectangularSliderValueIndicatorPathPainter {
|
||||
const _RectangularSliderValueIndicatorPathPainter();
|
||||
|
||||
static const double _triangleHeight = 8.0;
|
||||
static const double _labelPadding = 16.0;
|
||||
static const double _preferredHeight = 32.0;
|
||||
static const double _minLabelWidth = 16.0;
|
||||
static const double _bottomTipYOffset = 14.0;
|
||||
static const double _preferredHalfHeight = _preferredHeight / 2;
|
||||
static const double _upperRectRadius = 4;
|
||||
|
||||
Size getPreferredSize(TextPainter labelPainter, double textScaleFactor) {
|
||||
return Size(
|
||||
_upperRectangleWidth(labelPainter, 1, textScaleFactor),
|
||||
labelPainter.height + _labelPadding,
|
||||
);
|
||||
}
|
||||
|
||||
double getHorizontalShift({
|
||||
required RenderBox parentBox,
|
||||
required Offset center,
|
||||
required TextPainter labelPainter,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
required double scale,
|
||||
}) {
|
||||
assert(!sizeWithOverflow.isEmpty);
|
||||
|
||||
const double edgePadding = 8.0;
|
||||
final double rectangleWidth = _upperRectangleWidth(labelPainter, scale, textScaleFactor);
|
||||
|
||||
/// Value indicator draws on the Overlay and by using the global Offset
|
||||
/// we are making sure we use the bounds of the Overlay instead of the Slider.
|
||||
final Offset globalCenter = parentBox.localToGlobal(center);
|
||||
|
||||
// The rectangle must be shifted towards the center so that it minimizes the
|
||||
// chance of it rendering outside the bounds of the render box. If the shift
|
||||
// is negative, then the lobe is shifted from right to left, and if it is
|
||||
// positive, then the lobe is shifted from left to right.
|
||||
final double overflowLeft = math.max(0, rectangleWidth / 2 - globalCenter.dx + edgePadding);
|
||||
final double overflowRight = math.max(
|
||||
0,
|
||||
rectangleWidth / 2 - (sizeWithOverflow.width - globalCenter.dx - edgePadding),
|
||||
);
|
||||
|
||||
if (rectangleWidth < sizeWithOverflow.width) {
|
||||
return overflowLeft - overflowRight;
|
||||
} else if (overflowLeft - overflowRight > 0) {
|
||||
return overflowLeft - (edgePadding * textScaleFactor);
|
||||
} else {
|
||||
return -overflowRight + (edgePadding * textScaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
double _upperRectangleWidth(TextPainter labelPainter, double scale, double textScaleFactor) {
|
||||
final double unscaledWidth =
|
||||
math.max(_minLabelWidth * textScaleFactor, labelPainter.width) + _labelPadding * 2;
|
||||
return unscaledWidth * scale;
|
||||
}
|
||||
|
||||
void paint({
|
||||
required RenderBox parentBox,
|
||||
required Canvas canvas,
|
||||
required Offset center,
|
||||
required double scale,
|
||||
required TextPainter labelPainter,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
required Color backgroundPaintColor,
|
||||
Color? strokePaintColor,
|
||||
}) {
|
||||
if (scale == 0.0) {
|
||||
// Zero scale essentially means "do not draw anything", so it's safe to just return.
|
||||
return;
|
||||
}
|
||||
assert(!sizeWithOverflow.isEmpty);
|
||||
|
||||
final double rectangleWidth = _upperRectangleWidth(labelPainter, scale, textScaleFactor);
|
||||
final double horizontalShift = getHorizontalShift(
|
||||
parentBox: parentBox,
|
||||
center: center,
|
||||
labelPainter: labelPainter,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: sizeWithOverflow,
|
||||
scale: scale,
|
||||
);
|
||||
|
||||
final double rectHeight = labelPainter.height + _labelPadding;
|
||||
final Rect upperRect = Rect.fromLTWH(
|
||||
-rectangleWidth / 2 + horizontalShift,
|
||||
-_triangleHeight - rectHeight,
|
||||
rectangleWidth,
|
||||
rectHeight,
|
||||
);
|
||||
|
||||
final Path trianglePath =
|
||||
Path()
|
||||
..lineTo(-_triangleHeight, -_triangleHeight)
|
||||
..lineTo(_triangleHeight, -_triangleHeight)
|
||||
..close();
|
||||
final Paint fillPaint = Paint()..color = backgroundPaintColor;
|
||||
final RRect upperRRect = RRect.fromRectAndRadius(
|
||||
upperRect,
|
||||
const Radius.circular(_upperRectRadius),
|
||||
);
|
||||
trianglePath.addRRect(upperRRect);
|
||||
|
||||
canvas.save();
|
||||
// Prepare the canvas for the base of the tooltip, which is relative to the
|
||||
// center of the thumb.
|
||||
canvas.translate(center.dx, center.dy - _bottomTipYOffset);
|
||||
canvas.scale(scale, scale);
|
||||
if (strokePaintColor != null) {
|
||||
final Paint strokePaint =
|
||||
Paint()
|
||||
..color = strokePaintColor
|
||||
..strokeWidth = 1.0
|
||||
..style = PaintingStyle.stroke;
|
||||
canvas.drawPath(trianglePath, strokePaint);
|
||||
}
|
||||
canvas.drawPath(trianglePath, fillPaint);
|
||||
|
||||
// The label text is centered within the value indicator.
|
||||
final double bottomTipToUpperRectTranslateY = -_preferredHalfHeight / 2 - upperRect.height;
|
||||
canvas.translate(0, bottomTipToUpperRectTranslateY);
|
||||
final Offset boxCenter = Offset(horizontalShift, upperRect.height / 2);
|
||||
final Offset halfLabelPainterOffset = Offset(labelPainter.width / 2, labelPainter.height / 2);
|
||||
final Offset labelOffset = boxCenter - halfLabelPainterOffset;
|
||||
labelPainter.paint(canvas, labelOffset);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant shape of a [Slider]'s value indicator . The value indicator is in
|
||||
/// the shape of an upside-down pear.
|
||||
///
|
||||
|
||||
@ -0,0 +1,282 @@
|
||||
// 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 'slider_theme.dart';
|
||||
|
||||
/// The default shape of a [Slider]'s value indicator.
|
||||
///
|
||||
/// 
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Slider], which includes a value indicator defined by this shape.
|
||||
/// * [SliderTheme], which can be used to configure the slider value indicator
|
||||
/// of all sliders in a widget subtree.
|
||||
class RectangularSliderValueIndicatorShape extends SliderComponentShape {
|
||||
/// Create a slider value indicator that resembles a rectangular tooltip.
|
||||
const RectangularSliderValueIndicatorShape();
|
||||
|
||||
static const _RectangularSliderValueIndicatorPathPainter _pathPainter =
|
||||
_RectangularSliderValueIndicatorPathPainter();
|
||||
|
||||
@override
|
||||
Size getPreferredSize(
|
||||
bool isEnabled,
|
||||
bool isDiscrete, {
|
||||
TextPainter? labelPainter,
|
||||
double? textScaleFactor,
|
||||
}) {
|
||||
assert(labelPainter != null);
|
||||
assert(textScaleFactor != null && textScaleFactor >= 0);
|
||||
return _pathPainter.getPreferredSize(labelPainter!, textScaleFactor!);
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(
|
||||
PaintingContext context,
|
||||
Offset center, {
|
||||
required Animation<double> activationAnimation,
|
||||
required Animation<double> enableAnimation,
|
||||
required bool isDiscrete,
|
||||
required TextPainter labelPainter,
|
||||
required RenderBox parentBox,
|
||||
required SliderThemeData sliderTheme,
|
||||
required TextDirection textDirection,
|
||||
required double value,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
}) {
|
||||
final Canvas canvas = context.canvas;
|
||||
final double scale = activationAnimation.value;
|
||||
_pathPainter.paint(
|
||||
parentBox: parentBox,
|
||||
canvas: canvas,
|
||||
center: center,
|
||||
scale: scale,
|
||||
labelPainter: labelPainter,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: sizeWithOverflow,
|
||||
backgroundPaintColor: sliderTheme.valueIndicatorColor!,
|
||||
strokePaintColor: sliderTheme.valueIndicatorStrokeColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The default shape of a [RangeSlider]'s value indicators.
|
||||
///
|
||||
/// 
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [RangeSlider], which includes value indicators defined by this shape.
|
||||
/// * [SliderTheme], which can be used to configure the range slider value
|
||||
/// indicator of all sliders in a widget subtree.
|
||||
class RectangularRangeSliderValueIndicatorShape extends RangeSliderValueIndicatorShape {
|
||||
/// Create a range slider value indicator that resembles a rectangular tooltip.
|
||||
const RectangularRangeSliderValueIndicatorShape();
|
||||
|
||||
static const _RectangularSliderValueIndicatorPathPainter _pathPainter =
|
||||
_RectangularSliderValueIndicatorPathPainter();
|
||||
|
||||
@override
|
||||
Size getPreferredSize(
|
||||
bool isEnabled,
|
||||
bool isDiscrete, {
|
||||
required TextPainter labelPainter,
|
||||
required double textScaleFactor,
|
||||
}) {
|
||||
assert(textScaleFactor >= 0);
|
||||
return _pathPainter.getPreferredSize(labelPainter, textScaleFactor);
|
||||
}
|
||||
|
||||
@override
|
||||
double getHorizontalShift({
|
||||
RenderBox? parentBox,
|
||||
Offset? center,
|
||||
TextPainter? labelPainter,
|
||||
Animation<double>? activationAnimation,
|
||||
double? textScaleFactor,
|
||||
Size? sizeWithOverflow,
|
||||
}) {
|
||||
return _pathPainter.getHorizontalShift(
|
||||
parentBox: parentBox!,
|
||||
center: center!,
|
||||
labelPainter: labelPainter!,
|
||||
textScaleFactor: textScaleFactor!,
|
||||
sizeWithOverflow: sizeWithOverflow!,
|
||||
scale: activationAnimation!.value,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(
|
||||
PaintingContext context,
|
||||
Offset center, {
|
||||
Animation<double>? activationAnimation,
|
||||
Animation<double>? enableAnimation,
|
||||
bool? isDiscrete,
|
||||
bool? isOnTop,
|
||||
TextPainter? labelPainter,
|
||||
double? textScaleFactor,
|
||||
Size? sizeWithOverflow,
|
||||
RenderBox? parentBox,
|
||||
SliderThemeData? sliderTheme,
|
||||
TextDirection? textDirection,
|
||||
double? value,
|
||||
Thumb? thumb,
|
||||
}) {
|
||||
final Canvas canvas = context.canvas;
|
||||
final double scale = activationAnimation!.value;
|
||||
_pathPainter.paint(
|
||||
parentBox: parentBox!,
|
||||
canvas: canvas,
|
||||
center: center,
|
||||
scale: scale,
|
||||
labelPainter: labelPainter!,
|
||||
textScaleFactor: textScaleFactor!,
|
||||
sizeWithOverflow: sizeWithOverflow!,
|
||||
backgroundPaintColor: sliderTheme!.valueIndicatorColor!,
|
||||
strokePaintColor:
|
||||
isOnTop!
|
||||
? sliderTheme.overlappingShapeStrokeColor
|
||||
: sliderTheme.valueIndicatorStrokeColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RectangularSliderValueIndicatorPathPainter {
|
||||
const _RectangularSliderValueIndicatorPathPainter();
|
||||
|
||||
static const double _triangleHeight = 8.0;
|
||||
static const double _labelPadding = 16.0;
|
||||
static const double _preferredHeight = 32.0;
|
||||
static const double _minLabelWidth = 16.0;
|
||||
static const double _bottomTipYOffset = 14.0;
|
||||
static const double _preferredHalfHeight = _preferredHeight / 2;
|
||||
static const double _upperRectRadius = 4;
|
||||
|
||||
Size getPreferredSize(TextPainter labelPainter, double textScaleFactor) {
|
||||
return Size(
|
||||
_upperRectangleWidth(labelPainter, 1, textScaleFactor),
|
||||
labelPainter.height + _labelPadding,
|
||||
);
|
||||
}
|
||||
|
||||
double getHorizontalShift({
|
||||
required RenderBox parentBox,
|
||||
required Offset center,
|
||||
required TextPainter labelPainter,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
required double scale,
|
||||
}) {
|
||||
assert(!sizeWithOverflow.isEmpty);
|
||||
|
||||
const double edgePadding = 8.0;
|
||||
final double rectangleWidth = _upperRectangleWidth(labelPainter, scale, textScaleFactor);
|
||||
|
||||
/// Value indicator draws on the Overlay and by using the global Offset
|
||||
/// we are making sure we use the bounds of the Overlay instead of the Slider.
|
||||
final Offset globalCenter = parentBox.localToGlobal(center);
|
||||
|
||||
// The rectangle must be shifted towards the center so that it minimizes the
|
||||
// chance of it rendering outside the bounds of the render box. If the shift
|
||||
// is negative, then the lobe is shifted from right to left, and if it is
|
||||
// positive, then the lobe is shifted from left to right.
|
||||
final double overflowLeft = math.max(0, rectangleWidth / 2 - globalCenter.dx + edgePadding);
|
||||
final double overflowRight = math.max(
|
||||
0,
|
||||
rectangleWidth / 2 - (sizeWithOverflow.width - globalCenter.dx - edgePadding),
|
||||
);
|
||||
|
||||
if (rectangleWidth < sizeWithOverflow.width) {
|
||||
return overflowLeft - overflowRight;
|
||||
} else if (overflowLeft - overflowRight > 0) {
|
||||
return overflowLeft - (edgePadding * textScaleFactor);
|
||||
} else {
|
||||
return -overflowRight + (edgePadding * textScaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
double _upperRectangleWidth(TextPainter labelPainter, double scale, double textScaleFactor) {
|
||||
final double unscaledWidth =
|
||||
math.max(_minLabelWidth * textScaleFactor, labelPainter.width) + _labelPadding * 2;
|
||||
return unscaledWidth * scale;
|
||||
}
|
||||
|
||||
void paint({
|
||||
required RenderBox parentBox,
|
||||
required Canvas canvas,
|
||||
required Offset center,
|
||||
required double scale,
|
||||
required TextPainter labelPainter,
|
||||
required double textScaleFactor,
|
||||
required Size sizeWithOverflow,
|
||||
required Color backgroundPaintColor,
|
||||
Color? strokePaintColor,
|
||||
}) {
|
||||
if (scale == 0.0) {
|
||||
// Zero scale essentially means "do not draw anything", so it's safe to just return.
|
||||
return;
|
||||
}
|
||||
assert(!sizeWithOverflow.isEmpty);
|
||||
|
||||
final double rectangleWidth = _upperRectangleWidth(labelPainter, scale, textScaleFactor);
|
||||
final double horizontalShift = getHorizontalShift(
|
||||
parentBox: parentBox,
|
||||
center: center,
|
||||
labelPainter: labelPainter,
|
||||
textScaleFactor: textScaleFactor,
|
||||
sizeWithOverflow: sizeWithOverflow,
|
||||
scale: scale,
|
||||
);
|
||||
|
||||
final double rectHeight = labelPainter.height + _labelPadding;
|
||||
final Rect upperRect = Rect.fromLTWH(
|
||||
-rectangleWidth / 2 + horizontalShift,
|
||||
-_triangleHeight - rectHeight,
|
||||
rectangleWidth,
|
||||
rectHeight,
|
||||
);
|
||||
|
||||
final Path trianglePath =
|
||||
Path()
|
||||
..lineTo(-_triangleHeight, -_triangleHeight)
|
||||
..lineTo(_triangleHeight, -_triangleHeight)
|
||||
..close();
|
||||
final Paint fillPaint = Paint()..color = backgroundPaintColor;
|
||||
final RRect upperRRect = RRect.fromRectAndRadius(
|
||||
upperRect,
|
||||
const Radius.circular(_upperRectRadius),
|
||||
);
|
||||
trianglePath.addRRect(upperRRect);
|
||||
|
||||
canvas.save();
|
||||
// Prepare the canvas for the base of the tooltip, which is relative to the
|
||||
// center of the thumb.
|
||||
canvas.translate(center.dx, center.dy - _bottomTipYOffset);
|
||||
canvas.scale(scale, scale);
|
||||
if (strokePaintColor != null) {
|
||||
final Paint strokePaint =
|
||||
Paint()
|
||||
..color = strokePaintColor
|
||||
..strokeWidth = 1.0
|
||||
..style = PaintingStyle.stroke;
|
||||
canvas.drawPath(trianglePath, strokePaint);
|
||||
}
|
||||
canvas.drawPath(trianglePath, fillPaint);
|
||||
|
||||
// The label text is centered within the value indicator.
|
||||
final double bottomTipToUpperRectTranslateY = -_preferredHalfHeight / 2 - upperRect.height;
|
||||
canvas.translate(0, bottomTipToUpperRectTranslateY);
|
||||
final Offset boxCenter = Offset(horizontalShift, upperRect.height / 2);
|
||||
final Offset halfLabelPainterOffset = Offset(labelPainter.width / 2, labelPainter.height / 2);
|
||||
final Offset labelOffset = boxCenter - halfLabelPainterOffset;
|
||||
labelPainter.paint(canvas, labelOffset);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user