phase3: util_web + shop_scroll (custom scroll) null-safe; global Key? param fix

This commit is contained in:
2026-07-24 20:24:57 +08:00
parent f831fd1405
commit e2f815e141
133 changed files with 164 additions and 163 deletions

View File

@@ -21,7 +21,7 @@ class DoubleBackToCloseApp extends StatefulWidget {
/// Creates a widget that allows the user to close the app by double tapping
/// the back-button.
const DoubleBackToCloseApp({
Key key,
Key? key,
@required this.snackBar,
@required this.child,
}) : assert(snackBar != null),

View File

@@ -38,7 +38,7 @@ class ShopScrollController extends ScrollController {
final bool keepScrollOffset;
/// [toString]输出中使用的标签。 旨在帮助在调试输出中标识滚动控制器实例。
final String debugLabel;
final String? debugLabel;
/// The currently attached positions. 当前附加的 [positions]。
/// 这不应直接突变。 可以使用[attach]和[detach]添加和删除[ScrollPosition]对象。
@@ -61,7 +61,7 @@ class ShopScrollController extends ScrollController {
'ScrollController not attached to any scroll views.');
assert(_positions.length == 1,
'ScrollController attached to multiple scroll views.');
return _positions.single;
return _positions.single as ShopScrollPosition;
}
/// 可滚动小部件的当前滚动偏移量
@@ -83,12 +83,12 @@ class ShopScrollController extends ScrollController {
/// 持续时间不能为零。 要在没有动画的情况下跳至特定值,请使用[jumpTo]。
Future<void> animateTo(
double offset, {
@required Duration duration,
@required Curve curve,
required Duration duration,
required Curve curve,
}) {
assert(_positions.isNotEmpty,
'ScrollController not attached to any scroll views.');
final List<Future<void>> animations = List<Future<void>>(_positions.length);
final List<Future<void>> animations = List<Future<void>>.filled(_positions.length, Future<void>.value());
for (int i = 0; i < _positions.length; i += 1)
animations[i] =
_positions[i].animateTo(offset, duration: duration, curve: curve);
@@ -149,7 +149,7 @@ class ShopScrollController extends ScrollController {
/// 则它将是前一个实例。 当环境已更改并且[Scrollable]需要重新创建[ScrollPosition]
/// 对象时,将使用此方法。 第一次创建[ScrollPosition]时为null。
ScrollPosition createScrollPosition(ScrollPhysics physics,
ScrollContext context, ScrollPosition oldPosition) {
ScrollContext context, ScrollPosition? oldPosition) {
return ShopScrollPosition(
coordinator: coordinator,
physics: physics,
@@ -175,7 +175,7 @@ class ShopScrollController extends ScrollController {
@mustCallSuper
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
if (debugLabel != null) description.add(debugLabel);
if (debugLabel != null) description.add(debugLabel!);
if (initialScrollOffset != 0.0)
description.add(
'initialScrollOffset: ${initialScrollOffset.toStringAsFixed(1)}, ');

View File

@@ -12,15 +12,15 @@ class ShopScrollCoordinator {
/// 页面主 CustomScrollView 控制
final String pageLabel = "page";
ShopScrollController _pageScrollController;
double Function() pinnedHeaderSliverHeightBuilder;
late ShopScrollController _pageScrollController;
double Function()? pinnedHeaderSliverHeightBuilder;
ShopScrollPosition get _pageScrollPosition => _pageScrollController.position;
ScrollDragController scrollDragController;
late ScrollDragController scrollDragController;
/// 主页面滑动部件默认位置
double _pageInitialOffset;
late double _pageInitialOffset;
/// 获取主页面滑动控制器
ShopScrollController pageScrollController([double initialOffset = 0.0]) {
@@ -32,26 +32,26 @@ class ShopScrollCoordinator {
}
/// 创建并获取一个子滑动控制器
ShopScrollController newChildScrollController([String debugLabel]) =>
ShopScrollController newChildScrollController([String? debugLabel]) =>
ShopScrollController(this, debugLabel: debugLabel);
/// 子部件滑动数据协调
/// [userScrollDirection]用户滑动方向
/// [position]被滑动的子部件的位置信息
void applyUserOffset(double delta,
[ScrollDirection userScrollDirection, ShopScrollPosition position]) {
[ScrollDirection? userScrollDirection, ShopScrollPosition? position]) {
if (userScrollDirection == ScrollDirection.reverse) {
updateUserScrollDirection(_pageScrollPosition, userScrollDirection);
updateUserScrollDirection(_pageScrollPosition, userScrollDirection!);
final innerDelta = _pageScrollPosition.applyClampedDragUpdate(delta);
if (innerDelta != 0.0) {
updateUserScrollDirection(position, userScrollDirection);
updateUserScrollDirection(position!, userScrollDirection);
position.applyFullDragUpdate(innerDelta);
}
} else {
updateUserScrollDirection(position, userScrollDirection);
updateUserScrollDirection(position!, userScrollDirection!);
final outerDelta = position.applyClampedDragUpdate(delta);
if (outerDelta != 0.0) {
updateUserScrollDirection(_pageScrollPosition, userScrollDirection);
updateUserScrollDirection(_pageScrollPosition, userScrollDirection!);
_pageScrollPosition.applyFullDragUpdate(outerDelta);
}
}
@@ -60,7 +60,7 @@ class ShopScrollCoordinator {
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent,
ShopScrollPosition position) {
if (pinnedHeaderSliverHeightBuilder != null) {
maxScrollExtent = maxScrollExtent - pinnedHeaderSliverHeightBuilder();
maxScrollExtent = maxScrollExtent - pinnedHeaderSliverHeightBuilder!();
maxScrollExtent = math.max(0.0, maxScrollExtent);
}
return position.applyContentDimensions(

View File

@@ -11,17 +11,17 @@ import 'shop_scroll_coordinator.dart';
class ShopScrollPosition extends ScrollPosition
implements ScrollActivityDelegate {
final ShopScrollCoordinator coordinator; // 协调器
ScrollDragController _currentDrag;
ScrollDragController? _currentDrag;
double _heldPreviousVelocity = 0.0;
ShopScrollPosition(
{@required ScrollPhysics physics,
@required ScrollContext context,
{required ScrollPhysics physics,
required ScrollContext context,
double initialPixels = 0.0,
bool keepScrollOffset = true,
ScrollPosition oldPosition,
String debugLabel,
@required this.coordinator})
ScrollPosition? oldPosition,
String? debugLabel,
required this.coordinator})
: super(
physics: physics,
context: context,
@@ -40,7 +40,7 @@ class ShopScrollPosition extends ScrollPosition
@override
double setPixels(double newPixels) {
assert(activity.isScrolling);
assert(activity!.isScrolling);
return super.setPixels(newPixels);
}
@@ -51,13 +51,13 @@ class ShopScrollPosition extends ScrollPosition
goIdle();
return;
}
activity.updateDelegate(this);
activity!.updateDelegate(this);
final ShopScrollPosition typedOther = other as ShopScrollPosition;
_userScrollDirection = typedOther._userScrollDirection;
assert(_currentDrag == null);
if (typedOther._currentDrag != null) {
_currentDrag = typedOther._currentDrag;
_currentDrag.updateDelegate(this);
_currentDrag!.updateDelegate(this);
typedOther._currentDrag = null;
}
}
@@ -131,14 +131,14 @@ class ShopScrollPosition extends ScrollPosition
}
@override
void beginActivity(ScrollActivity newActivity) {
void beginActivity(ScrollActivity? newActivity) {
_heldPreviousVelocity = 0.0;
if (newActivity == null) return;
assert(newActivity.delegate == this);
super.beginActivity(newActivity);
_currentDrag?.dispose();
_currentDrag = null;
if (!activity.isScrolling) updateUserScrollDirection(ScrollDirection.idle);
if (!activity!.isScrolling) updateUserScrollDirection(ScrollDirection.idle);
}
/// 将[用户滚动方向]设置为给定值。
@@ -154,7 +154,7 @@ class ShopScrollPosition extends ScrollPosition
@override
ScrollHoldController hold(VoidCallback holdCancelCallback) {
final double previousVelocity = activity.velocity;
final double previousVelocity = activity!.velocity;
final HoldScrollActivity holdActivity =
HoldScrollActivity(delegate: this, onHoldCanceled: holdCancelCallback);
beginActivity(holdActivity);
@@ -195,10 +195,10 @@ class ShopScrollPosition extends ScrollPosition
if (coordinator.pageExpand == PageExpandState.Expanding) return;
}
assert(pixels != null);
final Simulation simulation =
final Simulation? simulation =
physics.createBallisticSimulation(this, velocity);
if (simulation != null) {
beginActivity(BallisticScrollActivity(this, simulation, context.vsync));
beginActivity(BallisticScrollActivity(this, simulation, context.vsync, false));
} else {
goIdle();
}
@@ -220,8 +220,8 @@ class ShopScrollPosition extends ScrollPosition
@override
Future<void> animateTo(
double to, {
@required Duration duration,
@required Curve curve,
required Duration duration,
required Curve curve,
}) {
if (nearEqual(to, pixels, physics.tolerance.distance)) {
// 跳过动画,直接移到我们已经靠近的位置。