Flutter:CupertinoScrollbar嵌套Padding时,如何避免列表项进入padding即消失?
问题描述
我的CupertinoScrollbar嵌套在一个顶部内边距为300的Padding组件中,期望列表项滚动穿过整个内边距区域后再消失,但目前列表项一进入该内边距区域就会消失。即使设置了clipBehavior: Clip.none问题依然存在;移除该属性的话,列表项进入内边距区域时会被裁剪,而非完全移出后消失。我需要实现类似iOS中UITableView的contentInset效果,确保列表项直到移出屏幕顶部才消失。
原始代码
class MyHomePage extends Stateful Widget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { ScrollController scrollController = ScrollController(); @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: EdgeInsets.only(top: 300), child: CupertinoScrollbar( controller: scrollController, child: CustomScrollView( clipBehavior: Clip.none, controller: scrollController, slivers: [ SliverList( delegate: SliverChildListDelegate( List.generate(words.length, (index) { return Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0), child: Card( child: ListTile( title: Text("$index. ${words[index]}"), onTap: () { setState(() { words.removeAt(index); }); }, ), ), ); }), ), ), ], ), ), ), floatingActionButton: FloatingActionButton( child: Icon( Icons.add_rounded, size: 35, color: Colors.white, ), onPressed: () { setState(() { addWords(howMany: 1); }); }, ), ); } }
解决方案
问题出在你把CupertinoScrollbar和CustomScrollView放在了外层Padding里,这会限制列表的布局范围——列表的可视区域从顶部300的位置开始,往上滚动时列表项一旦超出这个区域就会被处理,和clipBehavior的设置无关。要实现类似iOScontentInset的效果,应该直接给滚动组件设置内边距,而不是用外层Padding包裹:
方法1:给CustomScrollView设置padding
移除外层的Padding组件,给CustomScrollView添加padding属性,这样列表的内容区域会包含顶部300的内边距空间,滚动时列表项可以自然穿过该区域直到移出屏幕:
class MyHomePage extends Stateful Widget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { ScrollController scrollController = ScrollController(); @override Widget build(BuildContext context) { return Scaffold( body: CupertinoScrollbar( controller: scrollController, child: CustomScrollView( clipBehavior: Clip.none, controller: scrollController, // 直接给CustomScrollView设置顶部内边距 padding: EdgeInsets.only(top: 300), slivers: [ SliverList( delegate: SliverChildListDelegate( List.generate(words.length, (index) { return Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0), child: Card( child: ListTile( title: Text("$index. ${words[index]}"), onTap: () { setState(() { words.removeAt(index); }); }, ), ), ); }), ), ), ], ), ), floatingActionButton: FloatingActionButton( child: Icon( Icons.add_rounded, size: 35, color: Colors.white, ), onPressed: () { setState(() { addWords(howMany: 1); }); }, ), ); } }
方法2:使用SliverPadding包裹列表
如果需要更灵活的内边距控制(比如仅给列表内容加内边距,而非整个滚动视图),可以用SliverPadding包裹SliverList:
class MyHomePage extends Stateful Widget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { ScrollController scrollController = ScrollController(); @override Widget build(BuildContext context) { return Scaffold( body: CupertinoScrollbar( controller: scrollController, child: CustomScrollView( clipBehavior: Clip.none, controller: scrollController, slivers: [ // 用SliverPadding给列表添加顶部内边距 SliverPadding( padding: EdgeInsets.only(top: 300), sliver: SliverList( delegate: SliverChildListDelegate( List.generate(words.length, (index) { return Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0), child: Card( child: ListTile( title: Text("$index. ${words[index]}"), onTap: () { setState(() { words.removeAt(index); }); }, ), ), ); }), ), ), ), ], ), ), floatingActionButton: FloatingActionButton( child: Icon( Icons.add_rounded, size: 35, color: Colors.white, ), onPressed: () { setState(() { addWords(howMany: 1); }); }, ), ); } }
这两种方式都能让列表项滚动穿过顶部内边距区域,直到完全移出屏幕才消失,和iOSUITableView的contentInset效果一致。
内容的提问来源于stack exchange,提问作者sudoExclamationExclamation
相关产品推荐
相关产品推荐

