user3686123
user3686123

Reputation: 285

How to achieve ListView smooth scrolling in Flutter

I have created a list view with network images and when I am trying to scroll the listview it's scrolling is not smooths it feels like jerking. For caching I have used cached_network_image: any, this library itself working fine but listview is not getting scrolled smooth.

I know we can achieve this with Future widget but dont know how to return cached image with future.

import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';

void main() {
   runApp(
      MaterialApp(
        title: 'List view with network images',
        home: ListViewController(),

     )
  );
}


class ListViewController extends StatelessWidget {

var imagesArray = [
  "http://iastro.shangcarts.com/pub/media/notice/File-1550484786.jpeg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1550218043.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1550217808.jpeg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1550111913.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1550108862.jpeg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1550024618.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1550022739.jpeg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1549935759.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1549935073.jpeg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1549850545.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1549849978.jpeg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1549008412.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1548985261.jpeg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1548821873.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1548731040.jpeg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1548641672.jpeg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1548410089.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547774905.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547701178.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547625318.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547624883.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547619153.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547606341.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547606200.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547603338.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547602464.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547454842.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547192524.jpg",
  "http://iastro.shangcarts.com/pub/media/notice/File-1547191374.jpg"
];




Widget _imageCell(String imageUrl) {

return ListTile(
  leading: CachedNetworkImage(imageUrl: imageUrl, placeholder: CircularProgressIndicator(), errorWidget: Icon(Icons.error),),
);

}


@override
Widget build(BuildContext context) {
// TODO: implement build
return Material(
  child: ListView.separated(
      itemBuilder: (BuildContext context, int index) {
        return _imageCell(imagesArray[index]);
      },
      separatorBuilder: (context, index) => Divider(
        color: Colors.black,
      ),
      itemCount: imagesArray.length),
);
}



}

Edit: After changing build configuration to Release still same, jerking.

Upvotes: 14

Views: 27360

Answers (4)

Rohan
Rohan

Reputation: 678

I was also looking at it for a month and after a lot of research I found a single line code that makes a listview or any list a buttery smooth scrolling

Just use physics property as physics: BouncingScrollPhysics(), and feel the difference

also if you want it for a Column you can use Singlechildscrollview and in that, you can use the physics property.

Upvotes: 5

TruongSinh
TruongSinh

Reputation: 4866

2 things

  • consider using FadeInImage.memoryNetwork instead of cached_network_image, and/or
  • consider using ListView(children: List<Widget> ) instead of ListView.separated(itemBuilder: )

FadeInImage.memoryNetwork

Ref https://flutter.dev/docs/cookbook/images/fading-in-images

When running your example code, I can't help to notice that cached_network_image doing some image rescaling/sampling affecting main UI thread, it's likely that this package is doing computational-heavy task on main thread. Using the official cooking book yields better result for me (Android emulator), full sample code (you may want to change kTransparentImage to some other loading icon)

import 'package:flutter/material.dart';
import 'package:transparent_image/transparent_image.dart';

void main() {
  runApp(MaterialApp(
    title: 'List view with network images',
    home: ListViewController(),
  ));
}

class ListViewController extends StatelessWidget {
  var imagesArray = [
    "http://iastro.shangcarts.com/pub/media/notice/File-1550484786.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550218043.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550217808.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550111913.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550108862.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550024618.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550022739.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549935759.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549935073.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549850545.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549849978.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549008412.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548985261.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548821873.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548731040.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548641672.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548410089.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547774905.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547701178.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547625318.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547624883.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547619153.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547606341.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547606200.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547603338.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547602464.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547454842.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547192524.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547191374.jpg"
  ];

  Widget _imageCell(String imageUrl) {
    return ListTile(
        leading: FadeInImage.memoryNetwork(
      placeholder: kTransparentImage,
      image: imageUrl,
    ));
  }

  @override
  Widget build(BuildContext context) {
// TODO: implement build
    return Material(
      child: 
      ListView.separated(
          itemBuilder: (BuildContext context, int index) {
            return _imageCell(imagesArray[index]);
          },
          separatorBuilder: (context, index) => Divider(
                color: Colors.black,
              ),
          itemCount: imagesArray.length),
    );
  }
}

ListView(children: List<Widget> )

Ref https://docs.flutter.io/flutter/widgets/ListView-class.html

Secondly, if you know in advance that you are going to have this finite not-so-long list, maybe you want to use ListView(children: List<Widget> ) instead of ListView.separated(itemBuilder: ), because itemBuilder will invoke/call the functions more often, and right now images are cached (either by FadeInImage.memoryNetwork or cached_network_image) for full content only, not for thumbnail, @user1462442 mention source image size, and I agree with that assessment. What we can do is to reduce the invocation to a number as low as possible.

