41

I have the following example (tested on an 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,
        ),
      ]
    );
  }

}

In this case the ListView acts like expected. I can scroll beyond the viewport and the ListView bounces back again (typical iOS behavior). But when I add a ScrollController to track the offset, the behavior of the scrolling changes:

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,
        ),
      ]
    );
  }
}

In this case the scrolling is not possible anymore. Why is it that when I add a ScrollController, that the scrolling is not possible anymore? Also adding physics: new BouncingScrollPhysics(), to the ListView does not help.

Thanks for any help :)

2
  • if someone have not find a solution. see this link Click here Commented Oct 6, 2019 at 8:34
  • As I see nobody answer WHY scrolling is not possible anymore if ScrollController was added? Commented Sep 7, 2023 at 11:44

9 Answers 9

44

To always have the scroll enabled on a ListView you can wrap the original scroll phisics you want with the AlwaysScrollableScrollPhysics class. More details here. If you want you can specify a parent or rely on the default.

Here is your example with the option added:

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,
          ),
        ]
    );
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

This does seem to be an acceptable answer. I tried it with a regular ScrollController and works perfect.
This does not seem to work on a desktop (macOS) app.
It is good that you suggested the way to fix the problem. Buy you didn't answer the question Why is it that when I add a ScrollController, that the scrolling is not possible anymore?
26

if you are facing this problem on Flutter Web then You should wrap listview with ScrollConfiguration. Add PointerDeviceKind.mouse in ScrollConfiguration behavior argument. 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()),
  ],
),

),

2 Comments

Works like a charm. One would think a mouse and a touch gesture are the same and debug with all kinds of wrappers.
needed for Mac desktop too
5

Just add AlwaysScrollableScrollPhysics

ListView(
        physics: const AlwaysScrollableScrollPhysics(),
        children :  [...]
}

Comments

3

**

Use container height for scrolling and also use physics: AlwaysScrollableScrollPhysics(), controller: controller,

**

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,)
                    ],
                  ),
                ),
              ),
            )
        )
      ],),
    ),
  );

Comments

2

In my case, I simply added a specified height to the parent container, like so

sizedBox(
 height:300
 child:   child: ListView.builder(
          controller: _controller,
          physics: const AlwaysScrollableScrollPhysics(),
          shrinkWrap: true,
          itemCount: news.length,
          itemBuilder: (context, index) {
            //container
          }

)

Comments

1

I think this solution is better without CustomScrollView. Just use NotificationListener to wrap the ListView.

  Widget noti = new NotificationListener(
    child:listView,
    onNotification: (ScrollNotification note){
      print(note.metrics.pixels.toInt());
    },
  );

I have test it , Bounce is effective

Comments

0

I found a solution how to track the offset with lists that have a smaller content height than the viewport. Use a NotificationListener together with a CustomScrollView in the build() method like this:

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;
  }
}

As long as there is no solution with a ScrollController only (or a "better" (more elegant)) solution, I will accept this as the answer.

Comments

0

in macOs

class MyCustomScrollBehavior extends MaterialScrollBehavior {
  // Override behavior methods and getters like dragDevices
  @override
  Set<PointerDeviceKind> get dragDevices => { 
    PointerDeviceKind.touch,
    PointerDeviceKind.mouse,
    // etc.
  };
}

// Set ScrollBehavior for an entire application.
MaterialApp(
  scrollBehavior: MyCustomScrollBehavior(),
  // ...
);

1 Comment

Are you getting the "bounce" effect with a scroll view in a macOS app with this? I tried it and the bounce still doesn't work. 😔
0

Windows Desktop.

The only way I could get this to work on ordinary Windows Desktop is

return ScrollConfiguration(
  behavior: ScrollConfiguration.of(context).copyWith(
    dragDevices: {PointerDeviceKind.touch, PointerDeviceKind.mouse},
  ),
  child: ListView(
    physics: AlwaysScrollableScrollPhysics(),
    children: [
      Padding(padding: const EdgeInsets.all(20)),
      for (int _ in Iterable.generate(20))
        GlamourText(currentState: currentState),
    ],
  ),
);

It's so annoying it's hard to find up-to-date definitive examples of Flutter Dart.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.