Flutter:ListView不可滚动,不可弹跳
Posted
技术标签:
【中文标题】Flutter:ListView不可滚动,不可弹跳【英文标题】:Flutter: ListView not scrollable, not bouncing 【发布时间】:2018-06-13 09:29:45 【问题描述】:我有以下示例(在 iPhone X、ios 11 上测试):
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
class MyHomePage extends StatefulWidget
MyHomePage(Key key, this.title) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
@override
Widget build(BuildContext context)
return new ListView(
children: <Widget>[
new Container(
height: 40.0,
color: Colors.blue,
),
new Container(
height: 40.0,
color: Colors.red,
),
new Container(
height: 40.0,
color: Colors.green,
),
]
);
在这种情况下,ListView 的行为符合预期。我可以滚动到视口之外,ListView 会再次反弹(典型的 iOS 行为)。但是当我添加一个 ScrollController 来跟踪偏移量时,滚动的行为会发生变化:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
class MyHomePage extends StatefulWidget
MyHomePage(Key key, this.title) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
ScrollController _controller = new ScrollController();
@override
Widget build(BuildContext context)
return new ListView(
controller: _controller,
children: <Widget>[
new Container(
height: 40.0,
color: Colors.blue,
),
new Container(
height: 40.0,
color: Colors.red,
),
new Container(
height: 40.0,
color: Colors.green,
),
]
);
在这种情况下,滚动不再可能。为什么当我添加一个 ScrollController 时,滚动不再可能了?将physics: new BouncingScrollPhysics(),
添加到 ListView 也无济于事。
感谢您的帮助:)
【问题讨论】:
如果有人没有找到解决方案。看到这个链接Click here 【参考方案1】:要始终在 ListView
上启用滚动,您可以使用 AlwaysScrollableScrollPhysics
类包装您想要的原始滚动物理。更多详情here。如果您愿意,可以指定 parent
或依赖默认值。
这是添加了选项的示例:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
class MyHomePage extends StatefulWidget
MyHomePage(Key key, this.title) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
ScrollController _controller = new ScrollController();
@override
Widget build(BuildContext context)
return new ListView(
physics: const AlwaysScrollableScrollPhysics(), // new
controller: _controller,
children: <Widget>[
new Container(
height: 40.0,
color: Colors.blue,
),
new Container(
height: 40.0,
color: Colors.red,
),
new Container(
height: 40.0,
color: Colors.green,
),
]
);
【讨论】:
这似乎是一个可以接受的答案。我用普通的 ScrollController 试了一下,效果很好。【参考方案2】:使用始终允许用户滚动的AlwaysScrollableScrollPhysics
创建滚动物理。
对于具有弹跳效果的滚动只需向您的滚动条提供物理参数:BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics)
。这将使其始终可滚动,即使没有溢出的内容
const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics())
这是完整的例子
ListView(
padding: EdgeInsets.all(8.0),
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
children: _listData.map((i)
return ListTile(
title: Text("Item $i"),
);
).toList(),
);
【讨论】:
但是如果列表只有 3 - 5 个项目,这将不起作用。它只在列表的末尾反弹,而不是顶部。 @JayVDiyk 只是让shrinkWrap: true
@intraector 对我来说是相反的,shrinkWrap: true
它不会在顶部弹跳,shrinkWrap: false
可以解决问题【参考方案3】:
如果你在 Flutter Web 上遇到这个问题,那么你应该用 ScrollConfiguration 包装 listview。在 ScrollConfiguration 行为参数中添加 PointerDeviceKind.mouse。 You can see example.
ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(dragDevices:
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
,),
child: ListView(
controller: _controller,
physics: const AlwaysScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
children: <Widget>[
for (int index = 0; index < showThumbnailList.length; index++) _thumbnail(showThumbnailList[index], index)
// showThumbnailList.map((x) => _thumbnail(x) ).toList()),
],
),
),
【讨论】:
非常感谢。这解决了我的问题。【参考方案4】:只需添加AlwaysScrollableScrollPhysics
ListView(
physics: const AlwaysScrollableScrollPhysics(),
children : [...]
【讨论】:
【参考方案5】:我认为没有CustomScrollView 这个解决方案会更好。只需使用 NotificationListener 来包装 ListView。
Widget noti = new NotificationListener(
child:listView,
onNotification: (ScrollNotification note)
print(note.metrics.pixels.toInt());
,
);
我已经测试过了,弹跳有效
【讨论】:
【参考方案6】:**
使用容器高度进行滚动,也可以使用 物理:AlwaysScrollableScrollPhysics(), 控制器:控制器,
**
Container(
width: 400,
child: Drawer(
child: Stack(children: [
Container(
height: MediaQuery.of(context).size.height-80,
child: ListView(
controller: controller,
padding: EdgeInsets.zero,
physics: AlwaysScrollableScrollPhysics(),
children: [
Container(
height: 300,
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
child: DrawerHeader(
child:Stack(children: [
Center(
child: Column(
children: [
nullCatcher(image) == "" ? Image.asset("assets/images/doctor.png",height: 90,width: 90,) : Image.network(
"$image",
height: 90,
width: 90,
),
SizedBox(width: 30,),
Text("$name",style: TextStyle(color: Colors.grey[700],fontWeight: FontWeight.bold,fontSize: 25),),
Text("$specialty",style: TextStyle(color: Colors.grey[600]),),
],
),
),
Positioned(
right: 0,bottom: 10,
child: Text("Version: 1.0.0",style: TextStyle(color: Colors.orange),))
],),
),
),
ListTile(
contentPadding: EdgeInsets.zero,
title: Container(
height: 70,
padding: EdgeInsets.symmetric(horizontal: 30),
decoration: drawerListDecoration,
child: Row(
children: [
Container(
height: 35,width: 35,
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: BorderRadius.circular(100)
),
child: Icon(Icons.attach_file,color: Colors.white,size: 20,)),
SizedBox(width: 20,),
Text('Create Appointment'),
],
)),
onTap: ()
Navigator.pushReplacement(context, MaterialPageRoute(builder: (__)=>CreateAppointment()));
// Update the state of the app.
// ...
,
),
ListTile(
contentPadding: EdgeInsets.zero,
title: Container(
height: 70,
padding: EdgeInsets.symmetric(horizontal: 30),
decoration: drawerListDecoration,
child: Row(
children: [
Container(
height: 35,width: 35,
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: BorderRadius.circular(100)
),
child: Icon(Icons.attach_file,color: Colors.white,size: 20,)),
SizedBox(width: 20,),
Text('Appointment / Prescription List'),
],
)),
onTap: ()
Navigator.pushReplacement(context, MaterialPageRoute(builder: (__)=>AppointmentList()));
// Navigator.pop(context);
,
),
Container(
height: 70,
padding: EdgeInsets.symmetric(horizontal: 30 ),
color: Colors.grey[200],
child: Row(
children: [
Container(
height: 35,width: 35,
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: BorderRadius.circular(100)
),
child: Icon(Icons.attach_file,color: Colors.white,size: 20,)),
SizedBox(width: 20,),
Text("Clinical Options:",style: TextStyle(fontWeight: FontWeight.bold,color: Colors.grey[600]),),
],
),
),
ListTile(
contentPadding: EdgeInsets.zero,
title: Container(
height: childHeight,
padding: EdgeInsets.only(left: childPaddeing),
// decoration: drawerListDecoration,
child: Row(
children: [
lineDesign(),
SizedBox(width: 20,),
Text('Chief Complain'),
],
)),
onTap: ()
Navigator.pushReplacement(context, MaterialPageRoute(builder: (__)=>AppointmentList()));
// Navigator.pop(context);
,
),
ListTile(
contentPadding: EdgeInsets.zero,
title: Container(
height: 50,
padding: EdgeInsets.symmetric(horizontal: 45),
decoration: drawerListDecoration,
child: Row(
children: [
lineDesign(),
SizedBox(width: 20,),
Text('On Examination'),
],
)),
onTap: ()
Navigator.pushReplacement(context, MaterialPageRoute(builder: (__)=>AppointmentList()));
// Navigator.pop(context);
,
),
ListTile(
contentPadding: EdgeInsets.zero,
title: Container(
height: 70,
padding: EdgeInsets.symmetric(horizontal: 30),
decoration: drawerListDecoration,
child: Row(
children: [
Container(
height: 35,width: 35,
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: BorderRadius.circular(100)
),
child: Icon(Icons.attach_file,color: Colors.white,size: 20,)),
SizedBox(width: 20,),
Text('Examination Category'),
],
)),
onTap: ()
Navigator.pushReplacement(context, MaterialPageRoute(builder: (__)=>AppointmentList()));
// Navigator.pop(context);
,
),
ListTile(
contentPadding: EdgeInsets.zero,
title: Container(
height: 70,
padding: EdgeInsets.symmetric(horizontal: 30),
decoration: drawerListDecoration,
child: Row(
children: [
Container(
height: 35,width: 35,
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: BorderRadius.circular(100)
),
child: Icon(Icons.attach_file,color: Colors.white,size: 20,)),
SizedBox(width: 20,),
Text('Diagnosis'),
],
)),
onTap: ()
Navigator.pushReplacement(context, MaterialPageRoute(builder: (__)=>AppointmentList()));
// Navigator.pop(context);
,
),
ListTile(
contentPadding: EdgeInsets.zero,
title: Container(
height: 70,
padding: EdgeInsets.symmetric(horizontal: 30),
decoration: drawerListDecoration,
child: Row(
children: [
Container(
height: 35,width: 35,
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: BorderRadius.circular(100)
),
child: Icon(Icons.attach_file,color: Colors.white,size: 20,)),
SizedBox(width: 20,),
Text('Investigations'),
],
)),
onTap: ()
Navigator.pushReplacement(context, MaterialPageRoute(builder: (__)=>AppointmentList()));
// Navigator.pop(context);
,
),
],
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: ButtonTheme(
child: RaisedButton(
color: Colors.red[900],
onPressed: ()
if(blocState is LogoutInLoading)else logoutAlert(blocContext);
,
child: Container(
height: 70,
child:blocState is LogoutInLoading ? Container( height: 20,width: 20,margin: EdgeInsets.symmetric(vertical: 25), child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.white),),) : Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Sign Out",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 25),),
SizedBox(width: 20,),
Icon(Icons.logout,color: Colors.white,)
],
),
),
),
)
)
],),
),
);
【讨论】:
【参考方案7】:我找到了一个解决方案,如何使用内容高度小于视口的列表来跟踪偏移量。在build()
方法中使用NotificationListener
和CustomScrollView
,如下所示:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
class MyHomePage extends StatefulWidget
MyHomePage(Key key, this.title) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
ScrollController _controller = new ScrollController();
@override
Widget build(BuildContext context)
return new NotificationListener(
onNotification: _handleScrollPosition,
child: new CustomScrollView(
slivers: [
new SliverList(
delegate: new SliverChildListDelegate([
new Container(
height: 40.0,
color: Colors.blue,
),
new Container(
height: 40.0,
color: Colors.red,
),
new Container(
height: 40.0,
color: Colors.green,
),
])
)
]
)
);
bool _handleScrollPosition(ScrollNotification notification)
print(notification.metrics.pixels);
return true;
只要没有仅ScrollController
(或“更好”(更优雅))的解决方案,我将接受此作为答案。
【讨论】:
以上是关于Flutter:ListView不可滚动,不可弹跳的主要内容,如果未能解决你的问题,请参考以下文章
一个 ListView 在 AnimatedCrossFade 中不可滚动
Flutter Parallax Scroll 中的按钮不可点击