Flutter 滚动视图内容尺寸变更时如何停止滚动模拟
解决方案
你遇到的是Flutter滚动容器默认的边界校准逻辑:当内容尺寸动态缩小、当前滚动偏移超过新的最大滚动范围时,系统会自动触发回弹模拟动画回到合法偏移范围。可以通过以下两种方案禁用该效果:
方案1:更新内容尺寸前主动校准滚动偏移(推荐)
该方案无需修改滚动底层逻辑,适配性最高,实现步骤如下:
- 给
SingleChildScrollView绑定ScrollController获取当前滚动状态 - 修改内容尺寸前,先计算新尺寸对应的合法最大滚动偏移
- 若当前偏移超出合法范围,先通过
jumpTo无动画跳转到合法偏移位置,再更新内容尺寸
修改后的完整代码如下:
import 'package:flutter/material.dart'; class ScrollingSimulationDemo extends StatefulWidget { // 修正原代码的构造函数名错误 ScrollingSimulationDemo({Key? key}) : super(key: key); @override _ScrollingSimulationDemoState createState() => _ScrollingSimulationDemoState(); } class _ScrollingSimulationDemoState extends State<ScrollingSimulationDemo> { double width = 500; // 初始化滚动控制器 final ScrollController _scrollController = ScrollController(); @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: SingleChildScrollView( controller: _scrollController, scrollDirection: Axis.horizontal, child: Container( color: Colors.lightBlue[100], height: 350, width: width, ), ), floatingActionButton: FloatingActionButton( onPressed: () { double newWidth = 250.0; // 计算新尺寸对应的最大合法滚动偏移 double newMaxScrollExtent = newWidth - _scrollController.position.viewportDimension; // 边界处理:最大偏移不能小于0 newMaxScrollExtent = newMaxScrollExtent < 0 ? 0 : newMaxScrollExtent; // 若当前偏移超出合法范围,先无动画跳转 if (_scrollController.offset > newMaxScrollExtent) { _scrollController.jumpTo(newMaxScrollExtent); } // 再更新内容尺寸 setState(() { width = newWidth; }); }, ), ); } }
方案2:自定义滚动物理规则全局禁用回弹模拟
如果需要多处复用该无回弹的滚动效果,可以自定义ScrollPhysics,修改边界处理逻辑:
class NoBounceScrollPhysics extends ScrollPhysics { const NoBounceScrollPhysics({ScrollPhysics? parent}) : super(parent: parent); @override NoBounceScrollPhysics applyTo(ScrollPhysics? ancestor) { return NoBounceScrollPhysics(parent: buildParent(ancestor)); } @override double applyBoundaryConditions(ScrollMetrics position, double value) { // 完全禁止超出边界的滚动阻力和回弹效果 if (value < position.pixels && position.pixels <= position.minScrollExtent) { return value - position.pixels; } if (position.minScrollExtent <= position.pixels && position.pixels < value) { return value - position.pixels; } if (value < position.minScrollExtent && position.minScrollExtent < position.pixels) { return value - position.minScrollExtent; } if (position.pixels < position.maxScrollExtent && position.maxScrollExtent < value) { return value - position.maxScrollExtent; } return 0.0; } @override Simulation? createBallisticSimulation(ScrollMetrics position, double velocity) { // 内容尺寸变化时不触发任何回弹模拟 return null; } }
使用时将该物理规则赋值给SingleChildScrollView的physics属性即可:
SingleChildScrollView( physics: const NoBounceScrollPhysics(), // 其余属性不变 )
内容的提问来源于stack exchange,提问作者Ashok Kuvaraja
相关产品推荐
相关产品推荐

