diff --git a/packages/flutter/lib/src/material/overscroll_indicator.dart b/packages/flutter/lib/src/material/overscroll_indicator.dart index 08a0b2e920..74b8d3f9f9 100644 --- a/packages/flutter/lib/src/material/overscroll_indicator.dart +++ b/packages/flutter/lib/src/material/overscroll_indicator.dart @@ -226,10 +226,10 @@ class _OverscrollIndicatorState extends State { child: new AnimatedBuilder( animation: _extentAnimation, builder: (BuildContext context, Widget child) { - if (_scrollDirection == null) // Haven't seen a scroll yet. - return child; + // We keep the same widget hierarchy here, even when we're not + // painting anything, to avoid rebuilding the children. return new CustomPaint( - foregroundPainter: new _Painter( + foregroundPainter: _scrollDirection == null ? null : new _Painter( scrollDirection: _scrollDirection, extent: _extentAnimation.value, isLeading: _scrollOffset < _minScrollOffset, diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart index 2986850827..e7603eecb4 100644 --- a/packages/flutter/lib/src/rendering/proxy_box.dart +++ b/packages/flutter/lib/src/rendering/proxy_box.dart @@ -1610,8 +1610,8 @@ class RenderCustomPaint extends RenderProxyBox { markNeedsPaint(); } if (attached) { - oldPainter._repaint?.removeListener(markNeedsPaint); - newPainter._repaint?.addListener(markNeedsPaint); + oldPainter?._repaint?.removeListener(markNeedsPaint); + newPainter?._repaint?.addListener(markNeedsPaint); } } diff --git a/packages/flutter/lib/src/widgets/clamp_overscrolls.dart b/packages/flutter/lib/src/widgets/clamp_overscrolls.dart index bab99998dc..20eb7634c7 100644 --- a/packages/flutter/lib/src/widgets/clamp_overscrolls.dart +++ b/packages/flutter/lib/src/widgets/clamp_overscrolls.dart @@ -50,14 +50,18 @@ class ClampOverscrolls extends InheritedWidget { /// /// This utility function is typically used by [Scrollable.builder] callbacks. static Widget buildViewport(BuildContext context, ScrollableState state, ViewportBuilder builder) { + // TODO(ianh): minScrollOffset and maxScrollOffset are typically determined + // by the container and content size. But we don't know those until we + // layout the viewport, which happens after build phase. We need to rethink + // this. final bool clampOverscrolls = ClampOverscrolls.of(context); final double clampedScrollOffset = clampOverscrolls ? state.scrollOffset.clamp(state.scrollBehavior.minScrollOffset, state.scrollBehavior.maxScrollOffset) : state.scrollOffset; - Widget viewport = builder(context, state, clampedScrollOffset); - if (clampOverscrolls) - viewport = new ClampOverscrolls(value: false, child: viewport); - return viewport; + Widget viewport = builder(context, state, clampedScrollOffset); + if (clampOverscrolls) + viewport = new ClampOverscrolls(value: false, child: viewport); + return viewport; } @override