Flutter中ListWheelScrollView横竖屏切换后位置异常问题
解决ListWheelScrollView返回后页码偏移问题
问题根源
- 横竖屏切换触发LayoutBuilder重新计算约束,
ListWheelScrollView的itemExtent依赖屏幕宽度,切换后尺寸变化导致滚动位置错乱 RotatedBox的旋转改变了布局坐标系,与ListWheelScrollView的滚动逻辑产生冲突- 原代码使用无状态组件,无法持久化滚动位置,页面返回重建后丢失之前的选中状态
修复方案
将组件改为有状态组件,通过ScrollController控制滚动位置,持久化当前选中索引,返回竖屏后手动恢复滚动位置:
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main(List<String> args) { runApp(const _Test()); } class _Test extends StatefulWidget { const _Test({super.key}); @override State<_Test> createState() => _TestState(); } class _TestState extends State<_Test> { final ScrollController _scrollController = ScrollController(); int _currentIndex = 0; @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(), body: LayoutBuilder( builder: (context, constraints) { final itemExtent = constraints.maxWidth; return RotatedBox( quarterTurns: -1, child: ListWheelScrollView.useDelegate( controller: _scrollController, itemExtent: itemExtent, physics: const PageScrollPhysics(), perspective: 0.002, onSelectedItemChanged: (index) { setState(() { _currentIndex = index % 8; // 适配循环代理的循环特性 }); }, childDelegate: ListWheelChildLoopingListDelegate( children: List.generate(8, (index) { return RotatedBox( quarterTurns: 1, child: GestureDetector( child: Container( alignment: Alignment.center, color: Colors.primaries[index].shade200, child: Text('<- Push $index ->'), ), onTap: () async { await SystemChrome.setPreferredOrientations([ DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight, ]); await Navigator.push(context, MaterialPageRoute(builder: (context) { return Scaffold( appBar: AppBar(), body: Center( child: FilledButton( child: Text('Pop $_currentIndex'), onPressed: () => Navigator.pop(context), ), ), ); })); await SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); // 等待布局完成后恢复滚动位置,避免尺寸未稳定导致偏差 WidgetsBinding.instance.addPostFrameCallback((_) { _scrollController.animateTo( _currentIndex * itemExtent, duration: const Duration(milliseconds: 100), curve: Curves.easeInOut, ); }); }, ), ); }), ), ), ); }, ), ), ); } }
关键修复点
- 用
StatefulWidget持久化_currentIndex和ScrollController,保存当前选中的页码 - 通过
onSelectedItemChanged监听滚动变化,实时更新当前索引(适配循环代理做取模处理) - 返回竖屏后,通过
addPostFrameCallback等待布局完成,再调用animateTo恢复到之前的索引位置 - 确保
itemExtent在布局重建时正确获取当前屏幕宽度,避免尺寸不匹配导致的滚动偏差
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

