如何让SingleChildScrollView内部的ListTile同时支持点击和滚动功能
问题根因
你遇到的滚动冲突本质不是ListTile拦截了事件,而是嵌套的两个滚动组件(外层SingleChildScrollView + 内层ListView.builder)默认的事件消费逻辑冲突:内层ListView默认开启滚动,会优先消费滑动事件,不会把事件向上传递给外层的SingleChildScrollView,同时你给内层ListView加了shrinkWrap: true只是让它自适应内容高度,没有关闭它自身的滚动能力。
解决方案
直接给内层的ListView.builder加属性禁用自身滚动,让滑动事件全部交给外层SingleChildScrollView处理即可,ListTile的点击事件不会受任何影响:
- 添加
physics: const NeverScrollableScrollPhysics(),关闭内层ListView的滚动响应 - 保留
shrinkWrap: true让内层ListView根据子项高度自适应,不会出现高度溢出
修改后的完整代码
return Scaffold( appBar: AppBar(title: const Text('Exersizes'),), body: Column( children: [ Container( height: MediaQuery.of(context).size.height * 0.7, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'Exersizes', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold ), ), FutureBuilder( future: DBProvider.getData('exerscises'), builder: (ctx, AsyncSnapshot<List<Map<String, dynamic>>> snapshot) => snapshot.connectionState == ConnectionState.waiting ? const CircularProgressIndicator() : ListView.builder( shrinkWrap: true, // 新增这行,禁用内层ListView滚动 physics: const NeverScrollableScrollPhysics(), itemCount: snapshot.data!.length, itemBuilder: (ctx, i) => ListTile( title: Text(snapshot.data![i]['exersizeName']), subtitle: snapshot.data![i]['iscardio'] == 1 ? const Text('Type: cardio') : const Text('Type: weights'), leading: CircleAvatar( backgroundColor: Theme.of(context).primaryColor, child: FittedBox(child: Text(_weekDays[snapshot.data![i]['weekday']]!)), ), onTap: () {print(snapshot.data![i]['exersizeName']);}, ) ), ), ], ), ), ), ElevatedButton( onPressed: () => Navigator.of(context).pushNamed(FormScreen.routeName), child: const Text('Add Exersize') ) ], ), );
可选优化方案
如果你不需要外层SingleChildScrollView里的Exersizes标题跟着列表一起滚动,也可以直接移除外层的SingleChildScrollView,把标题放到ListView.builder的itemBuilder里作为第一个子项渲染,这样只有一个滚动组件,也不会出现冲突,性能更好。
内容的提问来源于stack exchange,提问作者OptimusPrime
相关产品推荐
相关产品推荐

