switched to a double variant of clamp to avoid boxing (#103559)

This commit is contained in:
gaaclarke 2022-05-18 13:26:08 -07:00 committed by GitHub
parent 32157e3fcb
commit 64a0c19652
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 409 additions and 193 deletions

View File

@ -0,0 +1,73 @@
// 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 'package:flutter/foundation.dart' show clampDouble;
import '../common.dart';
const int _kBatchSize = 100000;
const int _kNumIterations = 1000;
void main() {
assert(false,
"Don't run benchmarks in debug mode! Use 'flutter run --release'.");
final BenchmarkResultPrinter printer = BenchmarkResultPrinter();
final Stopwatch watch = Stopwatch();
{
final List<double> clampDoubleValues = <double>[];
for (int j = 0; j < _kNumIterations; ++j) {
double tally = 0;
watch.reset();
watch.start();
for (int i = 0; i < _kBatchSize; i += 1) {
tally += clampDouble(-1.0, 0.0, 1.0);
tally += clampDouble(2.0, 0.0, 1.0);
tally += clampDouble(0.0, 0.0, 1.0);
tally += clampDouble(double.nan, 0.0, 1.0);
}
watch.stop();
clampDoubleValues.add(watch.elapsedMicroseconds.toDouble() / _kBatchSize);
if (tally < 0.0) {
print("This shouldn't happen.");
}
}
printer.addResultStatistics(
description: 'clamp - clampDouble',
values: clampDoubleValues,
unit: 'us per iteration',
name: 'clamp_clampDouble',
);
}
{
final List<double> doubleClampValues = <double>[];
for (int j = 0; j < _kNumIterations; ++j) {
double tally = 0;
watch.reset();
watch.start();
for (int i = 0; i < _kBatchSize; i += 1) {
tally += -1.0.clamp(0.0, 1.0);
tally += 2.0.clamp(0.0, 1.0);
tally += 0.0.clamp(0.0, 1.0);
tally += double.nan.clamp(0.0, 1.0);
}
watch.stop();
doubleClampValues.add(watch.elapsedMicroseconds.toDouble() / _kBatchSize);
if (tally < 0.0) {
print("This shouldn't happen.");
}
}
printer.addResultStatistics(
description: 'clamp - Double.clamp',
values: doubleClampValues,
unit: 'us per iteration',
name: 'clamp_Double_clamp',
);
}
printer.printToStdout();
}

View File

@ -88,6 +88,9 @@ Future<void> run(List<String> arguments) async {
exitWithError(<String>['The analyze.dart script must be run with --enable-asserts.']);
}
print('$clock No Double.clamp');
await verifyNoDoubleClamp(flutterRoot);
print('$clock All tool test files end in _test.dart...');
await verifyToolTestsEndInTestDart(flutterRoot);
@ -203,6 +206,80 @@ Future<void> run(List<String> arguments) async {
// TESTS
FeatureSet _parsingFeatureSet() => FeatureSet.fromEnableFlags2(
sdkLanguageVersion: Version.parse('2.17.0-0'),
flags: <String>['super-parameters']);
_Line _getLine(ParseStringResult parseResult, int offset) {
final int lineNumber =
parseResult.lineInfo.getLocation(offset).lineNumber;
final String content = parseResult.content.substring(
parseResult.lineInfo.getOffsetOfLine(lineNumber - 1),
parseResult.lineInfo.getOffsetOfLine(lineNumber) - 1);
return _Line(lineNumber, content);
}
class _DoubleClampVisitor extends RecursiveAstVisitor<CompilationUnit> {
_DoubleClampVisitor(this.parseResult);
final List<_Line> clamps = <_Line>[];
final ParseStringResult parseResult;
@override
CompilationUnit? visitMethodInvocation(MethodInvocation node) {
if (node.methodName.name == 'clamp') {
final _Line line = _getLine(parseResult, node.function.offset);
if (!line.content.contains('// ignore_clamp_double_lint')) {
clamps.add(line);
}
}
node.visitChildren(this);
return null;
}
}
/// Verify that we use clampDouble instead of Double.clamp for performance reasons.
///
/// We currently can't distinguish valid uses of clamp from problematic ones so
/// if the clamp is operating on a type other than a `double` the
/// `// ignore_clamp_double_lint` comment must be added to the line where clamp is
/// invoked.
///
/// See also:
/// * https://github.com/flutter/flutter/pull/103559
/// * https://github.com/flutter/flutter/issues/103917
Future<void> verifyNoDoubleClamp(String workingDirectory) async {
final String flutterLibPath = '$workingDirectory/packages/flutter/lib';
final Stream<File> testFiles =
_allFiles(flutterLibPath, 'dart', minimumMatches: 100);
final List<String> errors = <String>[];
await for (final File file in testFiles) {
try {
final ParseStringResult parseResult = parseFile(
featureSet: _parsingFeatureSet(),
path: file.absolute.path,
);
final _DoubleClampVisitor visitor = _DoubleClampVisitor(parseResult);
visitor.visitCompilationUnit(parseResult.unit);
for (final _Line clamp in visitor.clamps) {
errors.add('${file.path}:${clamp.line}: `clamp` method used without ignore_clamp_double_lint comment.');
}
} catch (ex) {
// TODO(gaaclarke): There is a bug with super parameter parsing on mac so
// we skip certain files until that is fixed.
// https://github.com/dart-lang/sdk/issues/49032
print('skipping ${file.path}: $ex');
}
}
if (errors.isNotEmpty) {
exitWithError(<String>[
...errors,
'\n${bold}See: https://github.com/flutter/flutter/pull/103559',
]);
}
}
/// Verify tool test files end in `_test.dart`.
///
/// The test runner will only recognize files ending in `_test.dart` as tests to
@ -518,16 +595,16 @@ Future<int> _verifyNoMissingLicenseForExtension(
return 0;
}
class _TestSkip {
_TestSkip(this.line, this.content);
class _Line {
_Line(this.line, this.content);
final int line;
final String content;
}
Iterable<_TestSkip> _getTestSkips(File file) {
Iterable<_Line> _getTestSkips(File file) {
final ParseStringResult parseResult = parseFile(
featureSet: FeatureSet.fromEnableFlags2(sdkLanguageVersion: Version.parse('2.17.0-0'), flags: <String>['super-parameters']),
featureSet: _parsingFeatureSet(),
path: file.absolute.path,
);
final _TestSkipLinesVisitor<CompilationUnit> visitor = _TestSkipLinesVisitor<CompilationUnit>(parseResult);
@ -536,10 +613,10 @@ Iterable<_TestSkip> _getTestSkips(File file) {
}
class _TestSkipLinesVisitor<T> extends RecursiveAstVisitor<T> {
_TestSkipLinesVisitor(this.parseResult) : skips = <_TestSkip>{};
_TestSkipLinesVisitor(this.parseResult) : skips = <_Line>{};
final ParseStringResult parseResult;
final Set<_TestSkip> skips;
final Set<_Line> skips;
static bool isTestMethod(String name) {
return name.startsWith('test') || name == 'group' || name == 'expect';
@ -550,10 +627,7 @@ class _TestSkipLinesVisitor<T> extends RecursiveAstVisitor<T> {
if (isTestMethod(node.methodName.toString())) {
for (final Expression argument in node.argumentList.arguments) {
if (argument is NamedExpression && argument.name.label.name == 'skip') {
final int lineNumber = parseResult.lineInfo.getLocation(argument.beginToken.charOffset).lineNumber;
final String content = parseResult.content.substring(parseResult.lineInfo.getOffsetOfLine(lineNumber - 1),
parseResult.lineInfo.getOffsetOfLine(lineNumber) - 1);
skips.add(_TestSkip(lineNumber, content));
skips.add(_getLine(parseResult, argument.beginToken.charOffset));
}
}
}
@ -571,7 +645,7 @@ Future<void> verifySkipTestComments(String workingDirectory) async {
.where((File f) => f.path.endsWith('_test.dart'));
await for (final File file in testFiles) {
for (final _TestSkip skip in _getTestSkips(file)) {
for (final _Line skip in _getTestSkips(file)) {
final Match? match = _skipTestCommentPattern.firstMatch(skip.content);
final String? skipComment = match?.group(1);
if (skipComment == null ||

View File

@ -60,6 +60,7 @@ TaskFunction createMicrobenchmarkTask() {
...await runMicrobench('lib/language/sync_star_bench.dart'),
...await runMicrobench('lib/language/sync_star_semantics_bench.dart'),
...await runMicrobench('lib/foundation/all_elements_bench.dart'),
...await runMicrobench('lib/foundation/clamp.dart'),
...await runMicrobench('lib/foundation/change_notifier_bench.dart'),
...await runMicrobench('lib/foundation/standard_method_codec_bench.dart'),
...await runMicrobench('lib/foundation/standard_message_codec_bench.dart'),

View File

@ -33,6 +33,7 @@ export 'src/foundation/diagnostics.dart';
export 'src/foundation/isolates.dart';
export 'src/foundation/key.dart';
export 'src/foundation/licenses.dart';
export 'src/foundation/math.dart';
export 'src/foundation/node.dart';
export 'src/foundation/object.dart';
export 'src/foundation/observer_list.dart';

View File

@ -395,7 +395,7 @@ class AnimationController extends Animation<double>
}
void _internalSetValue(double newValue) {
_value = newValue.clamp(lowerBound, upperBound);
_value = clampDouble(newValue, lowerBound, upperBound);
if (_value == lowerBound) {
_status = AnimationStatus.dismissed;
} else if (_value == upperBound) {
@ -598,7 +598,7 @@ class AnimationController extends Animation<double>
stop();
if (simulationDuration == Duration.zero) {
if (value != target) {
_value = target.clamp(lowerBound, upperBound);
_value = clampDouble(target, lowerBound, upperBound);
notifyListeners();
}
_status = (_direction == _AnimationDirection.forward) ?
@ -741,7 +741,7 @@ class AnimationController extends Animation<double>
assert(!isAnimating);
_simulation = simulation;
_lastElapsedDuration = Duration.zero;
_value = simulation.x(0.0).clamp(lowerBound, upperBound);
_value = clampDouble(simulation.x(0.0), lowerBound, upperBound);
final TickerFuture result = _ticker!.start();
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.forward :
@ -820,7 +820,7 @@ class AnimationController extends Animation<double>
_lastElapsedDuration = elapsed;
final double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.microsecondsPerSecond;
assert(elapsedInSeconds >= 0.0);
_value = _simulation!.x(elapsedInSeconds).clamp(lowerBound, upperBound);
_value = clampDouble(_simulation!.x(elapsedInSeconds), lowerBound, upperBound);
if (_simulation!.isDone(elapsedInSeconds)) {
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.completed :
@ -855,7 +855,7 @@ class _InterpolationSimulation extends Simulation {
@override
double x(double timeInSeconds) {
final double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0);
final double t = clampDouble(timeInSeconds / _durationInSeconds, 0.0, 1.0);
if (t == 0.0)
return _begin;
else if (t == 1.0)

View File

@ -182,7 +182,7 @@ class Interval extends Curve {
assert(end >= 0.0);
assert(end <= 1.0);
assert(end >= begin);
t = ((t - begin) / (end - begin)).clamp(0.0, 1.0);
t = clampDouble((t - begin) / (end - begin), 0.0, 1.0);
if (t == 0.0 || t == 1.0)
return t;
return curve.transform(t);

View File

@ -4,6 +4,7 @@
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart' show kMinFlingVelocity, kLongPressTimeout;
import 'package:flutter/scheduler.dart';
@ -958,7 +959,7 @@ class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with T
_moveAnimation = Tween<Offset>(
begin: Offset.zero,
end: Offset(
endX.clamp(-_kPadding, _kPadding),
clampDouble(endX, -_kPadding, _kPadding),
endY,
),
).animate(

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
@ -157,7 +158,7 @@ class _CupertinoDesktopTextSelectionControlsToolbarState extends State<_Cupertin
final MediaQueryData mediaQuery = MediaQuery.of(context);
final Offset midpointAnchor = Offset(
(widget.selectionMidpoint.dx - widget.globalEditableRegion.left).clamp(
clampDouble(widget.selectionMidpoint.dx - widget.globalEditableRegion.left,
mediaQuery.padding.left,
mediaQuery.size.width - mediaQuery.padding.right,
),

View File

@ -4,6 +4,7 @@
import 'dart:math';
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
@ -375,7 +376,7 @@ class CupertinoSliverRefreshControl extends StatefulWidget {
double refreshTriggerPullDistance,
double refreshIndicatorExtent,
) {
final double percentageComplete = (pulledExtent / refreshTriggerPullDistance).clamp(0.0, 1.0);
final double percentageComplete = clampDouble(pulledExtent / refreshTriggerPullDistance, 0.0, 1.0);
// Place the indicator at the top of the sliver that opens up. Note that we're using
// a Stack/Positioned widget because the CupertinoActivityIndicator does some internal

View File

@ -5,6 +5,7 @@
import 'dart:math' as math;
import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
@ -434,7 +435,7 @@ class _RenderCupertinoSlider extends RenderConstrainedBox {
double _currentDragValue = 0.0;
double get _discretizedCurrentDragValue {
double dragValue = _currentDragValue.clamp(0.0, 1.0);
double dragValue = clampDouble(_currentDragValue, 0.0, 1.0);
if (divisions != null)
dragValue = (dragValue * divisions!).round() / divisions!;
return dragValue;
@ -554,8 +555,8 @@ class _RenderCupertinoSlider extends RenderConstrainedBox {
config.onIncrease = _increaseAction;
config.onDecrease = _decreaseAction;
config.value = '${(value * 100).round()}%';
config.increasedValue = '${((value + _semanticActionUnit).clamp(0.0, 1.0) * 100).round()}%';
config.decreasedValue = '${((value - _semanticActionUnit).clamp(0.0, 1.0) * 100).round()}%';
config.increasedValue = '${(clampDouble(value + _semanticActionUnit, 0.0, 1.0) * 100).round()}%';
config.decreasedValue = '${(clampDouble(value - _semanticActionUnit, 0.0, 1.0) * 100).round()}%';
}
}
@ -563,11 +564,11 @@ class _RenderCupertinoSlider extends RenderConstrainedBox {
void _increaseAction() {
if (isInteractive)
onChanged!((value + _semanticActionUnit).clamp(0.0, 1.0));
onChanged!(clampDouble(value + _semanticActionUnit, 0.0, 1.0));
}
void _decreaseAction() {
if (isInteractive)
onChanged!((value - _semanticActionUnit).clamp(0.0, 1.0));
onChanged!(clampDouble(value - _semanticActionUnit, 0.0, 1.0));
}
}

View File

@ -494,7 +494,7 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
final int numOfChildren = widget.children.length;
assert(renderBox.hasSize);
assert(numOfChildren >= 2);
int index = (dx ~/ (renderBox.size.width / numOfChildren)).clamp(0, numOfChildren - 1);
int index = (dx ~/ (renderBox.size.width / numOfChildren)).clamp(0, numOfChildren - 1); // ignore_clamp_double_lint
switch (Directionality.of(context)) {
case TextDirection.ltr:

View File

@ -4,6 +4,7 @@
import 'dart:math' as math;
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
@ -91,7 +92,7 @@ class _CupertinoTextSelectionControlsToolbarState extends State<_CupertinoTextSe
// The toolbar should appear below the TextField when there is not enough
// space above the TextField to show it, assuming there's always enough
// space at the bottom in this case.
final double anchorX = (widget.selectionMidpoint.dx + widget.globalEditableRegion.left).clamp(
final double anchorX = clampDouble(widget.selectionMidpoint.dx + widget.globalEditableRegion.left,
_kArrowScreenPadding + mediaQuery.padding.left,
mediaQuery.size.width - mediaQuery.padding.right - _kArrowScreenPadding,
);

View File

@ -9,6 +9,7 @@ import 'package:meta/meta.dart';
import 'assertions.dart';
import 'constants.dart';
import 'debug.dart';
import 'math.dart' show clampDouble;
import 'object.dart';
// Examples can assume:
@ -2044,7 +2045,7 @@ class PercentProperty extends DoubleProperty {
final double? v = value;
if (v == null)
return value.toString();
return '${(v.clamp(0.0, 1.0) * 100.0).toStringAsFixed(1)}%';
return '${(clampDouble(v, 0.0, 1.0) * 100.0).toStringAsFixed(1)}%';
}
}

View File

@ -0,0 +1,23 @@
// 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.
/// Same as [num.clamp] but optimized for non-null [double].
///
/// This is faster because it avoids polymorphism, boxing, and special cases for
/// floating point numbers.
//
// See also: //dev/benchmarks/microbenchmarks/lib/foundation/clamp.dart
double clampDouble(double x, double min, double max) {
assert(min <= max && !max.isNaN && !min.isNaN);
if (x < min) {
return min;
}
if (x > max) {
return max;
}
if (x.isNaN) {
return max;
}
return x;
}

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart' show clampDouble;
import 'arena.dart';
import 'events.dart';
import 'recognizer.dart';
@ -331,7 +332,7 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
// If the device incorrectly reports a pressure outside of pressureMin
// and pressureMax, we still want this recognizer to respond normally.
if (!value.isNaN)
value = value.clamp(0.0, 1.0);
value = clampDouble(value, 0.0, 1.0);
return value;
}

View File

@ -9,6 +9,7 @@ import 'dart:math' as math show pi;
import 'dart:ui' as ui show Paint, Path, Canvas;
import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/widgets.dart';
// This package is split into multiple parts to enable a private API that is

View File

@ -161,7 +161,7 @@ class _AnimatedIconPainter extends CustomPainter {
}
canvas.scale(scale, scale);
final double clampedProgress = progress.value.clamp(0.0, 1.0);
final double clampedProgress = clampDouble(progress.value, 0.0, 1.0);
for (final _PathFrames path in paths)
path.paint(canvas, color, uiPathFactory, clampedProgress);
}

View File

@ -1263,7 +1263,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final bool isScrolledUnder = overlapsContent || (pinned && shrinkOffset > maxExtent - minExtent);
final bool isPinnedWithOpacityFade = pinned && floating && bottom != null && extraToolbarHeight == 0.0;
final double toolbarOpacity = !pinned || isPinnedWithOpacityFade
? (visibleToolbarHeight / (toolbarHeight ?? kToolbarHeight)).clamp(0.0, 1.0)
? clampDouble(visibleToolbarHeight / (toolbarHeight ?? kToolbarHeight), 0.0, 1.0)
: 1.0;
final Widget appBar = FlexibleSpaceBar.createSettings(
@ -1300,7 +1300,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
titleSpacing: titleSpacing,
shape: shape,
toolbarOpacity: toolbarOpacity,
bottomOpacity: pinned ? 1.0 : ((visibleMainHeight / _bottomHeight).clamp(0.0, 1.0)),
bottomOpacity: pinned ? 1.0 : clampDouble(visibleMainHeight / _bottomHeight, 0.0, 1.0),
toolbarHeight: toolbarHeight,
leadingWidth: leadingWidth,
backwardsCompatibility: backwardsCompatibility,

View File

@ -365,7 +365,7 @@ class _RawMaterialButtonState extends State<RawMaterialButton> with MaterialStat
right: densityAdjustment.dx,
bottom: densityAdjustment.dy,
),
).clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity);
).clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity); // ignore_clamp_double_lint
final Widget result = ConstrainedBox(

View File

@ -302,7 +302,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
final double dx = math.max(0, densityAdjustment.dx);
final EdgeInsetsGeometry padding = resolvedPadding!
.add(EdgeInsets.fromLTRB(dx, dy, dx, dy))
.clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity);
.clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity); // ignore_clamp_double_lint
// If an opaque button's background is becoming translucent while its
// elevation is changing, change the elevation first. Material implicitly

View File

@ -4,6 +4,7 @@
import 'dart:math' as math;
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
@ -1111,7 +1112,7 @@ class _RawChipState extends State<RawChip> with MaterialStateMixin, TickerProvid
final EdgeInsetsGeometry defaultLabelPadding = EdgeInsets.lerp(
const EdgeInsets.symmetric(horizontal: 8.0),
const EdgeInsets.symmetric(horizontal: 4.0),
(MediaQuery.of(context).textScaleFactor - 1.0).clamp(0.0, 1.0),
clampDouble(MediaQuery.of(context).textScaleFactor - 1.0, 0.0, 1.0),
)!;
final ThemeData theme = Theme.of(context);

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
@ -153,7 +154,7 @@ class _DesktopTextSelectionControlsToolbarState extends State<_DesktopTextSelect
final MediaQueryData mediaQuery = MediaQuery.of(context);
final Offset midpointAnchor = Offset(
(widget.selectionMidpoint.dx - widget.globalEditableRegion.left).clamp(
clampDouble(widget.selectionMidpoint.dx - widget.globalEditableRegion.left,
mediaQuery.padding.left,
mediaQuery.size.width - mediaQuery.padding.right,
),

View File

@ -4,6 +4,7 @@
import 'dart:ui';
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/widgets.dart';
import 'color_scheme.dart';
@ -1176,7 +1177,7 @@ class DialogRoute<T> extends RawDialogRoute<T> {
}
double _paddingScaleFactor(double textScaleFactor) {
final double clampedTextScaleFactor = textScaleFactor.clamp(1.0, 2.0);
final double clampedTextScaleFactor = clampDouble(textScaleFactor, 1.0, 2.0);
// The final padding scale factor is clamped between 1/3 and 1. For example,
// a non-scaled padding of 24 will produce a padding between 24 and 8.
return lerpDouble(1.0, 1.0 / 3.0, clampedTextScaleFactor - 1.0)!;

View File

@ -67,12 +67,12 @@ class _DropdownMenuPainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
final double selectedItemOffset = getSelectedItemOffset();
final Tween<double> top = Tween<double>(
begin: selectedItemOffset.clamp(0.0, math.max(size.height - _kMenuItemHeight, 0.0)),
begin: clampDouble(selectedItemOffset, 0.0, math.max(size.height - _kMenuItemHeight, 0.0)),
end: 0.0,
);
final Tween<double> bottom = Tween<double>(
begin: (top.begin! + _kMenuItemHeight).clamp(math.min(_kMenuItemHeight, size.height), size.height),
begin: clampDouble(top.begin! + _kMenuItemHeight, math.min(_kMenuItemHeight, size.height), size.height),
end: size.height,
);
@ -166,8 +166,8 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>>
if (widget.itemIndex == widget.route.selectedIndex) {
opacity = CurvedAnimation(parent: widget.route.animation!, curve: const Threshold(0.0));
} else {
final double start = (0.5 + (widget.itemIndex + 1) * unit).clamp(0.0, 1.0);
final double end = (start + 1.5 * unit).clamp(0.0, 1.0);
final double start = clampDouble(0.5 + (widget.itemIndex + 1) * unit, 0.0, 1.0);
final double end = clampDouble(start + 1.5 * unit, 0.0, 1.0);
opacity = CurvedAnimation(parent: widget.route.animation!, curve: Interval(start, end));
}
Widget child = Container(
@ -371,10 +371,10 @@ class _DropdownMenuRouteLayout<T> extends SingleChildLayoutDelegate {
final double left;
switch (textDirection!) {
case TextDirection.rtl:
left = buttonRect.right.clamp(0.0, size.width) - childSize.width;
left = clampDouble(buttonRect.right, 0.0, size.width) - childSize.width;
break;
case TextDirection.ltr:
left = buttonRect.left.clamp(0.0, size.width - childSize.width);
left = clampDouble(buttonRect.left, 0.0, size.width - childSize.width);
break;
}

View File

@ -5,6 +5,7 @@
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/widgets.dart';
import 'colors.dart';
@ -231,7 +232,7 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
// 0.0 -> Expanded
// 1.0 -> Collapsed to toolbar
final double t = (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0);
final double t = clampDouble(1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent, 0.0, 1.0);
// background
if (widget.background != null) {
@ -307,7 +308,10 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
if (widget.stretchModes.contains(StretchMode.fadeTitle) &&
constraints.maxHeight > settings.maxExtent) {
final double stretchOpacity = 1 -
(((constraints.maxHeight - settings.maxExtent) / 100).clamp(0.0, 1.0));
clampDouble(
(constraints.maxHeight - settings.maxExtent) / 100,
0.0,
1.0);
title = Opacity(
opacity: stretchOpacity,
child: title,

View File

@ -5,6 +5,7 @@
import 'dart:math' as math;
import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/widgets.dart';
/// Defines the appearance of an [InputDecorator]'s border.
@ -418,7 +419,7 @@ class OutlineInputBorder extends InputBorder {
// Currently, BorderRadius only supports circular radii.
const double cornerArcSweep = math.pi / 2.0;
final double tlCornerArcSweep = math.acos(
(1 - start / scaledRRect.tlRadiusX).clamp(0.0, 1.0),
clampDouble(1 - start / scaledRRect.tlRadiusX, 0.0, 1.0),
);
final Path path = Path()
@ -436,7 +437,7 @@ class OutlineInputBorder extends InputBorder {
} else if (start + extent < scaledRRect.width) {
final double dx = scaledRRect.width - (start + extent);
final double sweep = math.asin(
(1 - dx / scaledRRect.trRadiusX).clamp(0.0, 1.0),
clampDouble(1 - dx / scaledRRect.trRadiusX, 0.0, 1.0),
);
path.addArc(trCorner, trCornerArcStart + sweep, trCornerArcSweep - sweep);
}

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/widgets.dart';
import 'color_scheme.dart';
@ -918,7 +919,7 @@ class _ClampTextScaleFactor extends StatelessWidget {
Widget build(BuildContext context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: MediaQuery.of(context).textScaleFactor.clamp(
textScaleFactor: clampDouble(MediaQuery.of(context).textScaleFactor,
0.0,
upperLimit,
),

View File

@ -543,7 +543,7 @@ class _PopupMenu<T> extends StatelessWidget {
for (int i = 0; i < route.items.length; i += 1) {
final double start = (i + 1) * unit;
final double end = (start + 1.5 * unit).clamp(0.0, 1.0);
final double end = clampDouble(start + 1.5 * unit, 0.0, 1.0);
final CurvedAnimation opacity = CurvedAnimation(
parent: route.animation!,
curve: Interval(start, end),

View File

@ -202,7 +202,7 @@ class _LinearProgressIndicatorPainter extends CustomPainter {
}
if (value != null) {
drawBar(0.0, value!.clamp(0.0, 1.0) * size.width);
drawBar(0.0, clampDouble(value!, 0.0, 1.0) * size.width);
} else {
final double x1 = size.width * line1Tail.transform(animationValue);
final double width1 = size.width * line1Head.transform(animationValue) - x1;
@ -385,7 +385,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
? _startAngle
: _startAngle + tailValue * 3 / 2 * math.pi + rotationValue * math.pi * 2.0 + offsetValue * 0.5 * math.pi,
arcSweep = value != null
? value.clamp(0.0, 1.0) * _sweep
? clampDouble(value, 0.0, 1.0) * _sweep
: math.max(headValue * 3 / 2 * math.pi - tailValue * 3 / 2 * math.pi, _epsilon);
final Color? backgroundColor;

View File

@ -1079,7 +1079,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
}
double _discretize(double value) {
double result = value.clamp(0.0, 1.0);
double result = clampDouble(value, 0.0, 1.0);
if (isDiscrete) {
result = (result * divisions!).round() / divisions!;
}
@ -1092,7 +1092,7 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
void _startInteraction(Offset globalPosition) {
_state.showValueIndicator();
final double tapValue = _getValueFromGlobalPosition(globalPosition).clamp(0.0, 1.0);
final double tapValue = clampDouble(_getValueFromGlobalPosition(globalPosition), 0.0, 1.0);
_lastThumbSelection = sliderTheme.thumbSelector!(textDirection, values, tapValue, _thumbSize, size, 0);
if (_lastThumbSelection != null) {
@ -1613,11 +1613,11 @@ class _RenderRangeSlider extends RenderBox with RelayoutWhenSystemFontsChangeMix
}
double get _decreasedStartValue {
return (values.start - _semanticActionUnit).clamp(0.0, 1.0);
return clampDouble(values.start - _semanticActionUnit, 0.0, 1.0);
}
double get _increasedEndValue {
return (values.end + _semanticActionUnit).clamp(0.0, 1.0);
return clampDouble(values.end + _semanticActionUnit, 0.0, 1.0);
}
double get _decreasedEndValue {

View File

@ -5,6 +5,7 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/widgets.dart';
import 'debug.dart';
@ -409,7 +410,7 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS
double newValue = _dragOffset! / (containerExtent * _kDragContainerExtentPercentage);
if (_mode == _RefreshIndicatorMode.armed)
newValue = math.max(newValue, 1.0 / _kDragSizeFactorLimit);
_positionController.value = newValue.clamp(0.0, 1.0); // this triggers various rebuilds
_positionController.value = clampDouble(newValue, 0.0, 1.0); // this triggers various rebuilds
if (_mode == _RefreshIndicatorMode.drag && _valueColor.value!.alpha == 0xFF)
_mode = _RefreshIndicatorMode.armed;
}

View File

@ -993,7 +993,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
if (extendBody) {
bodyMaxHeight += bottomWidgetsHeight;
bodyMaxHeight = bodyMaxHeight.clamp(0.0, looseConstraints.maxHeight - contentTop);
bodyMaxHeight = clampDouble(bodyMaxHeight, 0.0, looseConstraints.maxHeight - contentTop);
assert(bodyMaxHeight <= math.max(0.0, looseConstraints.maxHeight - contentTop));
}
@ -2366,7 +2366,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
/// [Scaffold.floatingActionButton]. This value must not be null.
set _floatingActionButtonVisibilityValue(double newValue) {
assert(newValue != null);
_floatingActionButtonVisibilityController.value = newValue.clamp(
_floatingActionButtonVisibilityController.value = clampDouble(newValue,
_floatingActionButtonVisibilityController.lowerBound,
_floatingActionButtonVisibilityController.upperBound,
);

View File

@ -1273,7 +1273,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
}
double _discretize(double value) {
double result = value.clamp(0.0, 1.0);
double result = clampDouble(value, 0.0, 1.0);
if (isDiscrete) {
result = (result * divisions!).round() / divisions!;
}
@ -1540,12 +1540,12 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
if (semanticFormatterCallback != null) {
config.value = semanticFormatterCallback!(_state._lerp(value));
config.increasedValue = semanticFormatterCallback!(_state._lerp((value + _semanticActionUnit).clamp(0.0, 1.0)));
config.decreasedValue = semanticFormatterCallback!(_state._lerp((value - _semanticActionUnit).clamp(0.0, 1.0)));
config.increasedValue = semanticFormatterCallback!(_state._lerp(clampDouble(value + _semanticActionUnit, 0.0, 1.0)));
config.decreasedValue = semanticFormatterCallback!(_state._lerp(clampDouble(value - _semanticActionUnit, 0.0, 1.0)));
} else {
config.value = '${(value * 100).round()}%';
config.increasedValue = '${((value + _semanticActionUnit).clamp(0.0, 1.0) * 100).round()}%';
config.decreasedValue = '${((value - _semanticActionUnit).clamp(0.0, 1.0) * 100).round()}%';
config.increasedValue = '${(clampDouble(value + _semanticActionUnit, 0.0, 1.0) * 100).round()}%';
config.decreasedValue = '${(clampDouble(value - _semanticActionUnit, 0.0, 1.0) * 100).round()}%';
}
}
@ -1553,13 +1553,13 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin {
void increaseAction() {
if (isInteractive) {
onChanged!((value + _semanticActionUnit).clamp(0.0, 1.0));
onChanged!(clampDouble(value + _semanticActionUnit, 0.0, 1.0));
}
}
void decreaseAction() {
if (isInteractive) {
onChanged!((value - _semanticActionUnit).clamp(0.0, 1.0));
onChanged!(clampDouble(value - _semanticActionUnit, 0.0, 1.0));
}
}
}

View File

@ -3180,7 +3180,7 @@ class _PaddleSliderValueIndicatorPathPainter {
// factor of the value indicator.
final double neckStretchBaseline = math.max(0.0, rightBottomNeckCenterY - math.max(leftTopNeckCenter.dy, neckRightCenter.dy));
final double t = math.pow(inverseTextScale, 3.0) as double;
final double stretch = (neckStretchBaseline * t).clamp(0.0, 10.0 * neckStretchBaseline);
final double stretch = clampDouble(neckStretchBaseline * t, 0.0, 10.0 * neckStretchBaseline);
final Offset neckStretch = Offset(0.0, neckStretchBaseline - stretch);
assert(!_debuggingLabelLocation || () {

View File

@ -311,7 +311,7 @@ double _indexChangeProgress(TabController controller) {
// The controller's offset is changing because the user is dragging the
// TabBarView's PageView to the left or right.
if (!controller.indexIsChanging)
return (currentIndex - controllerValue).abs().clamp(0.0, 1.0);
return clampDouble((currentIndex - controllerValue).abs(), 0.0, 1.0);
// The TabController animation's value is changing from previousIndex to currentIndex.
return (controllerValue - currentIndex).abs() / (currentIndex - previousIndex).abs();
@ -417,8 +417,8 @@ class _IndicatorPainter extends CustomPainter {
final double index = controller.index.toDouble();
final double value = controller.animation!.value;
final bool ltr = index > value;
final int from = (ltr ? value.floor() : value.ceil()).clamp(0, maxTabIndex);
final int to = (ltr ? from + 1 : from - 1).clamp(0, maxTabIndex);
final int from = (ltr ? value.floor() : value.ceil()).clamp(0, maxTabIndex); // ignore_clamp_double_lint
final int to = (ltr ? from + 1 : from - 1).clamp(0, maxTabIndex); // ignore_clamp_double_lint
final Rect fromRect = indicatorRect(size, from);
final Rect toRect = indicatorRect(size, to);
_currentRect = Rect.lerp(fromRect, toRect, (value - from).abs());
@ -491,8 +491,8 @@ class _DragAnimation extends Animation<double> with AnimationWithParentMixin<dou
double get value {
assert(!controller.indexIsChanging);
final double controllerMaxValue = (controller.length - 1).toDouble();
final double controllerValue = controller.animation!.value.clamp(0.0, controllerMaxValue);
return (controllerValue - index.toDouble()).abs().clamp(0.0, 1.0);
final double controllerValue = clampDouble(controller.animation!.value, 0.0, controllerMaxValue);
return clampDouble((controllerValue - index.toDouble()).abs(), 0.0, 1.0);
}
}
@ -1048,7 +1048,7 @@ class _TabBarState extends State<TabBar> {
case TextDirection.ltr:
break;
}
return (tabCenter - viewportWidth / 2.0).clamp(minExtent, maxExtent);
return clampDouble(tabCenter - viewportWidth / 2.0, minExtent, maxExtent);
}
double _tabCenteredScrollOffset(int index) {
@ -1510,12 +1510,12 @@ class _TabBarViewState extends State<TabBarView> {
_controller!.index = _pageController.page!.round();
_currentIndex =_controller!.index;
}
_controller!.offset = (_pageController.page! - _controller!.index).clamp(-1.0, 1.0);
_controller!.offset = clampDouble(_pageController.page! - _controller!.index, -1.0, 1.0);
} else if (notification is ScrollEndNotification) {
_controller!.index = _pageController.page!.round();
_currentIndex = _controller!.index;
if (!_controller!.indexIsChanging)
_controller!.offset = (_pageController.page! - _controller!.index).clamp(-1.0, 1.0);
_controller!.offset = clampDouble(_pageController.page! - _controller!.index, -1.0, 1.0);
}
_warpUnderwayCount -= 1;

View File

@ -898,7 +898,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
if (widget.maxLength! > 0) {
// Show the maxLength in the counter
counterText += '/${widget.maxLength}';
final int remaining = (widget.maxLength! - currentLength).clamp(0, widget.maxLength!);
final int remaining = (widget.maxLength! - currentLength).clamp(0, widget.maxLength!); // ignore_clamp_double_lint
semanticCounterText = localizations.remainingTextFieldCharacterCount(remaining);
}

View File

@ -2686,8 +2686,8 @@ class VisualDensity with Diagnosticable {
BoxConstraints effectiveConstraints(BoxConstraints constraints) {
assert(constraints != null && constraints.debugAssertIsValid());
return constraints.copyWith(
minWidth: (constraints.minWidth + baseSizeAdjustment.dx).clamp(0.0, constraints.maxWidth),
minHeight: (constraints.minHeight + baseSizeAdjustment.dy).clamp(0.0, constraints.maxHeight),
minWidth: clampDouble(constraints.minWidth + baseSizeAdjustment.dx, 0.0, constraints.maxWidth),
minHeight: clampDouble(constraints.minHeight + baseSizeAdjustment.dy, 0.0, constraints.maxHeight),
);
}

View File

@ -206,10 +206,10 @@ class HSVColor {
if (b == null)
return a._scaleAlpha(1.0 - t);
return HSVColor.fromAHSV(
lerpDouble(a.alpha, b.alpha, t)!.clamp(0.0, 1.0),
clampDouble(lerpDouble(a.alpha, b.alpha, t)!, 0.0, 1.0),
lerpDouble(a.hue, b.hue, t)! % 360.0,
lerpDouble(a.saturation, b.saturation, t)!.clamp(0.0, 1.0),
lerpDouble(a.value, b.value, t)!.clamp(0.0, 1.0),
clampDouble(lerpDouble(a.saturation, b.saturation, t)!, 0.0, 1.0),
clampDouble(lerpDouble(a.value, b.value, t)!, 0.0, 1.0),
);
}
@ -290,7 +290,7 @@ class HSLColor {
// Saturation can exceed 1.0 with rounding errors, so clamp it.
final double saturation = lightness == 1.0
? 0.0
: ((delta / (1.0 - (2.0 * lightness - 1.0).abs())).clamp(0.0, 1.0));
: clampDouble(delta / (1.0 - (2.0 * lightness - 1.0).abs()), 0.0, 1.0);
return HSLColor.fromAHSL(alpha, hue, saturation, lightness);
}
@ -390,10 +390,10 @@ class HSLColor {
if (b == null)
return a._scaleAlpha(1.0 - t);
return HSLColor.fromAHSL(
lerpDouble(a.alpha, b.alpha, t)!.clamp(0.0, 1.0),
clampDouble(lerpDouble(a.alpha, b.alpha, t)!, 0.0, 1.0),
lerpDouble(a.hue, b.hue, t)! % 360.0,
lerpDouble(a.saturation, b.saturation, t)!.clamp(0.0, 1.0),
lerpDouble(a.lightness, b.lightness, t)!.clamp(0.0, 1.0),
clampDouble(lerpDouble(a.saturation, b.saturation, t)!, 0.0, 1.0),
clampDouble(lerpDouble(a.lightness, b.lightness, t)!, 0.0, 1.0),
);
}

View File

@ -161,12 +161,12 @@ abstract class EdgeInsetsGeometry {
/// or equal to `min`, and less than or equal to `max`.
EdgeInsetsGeometry clamp(EdgeInsetsGeometry min, EdgeInsetsGeometry max) {
return _MixedEdgeInsets.fromLRSETB(
_left.clamp(min._left, max._left),
_right.clamp(min._right, max._right),
_start.clamp(min._start, max._start),
_end.clamp(min._end, max._end),
_top.clamp(min._top, max._top),
_bottom.clamp(min._bottom, max._bottom),
clampDouble(_left, min._left, max._left),
clampDouble(_right, min._right, max._right),
clampDouble(_start, min._start, max._start),
clampDouble(_end, min._end, max._end),
clampDouble(_top, min._top, max._top),
clampDouble(_bottom, min._bottom, max._bottom),
);
}
@ -505,10 +505,10 @@ class EdgeInsets extends EdgeInsetsGeometry {
@override
EdgeInsetsGeometry clamp(EdgeInsetsGeometry min, EdgeInsetsGeometry max) {
return EdgeInsets.fromLTRB(
_left.clamp(min._left, max._left),
_top.clamp(min._top, max._top),
_right.clamp(min._right, max._right),
_bottom.clamp(min._bottom, max._bottom),
clampDouble(_left, min._left, max._left),
clampDouble(_top, min._top, max._top),
clampDouble(_right, min._right, max._right),
clampDouble(_bottom, min._bottom, max._bottom),
);
}

View File

@ -119,7 +119,7 @@ class FlutterLogoDecoration extends Decoration {
b.style,
b.margin * t,
b._position,
b._opacity * t.clamp(0.0, 1.0),
b._opacity * clampDouble(t, 0.0, 1.0),
);
}
if (b == null) {
@ -128,7 +128,7 @@ class FlutterLogoDecoration extends Decoration {
a.style,
a.margin * t,
a._position,
a._opacity * (1.0 - t).clamp(0.0, 1.0),
a._opacity * clampDouble(1.0 - t, 0.0, 1.0),
);
}
if (t == 0.0)
@ -140,7 +140,7 @@ class FlutterLogoDecoration extends Decoration {
t < 0.5 ? a.style : b.style,
EdgeInsets.lerp(a.margin, b.margin, t)!,
a._position + (b._position - a._position) * t,
(a._opacity + (b._opacity - a._opacity) * t).clamp(0.0, 1.0),
clampDouble(a._opacity + (b._opacity - a._opacity) * t, 0.0, 1.0),
);
}

View File

@ -4,6 +4,7 @@
import 'dart:math' as math;
import 'package:flutter/foundation.dart' show clampDouble;
import 'basic_types.dart';
/// Position a child box within a container box, either above or below a target
@ -64,7 +65,7 @@ Offset positionDependentBox({
if (size.width - margin * 2.0 < childSize.width) {
x = (size.width - childSize.width) / 2.0;
} else {
final double normalizedTargetX = target.dx.clamp(margin, size.width - margin);
final double normalizedTargetX = clampDouble(target.dx, margin, size.width - margin);
final double edge = margin + childSize.width / 2.0;
if (normalizedTargetX < edge) {
x = margin;

View File

@ -621,7 +621,7 @@ class TextPainter {
newWidth = maxIntrinsicWidth;
break;
}
newWidth = newWidth.clamp(minWidth, maxWidth);
newWidth = clampDouble(newWidth, minWidth, maxWidth);
if (newWidth != _applyFloatingPointHack(_paragraph!.width)) {
_paragraph!.layout(ui.ParagraphConstraints(width: newWidth));
}
@ -781,7 +781,8 @@ class TextPainter {
final double caretEnd = box.end;
final double dx = box.direction == TextDirection.rtl ? caretEnd - caretPrototype.width : caretEnd;
return Rect.fromLTRB(dx.clamp(0, _paragraph!.width), box.top, dx.clamp(0, _paragraph!.width), box.bottom);
return Rect.fromLTRB(clampDouble(dx, 0, _paragraph!.width), box.top,
clampDouble(dx, 0, _paragraph!.width), box.bottom);
}
return null;
}
@ -823,7 +824,7 @@ class TextPainter {
final TextBox box = boxes.last;
final double caretStart = box.start;
final double dx = box.direction == TextDirection.rtl ? caretStart - caretPrototype.width : caretStart;
return Rect.fromLTRB(dx.clamp(0, _paragraph!.width), box.top, dx.clamp(0, _paragraph!.width), box.bottom);
return Rect.fromLTRB(clampDouble(dx, 0, _paragraph!.width), box.top, clampDouble(dx, 0, _paragraph!.width), box.bottom);
}
return null;
}

View File

@ -967,7 +967,7 @@ class TextStyle with Diagnosticable {
fontFamily: fontFamily ?? _fontFamily,
fontFamilyFallback: fontFamilyFallback ?? this.fontFamilyFallback,
fontSize: fontSize == null ? null : fontSize! * fontSizeFactor + fontSizeDelta,
fontWeight: fontWeight == null ? null : FontWeight.values[(fontWeight!.index + fontWeightDelta).clamp(0, FontWeight.values.length - 1)],
fontWeight: fontWeight == null ? null : FontWeight.values[(fontWeight!.index + fontWeightDelta).clamp(0, FontWeight.values.length - 1)], // ignore_clamp_double_lint
fontStyle: fontStyle ?? this.fontStyle,
letterSpacing: letterSpacing == null ? null : letterSpacing! * letterSpacingFactor + letterSpacingDelta,
wordSpacing: wordSpacing == null ? null : wordSpacing! * wordSpacingFactor + wordSpacingDelta,

View File

@ -55,10 +55,10 @@ class ClampedSimulation extends Simulation {
final double dxMax;
@override
double x(double time) => simulation.x(time).clamp(xMin, xMax);
double x(double time) => clampDouble(simulation.x(time), xMin, xMax);
@override
double dx(double time) => simulation.dx(time).clamp(dxMin, dxMax);
double dx(double time) => clampDouble(simulation.dx(time), dxMin, dxMax);
@override
bool isDone(double time) => simulation.isDone(time);

View File

@ -115,14 +115,14 @@ class BoundedFrictionSimulation extends FrictionSimulation {
super.velocity,
this._minX,
this._maxX,
) : assert(position.clamp(_minX, _maxX) == position);
) : assert(clampDouble(position, _minX, _maxX) == position);
final double _minX;
final double _maxX;
@override
double x(double time) {
return super.x(time).clamp(_minX, _maxX);
return clampDouble(super.x(time), _minX, _maxX);
}
@override

View File

@ -212,10 +212,10 @@ class BoxConstraints extends Constraints {
/// as close as possible to the original constraints.
BoxConstraints enforce(BoxConstraints constraints) {
return BoxConstraints(
minWidth: minWidth.clamp(constraints.minWidth, constraints.maxWidth),
maxWidth: maxWidth.clamp(constraints.minWidth, constraints.maxWidth),
minHeight: minHeight.clamp(constraints.minHeight, constraints.maxHeight),
maxHeight: maxHeight.clamp(constraints.minHeight, constraints.maxHeight),
minWidth: clampDouble(minWidth, constraints.minWidth, constraints.maxWidth),
maxWidth: clampDouble(maxWidth, constraints.minWidth, constraints.maxWidth),
minHeight: clampDouble(minHeight, constraints.minHeight, constraints.maxHeight),
maxHeight: clampDouble(maxHeight, constraints.minHeight, constraints.maxHeight),
);
}
@ -224,10 +224,10 @@ class BoxConstraints extends Constraints {
/// box constraints.
BoxConstraints tighten({ double? width, double? height }) {
return BoxConstraints(
minWidth: width == null ? minWidth : width.clamp(minWidth, maxWidth),
maxWidth: width == null ? maxWidth : width.clamp(minWidth, maxWidth),
minHeight: height == null ? minHeight : height.clamp(minHeight, maxHeight),
maxHeight: height == null ? maxHeight : height.clamp(minHeight, maxHeight),
minWidth: width == null ? minWidth : clampDouble(width, minWidth, maxWidth),
maxWidth: width == null ? maxWidth : clampDouble(width, minWidth, maxWidth),
minHeight: height == null ? minHeight : clampDouble(height, minHeight, maxHeight),
maxHeight: height == null ? maxHeight : clampDouble(height, minHeight, maxHeight),
);
}
@ -253,14 +253,14 @@ class BoxConstraints extends Constraints {
/// possible to the given width.
double constrainWidth([ double width = double.infinity ]) {
assert(debugAssertIsValid());
return width.clamp(minWidth, maxWidth);
return clampDouble(width, minWidth, maxWidth);
}
/// Returns the height that both satisfies the constraints and is as close as
/// possible to the given height.
double constrainHeight([ double height = double.infinity ]) {
assert(debugAssertIsValid());
return height.clamp(minHeight, maxHeight);
return clampDouble(height, minHeight, maxHeight);
}
Size _debugPropagateDebugSize(Size size, Size result) {

View File

@ -1651,8 +1651,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
final Offset start = Offset(0.0, preferredLineHeight) + caretOffset + paintOffset;
return <TextSelectionPoint>[TextSelectionPoint(start, null)];
} else {
final Offset start = Offset(boxes.first.start.clamp(0, _textPainter.size.width), boxes.first.bottom) + paintOffset;
final Offset end = Offset(boxes.last.end.clamp(0, _textPainter.size.width), boxes.last.bottom) + paintOffset;
final Offset start = Offset(clampDouble(boxes.first.start, 0, _textPainter.size.width), boxes.first.bottom) + paintOffset;
final Offset end = Offset(clampDouble(boxes.last.end, 0, _textPainter.size.width), boxes.last.bottom) + paintOffset;
return <TextSelectionPoint>[
TextSelectionPoint(start, boxes.first.direction),
TextSelectionPoint(end, boxes.last.direction),
@ -2480,8 +2480,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
void _paintHandleLayers(PaintingContext context, List<TextSelectionPoint> endpoints) {
Offset startPoint = endpoints[0].point;
startPoint = Offset(
startPoint.dx.clamp(0.0, size.width),
startPoint.dy.clamp(0.0, size.height),
clampDouble(startPoint.dx, 0.0, size.width),
clampDouble(startPoint.dy, 0.0, size.height),
);
context.pushLayer(
LeaderLayer(link: startHandleLayerLink, offset: startPoint),
@ -2491,8 +2491,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
if (endpoints.length == 2) {
Offset endPoint = endpoints[1].point;
endPoint = Offset(
endPoint.dx.clamp(0.0, size.width),
endPoint.dy.clamp(0.0, size.height),
clampDouble(endPoint.dx, 0.0, size.width),
clampDouble(endPoint.dy, 0.0, size.height),
);
context.pushLayer(
LeaderLayer(link: endHandleLayerLink, offset: endPoint),

View File

@ -1337,7 +1337,7 @@ abstract class RenderSliver extends RenderObject {
final double a = constraints.scrollOffset;
final double b = constraints.scrollOffset + constraints.remainingPaintExtent;
// the clamp on the next line is to avoid floating point rounding errors
return (to.clamp(a, b) - from.clamp(a, b)).clamp(0.0, constraints.remainingPaintExtent);
return clampDouble(clampDouble(to, a, b) - clampDouble(from, a, b), 0.0, constraints.remainingPaintExtent);
}
/// Computes the portion of the region from `from` to `to` that is within
@ -1353,7 +1353,7 @@ abstract class RenderSliver extends RenderObject {
final double a = constraints.scrollOffset + constraints.cacheOrigin;
final double b = constraints.scrollOffset + constraints.remainingCacheExtent;
// the clamp on the next line is to avoid floating point rounding errors
return (to.clamp(a, b) - from.clamp(a, b)).clamp(0.0, constraints.remainingCacheExtent);
return clampDouble(clampDouble(to, a, b) - clampDouble(from, a, b), 0.0, constraints.remainingCacheExtent);
}
/// Returns the distance from the leading _visible_ edge of the sliver to the

View File

@ -570,10 +570,10 @@ class RenderSliverGrid extends RenderSliverMultiBoxAdaptor {
if (firstChild != null) {
final int oldFirstIndex = indexOf(firstChild!);
final int oldLastIndex = indexOf(lastChild!);
final int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, childCount);
final int leadingGarbage = (firstIndex - oldFirstIndex).clamp(0, childCount); // ignore_clamp_double_lint
final int trailingGarbage = targetLastIndex == null
? 0
: (oldLastIndex - targetLastIndex).clamp(0, childCount);
: (oldLastIndex - targetLastIndex).clamp(0, childCount); // ignore_clamp_double_lint
collectGarbage(leadingGarbage, trailingGarbage);
} else {
collectGarbage(0, 0);

View File

@ -364,7 +364,7 @@ abstract class RenderSliverScrollingPersistentHeader extends RenderSliverPersist
geometry = SliverGeometry(
scrollExtent: maxExtent,
paintOrigin: math.min(constraints.overlap, 0.0),
paintExtent: paintExtent.clamp(0.0, constraints.remainingPaintExtent),
paintExtent: clampDouble(paintExtent, 0.0, constraints.remainingPaintExtent),
maxPaintExtent: maxExtent + stretchOffset,
hasVisualOverflow: true, // Conservatively say we do have overflow to avoid complexity.
);
@ -381,7 +381,7 @@ abstract class RenderSliverScrollingPersistentHeader extends RenderSliverPersist
geometry = SliverGeometry(
scrollExtent: maxExtent,
paintOrigin: math.min(constraints.overlap, 0.0),
paintExtent: paintExtent.clamp(0.0, constraints.remainingPaintExtent),
paintExtent: clampDouble(paintExtent, 0.0, constraints.remainingPaintExtent),
maxPaintExtent: maxExtent,
hasVisualOverflow: true, // Conservatively say we do have overflow to avoid complexity.
);
@ -423,7 +423,7 @@ abstract class RenderSliverPinnedPersistentHeader extends RenderSliverPersistent
final bool overlapsContent = constraints.overlap > 0.0;
layoutChild(constraints.scrollOffset, maxExtent, overlapsContent: overlapsContent);
final double effectiveRemainingPaintExtent = math.max(0, constraints.remainingPaintExtent - constraints.overlap);
final double layoutExtent = (maxExtent - constraints.scrollOffset).clamp(0.0, effectiveRemainingPaintExtent);
final double layoutExtent = clampDouble(maxExtent - constraints.scrollOffset, 0.0, effectiveRemainingPaintExtent);
final double stretchOffset = stretchConfiguration != null ?
constraints.overlap.abs() :
0.0;
@ -595,8 +595,8 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste
geometry = SliverGeometry(
scrollExtent: maxExtent,
paintOrigin: math.min(constraints.overlap, 0.0),
paintExtent: paintExtent.clamp(0.0, constraints.remainingPaintExtent),
layoutExtent: layoutExtent.clamp(0.0, constraints.remainingPaintExtent),
paintExtent: clampDouble(paintExtent, 0.0, constraints.remainingPaintExtent),
layoutExtent: clampDouble(layoutExtent, 0.0, constraints.remainingPaintExtent),
maxPaintExtent: maxExtent + stretchOffset,
hasVisualOverflow: true, // Conservatively say we do have overflow to avoid complexity.
);
@ -677,7 +677,7 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste
if (delta > 0.0) // If we are trying to expand when allowFloatingExpansion is false,
delta = 0.0; // disallow the expansion. (But allow shrinking, i.e. delta < 0.0 is fine.)
}
_effectiveScrollOffset = (_effectiveScrollOffset! - delta).clamp(0.0, constraints.scrollOffset);
_effectiveScrollOffset = clampDouble(_effectiveScrollOffset! - delta, 0.0, constraints.scrollOffset);
} else {
_effectiveScrollOffset = constraints.scrollOffset;
}
@ -738,13 +738,16 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste
// A stretch header can have a bigger childExtent than maxExtent.
final double effectiveMaxExtent = math.max(childExtent, maxExtent);
targetExtent = targetExtent.clamp(
showOnScreen.minShowOnScreenExtent,
showOnScreen.maxShowOnScreenExtent,
)
// Clamp the value back to the valid range after applying additional
// constraints. Contracting is not allowed.
.clamp(childExtent, effectiveMaxExtent);
targetExtent = clampDouble(
clampDouble(
targetExtent,
showOnScreen.minShowOnScreenExtent,
showOnScreen.maxShowOnScreenExtent,
),
// Clamp the value back to the valid range after applying additional
// constraints. Contracting is not allowed.
childExtent,
effectiveMaxExtent);
// Expands the header if needed, with animation.
if (targetExtent > childExtent) {
@ -806,7 +809,7 @@ abstract class RenderSliverFloatingPinnedPersistentHeader extends RenderSliverFl
constraints.remainingPaintExtent;
final double maxExtent = this.maxExtent;
final double paintExtent = maxExtent - _effectiveScrollOffset!;
final double clampedPaintExtent = paintExtent.clamp(
final double clampedPaintExtent = clampDouble(paintExtent,
minAllowedExtent,
constraints.remainingPaintExtent,
);
@ -818,7 +821,7 @@ abstract class RenderSliverFloatingPinnedPersistentHeader extends RenderSliverFl
scrollExtent: maxExtent,
paintOrigin: math.min(constraints.overlap, 0.0),
paintExtent: clampedPaintExtent,
layoutExtent: layoutExtent.clamp(0.0, clampedPaintExtent),
layoutExtent: clampDouble(layoutExtent, 0.0, clampedPaintExtent),
maxPaintExtent: maxExtent + stretchOffset,
maxScrollObstructionExtent: minExtent,
hasVisualOverflow: true, // Conservatively say we do have overflow to avoid complexity.

View File

@ -1539,8 +1539,8 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
// to the zero scroll offset (the line between the forward slivers and the
// reverse slivers).
final double centerOffset = mainAxisExtent * anchor - correctedOffset;
final double reverseDirectionRemainingPaintExtent = centerOffset.clamp(0.0, mainAxisExtent);
final double forwardDirectionRemainingPaintExtent = (mainAxisExtent - centerOffset).clamp(0.0, mainAxisExtent);
final double reverseDirectionRemainingPaintExtent = clampDouble(centerOffset, 0.0, mainAxisExtent);
final double forwardDirectionRemainingPaintExtent = clampDouble(mainAxisExtent - centerOffset, 0.0, mainAxisExtent);
switch (cacheExtentStyle) {
case CacheExtentStyle.pixel:
@ -1553,8 +1553,8 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
final double fullCacheExtent = mainAxisExtent + 2 * _calculatedCacheExtent!;
final double centerCacheOffset = centerOffset + _calculatedCacheExtent!;
final double reverseDirectionRemainingCacheExtent = centerCacheOffset.clamp(0.0, fullCacheExtent);
final double forwardDirectionRemainingCacheExtent = (fullCacheExtent - centerCacheOffset).clamp(0.0, fullCacheExtent);
final double reverseDirectionRemainingCacheExtent = clampDouble(centerCacheOffset, 0.0, fullCacheExtent);
final double forwardDirectionRemainingCacheExtent = clampDouble(fullCacheExtent - centerCacheOffset, 0.0, fullCacheExtent);
final RenderSliver? leadingNegativeChild = childBefore(center!);
@ -1571,7 +1571,7 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
growthDirection: GrowthDirection.reverse,
advance: childBefore,
remainingCacheExtent: reverseDirectionRemainingCacheExtent,
cacheOrigin: (mainAxisExtent - centerOffset).clamp(-_calculatedCacheExtent!, 0.0),
cacheOrigin: clampDouble(mainAxisExtent - centerOffset, -_calculatedCacheExtent!, 0.0),
);
if (result != 0.0)
return -result;
@ -1589,7 +1589,7 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
growthDirection: GrowthDirection.forward,
advance: childAfter,
remainingCacheExtent: forwardDirectionRemainingCacheExtent,
cacheOrigin: centerOffset.clamp(-_calculatedCacheExtent!, 0.0),
cacheOrigin: clampDouble(centerOffset, -_calculatedCacheExtent!, 0.0),
);
}

View File

@ -391,7 +391,7 @@ class FilteringTextInputFormatter extends TextInputFormatter {
// The length added by adding the replacementString.
final int replacedLength = originalIndex <= regionStart && originalIndex < regionEnd ? 0 : replacementString.length;
// The length removed by removing the replacementRange.
final int removedLength = originalIndex.clamp(regionStart, regionEnd) - regionStart;
final int removedLength = originalIndex.clamp(regionStart, regionEnd) - regionStart; // ignore_clamp_double_lint
return replacedLength - removedLength;
}

View File

@ -881,7 +881,7 @@ class TextEditingValue {
// The length added by adding the replacementString.
final int replacedLength = originalIndex <= replacementRange.start && originalIndex < replacementRange.end ? 0 : replacementString.length;
// The length removed by removing the replacementRange.
final int removedLength = originalIndex.clamp(replacementRange.start, replacementRange.end) - replacementRange.start;
final int removedLength = originalIndex.clamp(replacementRange.start, replacementRange.end) - replacementRange.start; // ignore_clamp_double_lint
return originalIndex + replacedLength - removedLength;
}

View File

@ -541,7 +541,7 @@ class _DraggableSheetExtent {
/// or a user drag.
void updateSize(double newSize, BuildContext context) {
assert(newSize != null);
_currentSize.value = newSize.clamp(minSize, maxSize);
_currentSize.value = clampDouble(newSize, minSize, maxSize);
DraggableScrollableNotification(
minExtent: minSize,
maxExtent: maxSize,
@ -580,7 +580,7 @@ class _DraggableSheetExtent {
onSizeChanged: onSizeChanged,
// Use the possibly updated initialSize if the user hasn't dragged yet.
currentSize: ValueNotifier<double>(hasDragged
? _currentSize.value.clamp(minSize, maxSize)
? clampDouble(_currentSize.value, minSize, maxSize)
: initialSize),
hasDragged: hasDragged,
);

View File

@ -2264,7 +2264,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
? editableSize.width / 2 - rect.center.dx
// Valid additional offsets range from (rect.right - size.width)
// to (rect.left). Pick the closest one if out of range.
: 0.0.clamp(rect.right - editableSize.width, rect.left);
: clampDouble(0.0, rect.right - editableSize.width, rect.left);
unitOffset = const Offset(1, 0);
} else {
// The caret is vertically centered within the line. Expand the caret's
@ -2278,17 +2278,17 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
additionalOffset = expandedRect.height >= editableSize.height
? editableSize.height / 2 - expandedRect.center.dy
: 0.0.clamp(expandedRect.bottom - editableSize.height, expandedRect.top);
: clampDouble(0.0, expandedRect.bottom - editableSize.height, expandedRect.top);
unitOffset = const Offset(0, 1);
}
// No overscrolling when encountering tall fonts/scripts that extend past
// the ascent.
final double targetOffset = (additionalOffset + _scrollController.offset)
.clamp(
_scrollController.position.minScrollExtent,
_scrollController.position.maxScrollExtent,
);
final double targetOffset = clampDouble(
additionalOffset + _scrollController.offset,
_scrollController.position.minScrollExtent,
_scrollController.position.maxScrollExtent,
);
final double offsetDelta = _scrollController.offset - targetOffset;
return RevealedOffset(rect: rect.shift(unitOffset * offsetDelta), offset: targetOffset);

View File

@ -84,7 +84,7 @@ class IconThemeData with Diagnosticable {
final Color? color;
/// An opacity to apply to both explicit and default icon colors.
double? get opacity => _opacity?.clamp(0.0, 1.0);
double? get opacity => _opacity == null ? null : clampDouble(_opacity!, 0.0, 1.0);
final double? _opacity;
/// The default size for icons.

View File

@ -848,7 +848,7 @@ class _AnimatedPaddingState extends AnimatedWidgetBaseState<AnimatedPadding> {
return Padding(
padding: _padding!
.evaluate(animation)
.clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity),
.clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity), // ignore_clamp_double_lint
child: widget.child,
);
}

View File

@ -4,6 +4,7 @@
import 'dart:math' as math;
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/gestures.dart';
import 'package:flutter/physics.dart';
import 'package:vector_math/vector_math_64.dart' show Quad, Vector3, Matrix4;
@ -386,7 +387,7 @@ class InteractiveViewer extends StatefulWidget {
// the point.
final Vector3 l1P = point - l1;
final Vector3 l1L2 = l2 - l1;
final double fraction = (l1P.dot(l1L2) / lengthSquared).clamp(0.0, 1.0);
final double fraction = clampDouble(l1P.dot(l1L2) / lengthSquared, 0.0, 1.0);
return l1 + l1L2 * fraction;
}
@ -659,7 +660,7 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
_viewport.height / _boundaryRect.height,
),
);
final double clampedTotalScale = totalScale.clamp(
final double clampedTotalScale = clampDouble(totalScale,
widget.minScale,
widget.maxScale,
);

View File

@ -732,7 +732,7 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
double pixels, minRange, maxRange, correctionOffset;
double extra = 0.0;
if (innerPosition.pixels == innerPosition.minScrollExtent) {
pixels = _outerPosition!.pixels.clamp(
pixels = clampDouble(_outerPosition!.pixels,
_outerPosition!.minScrollExtent,
_outerPosition!.maxScrollExtent,
); // TODO(ianh): gracefully handle out-of-range outer positions
@ -799,7 +799,7 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
double unnestOffset(double value, _NestedScrollPosition source) {
if (source == _outerPosition)
return value.clamp(
return clampDouble(value,
_outerPosition!.minScrollExtent,
_outerPosition!.maxScrollExtent,
);
@ -810,7 +810,7 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
double nestOffset(double value, _NestedScrollPosition target) {
if (target == _outerPosition)
return value.clamp(
return clampDouble(value,
_outerPosition!.minScrollExtent,
_outerPosition!.maxScrollExtent,
);
@ -1212,7 +1212,7 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
// 0.0, representing the end of the overscrolled portion.
: pixels < 0.0 ? 0.0 : math.max(maxScrollExtent, pixels);
final double oldPixels = pixels;
final double newPixels = (pixels - delta).clamp(min, max);
final double newPixels = clampDouble(pixels - delta, min, max);
final double clampedDelta = newPixels - pixels;
if (clampedDelta == 0.0)
return delta;
@ -1268,7 +1268,7 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
final double max = delta < 0.0
? double.infinity
: math.max(maxScrollExtent, pixels);
final double newPixels = (pixels + delta).clamp(min, max);
final double newPixels = clampDouble(pixels + delta, min, max);
final double clampedDelta = newPixels - pixels;
if (clampedDelta == 0.0)
return delta;
@ -1494,7 +1494,7 @@ class _NestedOuterBallisticScrollActivity extends BallisticScrollActivity {
done = true;
}
} else {
value = value.clamp(metrics.minRange, metrics.maxRange);
value = clampDouble(value, metrics.minRange, metrics.maxRange);
done = true;
}
final bool result = super.applyMoveTo(value + metrics.correctionOffset);

View File

@ -255,10 +255,10 @@ class _GlowingOverscrollIndicatorState extends State<GlowingOverscrollIndicator>
final Offset position = renderer.globalToLocal(notification.dragDetails!.globalPosition);
switch (notification.metrics.axis) {
case Axis.horizontal:
controller!.pull(notification.overscroll.abs(), size.width, position.dy.clamp(0.0, size.height), size.height);
controller!.pull(notification.overscroll.abs(), size.width, clampDouble(position.dy, 0.0, size.height), size.height);
break;
case Axis.vertical:
controller!.pull(notification.overscroll.abs(), size.height, position.dx.clamp(0.0, size.width), size.width);
controller!.pull(notification.overscroll.abs(), size.height, clampDouble(position.dx, 0.0, size.width), size.width);
break;
}
}
@ -405,9 +405,9 @@ class _GlowController extends ChangeNotifier {
assert(velocity >= 0.0);
_pullRecedeTimer?.cancel();
_pullRecedeTimer = null;
velocity = velocity.clamp(_minVelocity, _maxVelocity);
velocity = clampDouble(velocity, _minVelocity, _maxVelocity);
_glowOpacityTween.begin = _state == _GlowState.idle ? 0.3 : _glowOpacity.value;
_glowOpacityTween.end = (velocity * _velocityGlowFactor).clamp(_glowOpacityTween.begin!, _maxOpacity);
_glowOpacityTween.end = clampDouble(velocity * _velocityGlowFactor, _glowOpacityTween.begin!, _maxOpacity);
_glowSizeTween.begin = _glowSize.value;
_glowSizeTween.end = math.min(0.025 + 7.5e-7 * velocity * velocity, 1.0);
_glowController.duration = Duration(milliseconds: (0.15 + velocity * 0.02).round());
@ -716,7 +716,7 @@ class _StretchingOverscrollIndicatorState extends State<StretchingOverscrollIndi
final double viewportDimension = notification.metrics.viewportDimension;
final double distanceForPull =
(notification.overscroll.abs() / viewportDimension) + _stretchController.pullDistance;
final double clampedOverscroll = distanceForPull.clamp(0, 1.0);
final double clampedOverscroll = clampDouble(distanceForPull, 0, 1.0);
_stretchController.pull(clampedOverscroll);
}
}
@ -845,7 +845,7 @@ class _StretchController extends ChangeNotifier {
/// The velocity must be positive.
void absorbImpact(double velocity) {
assert(velocity >= 0.0);
velocity = velocity.clamp(1, 10000);
velocity = clampDouble(velocity, 1, 10000);
_stretchSizeTween.begin = _stretchSize.value;
_stretchSizeTween.end = math.min(_stretchIntensity + (_flingFriction / velocity), 1.0);
_stretchController.duration = Duration(milliseconds: (velocity * 0.02).round());

View File

@ -4,7 +4,7 @@
import 'dart:math' as math;
import 'package:flutter/foundation.dart' show precisionErrorTolerance;
import 'package:flutter/foundation.dart' show precisionErrorTolerance, clampDouble;
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:flutter/rendering.dart';
@ -296,7 +296,7 @@ class PageMetrics extends FixedScrollMetrics {
/// The current page displayed in the [PageView].
double? get page {
return math.max(0.0, pixels.clamp(minScrollExtent, maxScrollExtent)) /
return math.max(0.0, clampDouble(pixels, minScrollExtent, maxScrollExtent)) /
math.max(1.0, viewportDimension * viewportFraction);
}
@ -396,7 +396,7 @@ class _PagePosition extends ScrollPositionWithSingleContext implements PageMetri
);
return !hasPixels || !hasContentDimensions
? null
: _cachedPage ?? getPageFromPixels(pixels.clamp(minScrollExtent, maxScrollExtent), viewportDimension);
: _cachedPage ?? getPageFromPixels(clampDouble(pixels, minScrollExtent, maxScrollExtent), viewportDimension);
}
@override

View File

@ -116,9 +116,9 @@ mixin ScrollMetrics {
assert(minScrollExtent <= maxScrollExtent);
return viewportDimension
// "above" overscroll value
- (minScrollExtent - pixels).clamp(0, viewportDimension)
- clampDouble(minScrollExtent - pixels, 0, viewportDimension)
// "below" overscroll value
- (pixels - maxScrollExtent).clamp(0, viewportDimension);
- clampDouble(pixels - maxScrollExtent, 0, viewportDimension);
}
/// The quantity of content conceptually "below" the viewport in the scrollable.

View File

@ -554,7 +554,7 @@ class RangeMaintainingScrollPhysics extends ScrollPhysics {
double result = super.adjustPositionForNewDimensions(oldPosition: oldPosition, newPosition: newPosition, isScrolling: isScrolling, velocity: velocity);
if (enforceBoundary) {
// ...but if they put us out of range then reinforce the boundary.
result = result.clamp(newPosition.minScrollExtent, newPosition.maxScrollExtent);
result = clampDouble(result, newPosition.minScrollExtent, newPosition.maxScrollExtent);
}
return result;
}

View File

@ -710,16 +710,16 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
double target;
switch (alignmentPolicy) {
case ScrollPositionAlignmentPolicy.explicit:
target = viewport.getOffsetToReveal(object, alignment, rect: targetRect).offset.clamp(minScrollExtent, maxScrollExtent);
target = clampDouble(viewport.getOffsetToReveal(object, alignment, rect: targetRect).offset, minScrollExtent, maxScrollExtent);
break;
case ScrollPositionAlignmentPolicy.keepVisibleAtEnd:
target = viewport.getOffsetToReveal(object, 1.0, rect: targetRect).offset.clamp(minScrollExtent, maxScrollExtent);
target = clampDouble(viewport.getOffsetToReveal(object, 1.0, rect: targetRect).offset, minScrollExtent, maxScrollExtent);
if (target < pixels) {
target = pixels;
}
break;
case ScrollPositionAlignmentPolicy.keepVisibleAtStart:
target = viewport.getOffsetToReveal(object, 0.0, rect: targetRect).offset.clamp(minScrollExtent, maxScrollExtent);
target = clampDouble(viewport.getOffsetToReveal(object, 0.0, rect: targetRect).offset, minScrollExtent, maxScrollExtent);
if (target > pixels) {
target = pixels;
}
@ -824,7 +824,7 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
assert(clamp != null);
if (clamp!)
to = to.clamp(minScrollExtent, maxScrollExtent);
to = clampDouble(to, minScrollExtent, maxScrollExtent);
return super.moveTo(to, duration: duration, curve: curve);
}

View File

@ -212,13 +212,13 @@ class ClampingScrollSimulation extends Simulation {
@override
double x(double time) {
final double t = (time / _duration).clamp(0.0, 1.0);
final double t = clampDouble(time / _duration, 0.0, 1.0);
return position + _distance * _flingDistancePenetration(t) * velocity.sign;
}
@override
double dx(double time) {
final double t = (time / _duration).clamp(0.0, 1.0);
final double t = clampDouble(time / _duration, 0.0, 1.0);
return _distance * _flingVelocityPenetration(t) * velocity.sign / _duration;
}

View File

@ -539,8 +539,11 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
// Thumb extent reflects fraction of content visible, as long as this
// isn't less than the absolute minimum size.
// _totalContentExtent >= viewportDimension, so (_totalContentExtent - _mainAxisPadding) > 0
final double fractionVisible = ((_lastMetrics!.extentInside - _mainAxisPadding) / (_totalContentExtent - _mainAxisPadding))
.clamp(0.0, 1.0);
final double fractionVisible = clampDouble(
(_lastMetrics!.extentInside - _mainAxisPadding) /
(_totalContentExtent - _mainAxisPadding),
0.0,
1.0);
final double thumbExtent = math.max(
math.min(_trackExtent, minOverscrollLength),
@ -563,11 +566,11 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
// [0.8, 1.0] to [0.0, 1.0], so 0% to 20% of overscroll will produce
// values for the thumb that range between minLength and the smallest
// possible value, minOverscrollLength.
: safeMinLength * (1.0 - fractionOverscrolled.clamp(0.0, 0.2) / 0.2);
: safeMinLength * (1.0 - clampDouble(fractionOverscrolled, 0.0, 0.2) / 0.2);
// The `thumbExtent` should be no greater than `trackSize`, otherwise
// the scrollbar may scroll towards the wrong direction.
return thumbExtent.clamp(newMinLength, _trackExtent);
return clampDouble(thumbExtent, newMinLength, _trackExtent);
}
@override
@ -611,7 +614,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
final double scrollableExtent = metrics.maxScrollExtent - metrics.minScrollExtent;
final double fractionPast = (scrollableExtent > 0)
? ((metrics.pixels - metrics.minScrollExtent) / scrollableExtent).clamp(0.0, 1.0)
? clampDouble((metrics.pixels - metrics.minScrollExtent) / scrollableExtent, 0.0, 1.0)
: 0;
return (_isReversed ? 1 - fractionPast : fractionPast) * (_trackExtent - thumbExtent);
@ -1608,7 +1611,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
newPosition = newPosition.clamp(position.minScrollExtent, position.maxScrollExtent);
newPosition = clampDouble(newPosition, position.minScrollExtent, position.maxScrollExtent);
break;
case TargetPlatform.iOS:
case TargetPlatform.android:

View File

@ -60,7 +60,7 @@ class SliverFillViewport extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _SliverFractionalPadding(
viewportFraction: padEnds ? (1 - viewportFraction).clamp(0, 1) / 2 : 0,
viewportFraction: padEnds ? clampDouble(1 - viewportFraction, 0, 1) / 2 : 0,
sliver: _SliverFillViewportRenderObjectWidget(
viewportFraction: viewportFraction,
delegate: delegate,

View File

@ -0,0 +1,16 @@
// 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 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('clampDouble', () {
expect(clampDouble(-1.0, 0.0, 1.0), equals(0.0));
expect(clampDouble(2.0, 0.0, 1.0), equals(1.0));
expect(clampDouble(double.infinity, 0.0, 1.0), equals(1.0));
expect(clampDouble(-double.infinity, 0.0, 1.0), equals(0.0));
expect(clampDouble(double.nan, 0.0, double.infinity), equals(double.infinity));
});
}

View File

@ -15,6 +15,7 @@ import 'dart:math' as math show pi;
import 'dart:ui' show lerpDouble, Offset;
import 'dart:ui' as ui show Paint, Path, Canvas;
import 'package:flutter/foundation.dart' show clampDouble;
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';