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() 方法中使用NotificationListenerCustomScrollView,如下所示:

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 中的按钮不可点击

可滚动的 Activity,不可滚动的 ListView - Android

Nativescript Listview 不可滚动

ScrollView 内不可滚动的 ListView

可滚动父版面内的不可滚动ListView