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
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) {
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With