E.g code:

import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';

void main() {
  runApp(MaterialApp(
    title: 'List view with network images',
    home: ListViewController(),
  ));
}

class ListViewController extends StatelessWidget {
  var imagesArray = [
    "http://iastro.shangcarts.com/pub/media/notice/File-1550484786.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550218043.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550217808.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550111913.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550108862.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550024618.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1550022739.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549935759.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549935073.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549850545.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549849978.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1549008412.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548985261.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548821873.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548731040.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548641672.jpeg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1548410089.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547774905.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547701178.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547625318.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547624883.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547619153.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547606341.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547606200.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547603338.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547602464.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547454842.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547192524.jpg",
    "http://iastro.shangcarts.com/pub/media/notice/File-1547191374.jpg"
  ];

  Widget _imageCell(String imageUrl) {
    return ListTile(
      leading: CachedNetworkImage(
        imageUrl: imageUrl,
        placeholder: CircularProgressIndicator(),
        errorWidget: Icon(Icons.error),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
// TODO: implement build
    return Material(
      child: ListView(
        children: imagesArray.map((imageUrl) => _imageCell(imageUrl)).toList(),
      ),
    );
  }
}

As said, you can apply both of these recommendations as well.

Upvotes: 18

0xCCY
0xCCY

Reputation: 555

Try adding this to your listview:

 physics: const AlwaysScrollableScrollPhysics(),

If you are testing on an emulator, I suggest to build the release APK, install the APK on your Android phone and check to see if it is still jerky. Emulator are resource hogging, so it might be a cause.

Lastly, you can try to reduce the image numbers, image types or size to see if it is still laggy.

Else report this issue at github, so that the flutter team is aware.

Upvotes: 1

user1462442
user1462442

Reputation: 8202

I do not think there is a good solution right now other than reduce the source image size. I took a look at your image list and some the images are quite huge.

https://github.com/renefloor/flutter_cached_network_image/issues/90

https://github.com/pejalo/flutter_image_performance

https://github.com/flutter/flutter/issues/25469

https://github.com/flutter/flutter/issues/27625#issuecomment-461677587

https://github.com/flutter/flutter/issues/2848

https://github.com/flutter/flutter/issues/26194

Flutter downscaling is quite slow. Flutter team have to optimize it for performance.

Another thing, your code does not run on flutter stable

Flutter 1.0.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 5391447fae (3 months ago) • 2018-11-29 19:41:26 -0800
Engine • revision 7375a0f414
Tools • Dart 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)

Compiler message:
lib/main.dart:56:64: Error: The argument type '#lib1::CircularProgressIndicator'
can't be assigned to the parameter type '(#lib2::BuildContext, dart.core::String)
→ #lib2::Widget'.
Try changing the type of the parameter, or casting the argument to
'(#lib2::BuildContext, dart.core::String) → #lib2::Widget'.
  leading: CachedNetworkImage(imageUrl: imageUrl, placeholder:
  CircularProgressIndicator(), errorWidget: Icon(Icons.error),),
                                                               ^
lib/main.dart:56:106: Error: The argument type '#lib1::Icon' can't be assigned to
the parameter type '(#lib2::BuildContext, dart.core::String, dart.core::Exception)
→ #lib2::Widget'.
Try changing the type of the parameter, or casting the argument to
'(#lib2::BuildContext, dart.core::String, dart.core::Exception) → #lib2::Widget'.
  leading: CachedNetworkImage(imageUrl: imageUrl, placeholder:
  CircularProgressIndicator(), errorWidget: Icon(Icons.error),),
                                                                                                         ^
Compiler failed on /Users/blah/stackoverflow/issue_54786567/lib/main.dart
Error launching application on iPhone XR.


flutter run
Launching lib/main.dart on iPhone XR in debug mode...
Starting Xcode build...                                          
 ├─Assembling Flutter resources...                    3.9s

 └─Compiling, linking and signing...                  3.4s

Xcode build done.                                            9.1s
 5.8s
Syncing files to device iPhone XR...                             
flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
flutter: The following assertion was thrown during performLayout():
flutter: BoxConstraints forces an infinite height.
flutter: These invalid constraints were provided to RenderSemanticsAnnotations's layout() function by the
flutter: following function, which probably computed the invalid constraints in question:
flutter:   RenderConstrainedBox.performLayout (package:flutter/src/rendering/proxy_box.dart:258:13)
flutter: The offending constraints were:
flutter:   BoxConstraints(w=382.0, h=Infinity)
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0      BoxConstraints.debugAssertIsValid.<anonymous closure>.throwError (package:flutter/src/rendering/box.dart:504:9)
flutter: #1      BoxConstraints.debugAssertIsValid.<anonymous closure> (package:flutter/src/rendering/box.dart:547:21)
flutter: #2      BoxConstraints.debugAssertIsValid (package:flutter/src/rendering/box.dart:551:6)
flutter: #3      RenderObject.layout (package:flutter/src/rendering/object.dart:1549:24)
flutter: #4      RenderConstrainedBox.performLayout (package:flutter/src/rendering/proxy_box.dart:258:13)
flutter: #5      RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #6      _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:13)
flutter: #7      RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #8      RenderStack.performLayout (package:flutter/src/rendering/stack.dart:510:15)
flutter: #9      RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #10     _RenderListTile._layoutBox (package:flutter/src/material/list_tile.dart:892:9)
flutter: #11     _RenderListTile.performLayout (package:flutter/src/material/list_tile.dart:913:30)
flutter: #12     RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #13     RenderPadding.performLayout (package:flutter/src/rendering/shifted_box.dart:199:11)
flutter: #14     RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #15     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:13)
flutter: #16     RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #17     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:13)
flutter: #18     RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #19     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:13)
flutter: #20     RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #21     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:13)
flutter: #22     RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #23     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:13)
flutter: #24     RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #25     RenderSliverList.performLayout (package:flutter/src/rendering/sliver_list.dart:164:27)
flutter: #26     RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #27     RenderSliverPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:182:11)
flutter: #28     RenderObject.layout (package:flutter/src/rendering/object.dart:1634:7)
flutter: #29     RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:405:13)
flutter: #30     RenderViewport._attemptLayout (package:flutter/src/rendering/viewport.dart:1316:12)
flutter: #31     RenderViewport.performLayout (package:flutter/src/rendering/viewport.dart:1234:20)
flutter: #32     RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1509:7)
flutter: #33     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:768:18)
flutter: #34     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:281:19)
flutter: #35     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:677:13)
flutter: #36     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:219:5)
flutter: #37     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
flutter: #38     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
flutter: #39     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
flutter: #40     _invoke (dart:ui/hooks.dart:154:13)
flutter: #41     _drawFrame (dart:ui/hooks.dart:143:3)
flutter:
flutter: The following RenderObject was being processed when the exception was fired:
flutter:   RenderConstrainedBox#57507 relayoutBoundary=up12 NEEDS-LAYOUT NEEDS-PAINT
flutter:   creator: ConstrainedBox ← Container ← FadeTransition ← Stack ← StreamBuilder<FileInfo>-[#fb1e3] ←
flutter:   CachedNetworkImage ← IconTheme ← Builder ← _ListTile ← MediaQuery ← Padding ← SafeArea ← ⋯
flutter:   parentData: <none> (can use size)
flutter:   constraints: BoxConstraints(0.0<=w<=382.0, 0.0<=h<=Infinity)
flutter:   size: MISSING
flutter:   additionalConstraints: BoxConstraints(biggest)
flutter: This RenderObject had the following descendants (showing up to depth 5):
flutter:   RenderSemanticsAnnotations#494da NEEDS-LAYOUT NEEDS-PAINT
flutter:     RenderImage#98201 NEEDS-LAYOUT NEEDS-PAINT
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════
flutter: Another exception was thrown: RenderBox was not laid out: RenderConstrainedBox#57507 relayoutBoundary=up12 NEEDS-PAINT
flutter: Another exception was thrown: BoxConstraints forces an infinite height.
flutter: Another exception was thrown: RenderBox was not laid out: RenderConstrainedBox#ac73c relayoutBoundary=up12 NEEDS-PAINT
flutter: Another exception was thrown: BoxConstraints forces an infinite height.
flutter: Another exception was thrown: RenderBox was not laid out: RenderConstrainedBox#95164 relayoutBoundary=up12 NEEDS-PAINT
flutter: Another exception was thrown: BoxConstraints forces an infinite height.
flutter: Another exception was thrown: RenderBox was not laid out: RenderConstrainedBox#9efb3 relayoutBoundary=up12 NEEDS-PAINT
flutter: Another exception was thrown: BoxConstraints forces an infinite height.
flutter: Another exception was thrown: RenderBox was not laid out: RenderConstrainedBox#16fcc relayoutBoundary=up12 NEEDS-PAINT
flutter: Another exception was thrown: BoxConstraints forces an infinite height.

I have to make a few modifications.

Upvotes: 0

Related Questions