Reputation: 2166
When i scroll to the bottom of my listview, the bottom item gets rebuilt. The same when i scroll to the top, my first item gets rebuilt. The first item is a card with selectable chips that get unselected when this happens. And the "entrance" animation replays as well. How can i stop this?
Here's the basic code (it uses the simple_animations package and I can't seem to reproduce the problem with the chips, but I still have problems with the animations):
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List _chips = ['Hello', 'World'];
List _selected = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Issue demo'),
),
body: ListView(
children: <Widget>[
FadeIn(
1,
Card(
child: Wrap(
spacing: 10,
children: List<Widget>.generate(
_chips.length,
(int index) => InputChip(
label: Text(_chips[index]),
selected: _selected.contains(_chips[index]),
onSelected: (selected) {
setState(() {
if (selected) {
_selected.add(_chips[index]);
} else {
_selected.remove(_chips[index]);
}
});
}),
),
),
),
),
FadeIn(1.5, Text('A', style: Theme.of(context).textTheme.display4)),
FadeIn(2, Text('Very', style: Theme.of(context).textTheme.display4)),
FadeIn(2.5, Text('Big', style: Theme.of(context).textTheme.display4)),
FadeIn(3, Text('Scroll', style: Theme.of(context).textTheme.display4)),
FadeIn(3.5, Text('View', style: Theme.of(context).textTheme.display4)),
FadeIn(4, Text('With', style: Theme.of(context).textTheme.display4)),
FadeIn(4.5, Text('Lots', style: Theme.of(context).textTheme.display4)),
FadeIn(5, Text('Of', style: Theme.of(context).textTheme.display4)),
FadeIn(5.5,Text('Items', style: Theme.of(context).textTheme.display4)),
FadeIn(
6,
Card(
child: Text('Last item',
style: Theme.of(context).textTheme.display2),
),
),
],
),
);
}
}
class FadeIn extends StatelessWidget {
final double delay;
final Widget child;
FadeIn(this.delay, this.child);
@override
Widget build(BuildContext context) {
final tween = MultiTrackTween([
Track("opacity")
.add(Duration(milliseconds: 500), Tween(begin: 0.0, end: 1.0)),
Track("translateX").add(
Duration(milliseconds: 500), Tween(begin: 130.0, end: 0.0),
curve: Curves.easeOut)
]);
return ControlledAnimation(
delay: Duration(milliseconds: (300 * delay).round()),
duration: tween.duration,
tween: tween,
child: child,
builderWithChild: (context, child, animation) => Opacity(
opacity: animation["opacity"],
child: Transform.translate(
offset: Offset(animation["translateX"], 0), child: child),
),
);
}
}
You should run this yourself to fully understand the issue
Upvotes: 17
Views: 15948
Reputation: 1
Setting cacheExtent can improve scroll performance because it helps Flutter decide how far ahead or behind the current view to load items. This reduces the need for Flutter to load elements immediately as they come into view, which can enhance performance when scrolling through large data sets.
Note: Increasing the cacheExtent too much could impact memory usage, so you may need to experiment with the value based on your app's requirements and the size of your list items.
ListView.builder(
cacheExtent: 1000.0, // Adjust this value based on your specific use case
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
)
Upvotes: 0
Reputation: 1061
To keep elements in ListView alive (not re-render when scrolling back), you should user parameter addAutomaticKeepAlives: true
. And every element in ListView have to be StatefulWidget with AutomaticKeepAliveClientMixin.
Here is the code that I has edited for you
import 'package:flutter/material.dart';
import 'package:simple_animations/simple_animations.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List _chips = ['Hello', 'World'];
List _selected = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Issue demo'),
),
body: ListView(
addAutomaticKeepAlives: true,
children: <Widget>[
FadeIn(
1,
Card(
child: Wrap(
spacing: 10,
children: List<Widget>.generate(
_chips.length,
(int index) => InputChip(
label: Text(_chips[index]),
selected: _selected.contains(_chips[index]),
onSelected: (selected) {
setState(() {
if (selected) {
_selected.add(_chips[index]);
} else {
_selected.remove(_chips[index]);
}
});
}),
),
),
),
),
FadeIn(1.5, Text('A', style: Theme.of(context).textTheme.display4)),
FadeIn(2, Text('Very', style: Theme.of(context).textTheme.display4)),
FadeIn(2.5, Text('Big', style: Theme.of(context).textTheme.display4)),
FadeIn(3, Text('Scroll', style: Theme.of(context).textTheme.display4)),
FadeIn(3.5, Text('View', style: Theme.of(context).textTheme.display4)),
FadeIn(4, Text('With', style: Theme.of(context).textTheme.display4)),
FadeIn(4.5, Text('Lots', style: Theme.of(context).textTheme.display4)),
FadeIn(5, Text('Of', style: Theme.of(context).textTheme.display4)),
FadeIn(5.5,Text('Items', style: Theme.of(context).textTheme.display4)),
FadeIn(
6,
Card(
child: Text('Last item',
style: Theme.of(context).textTheme.display2),
),
),
],
),
);
}
}
class FadeIn extends StatefulWidget {
final double delay;
final Widget child;
FadeIn(this.delay, this.child);
_FadeInState createState() => _FadeInState();
}
class _FadeInState extends State<FadeIn> with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);//this line is needed
final tween = MultiTrackTween([
Track("opacity")
.add(Duration(milliseconds: 500), Tween(begin: 0.0, end: 1.0)),
Track("translateX").add(
Duration(milliseconds: 500), Tween(begin: 130.0, end: 0.0),
curve: Curves.easeOut)
]);
return ControlledAnimation(
delay: Duration(milliseconds: (300 * widget.delay).round()),
duration: tween.duration,
tween: tween,
child: widget.child,
builderWithChild: (context, child, animation) => Opacity(
opacity: animation["opacity"],
child: Transform.translate(
offset: Offset(animation["translateX"], 0), child: child),
),
);
}
@override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
Upvotes: 45