I'm trying to make a news section in my app. In this page that's gonna display the news, i want to be able to click anywhere on the page and get the news that is next in my list. So far no problem with that, but i wanted it to have a nice animation so i tried implementing AnimatedSwitcher, but i can't figure out why there is no animation showing.
I tried changing the hierarchy of my code. Putting the gesture detector inside the animated switcher and the other way around. Letting the main container outside or inside of it too. I tried an animation builder that would scale it just in case it wasnt obvious enough but nothing. Tried changing the duration too but that wasn't it.
class ShowNews extends StatefulWidget {
@override
_ShowNewsState createState() => _ShowNewsState();
}
class _ShowNewsState extends State<ShowNews> {
List<News> _news = [
News(title: 'OYÉ OYÉ', desc: 'bla bla bla bla bla'),
News(title: 'another one', desc: 'plus de bout d\'histoire'),
News(title: 'boum', desc: 'attention à l\'accident'),
News(title: 'Lorem ipsum', desc: 'Lorem ipsum in doloris'),
];
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
if (_currentIndex < _news.length - 1) {
_currentIndex++;
} else {
_currentIndex = 0;
}
});
},
child: Container(
height: 160,
padding: EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
child: AnimatedSwitcher(
duration: Duration(seconds: 5),
child: ColumnArticle(_news, _currentIndex),
),
),
);
}
}
Everything is working fine but the animation.
Edit: I tried adding a key to make it different but still no animation.
class ColumnArticle extends StatelessWidget {
final List<News> _news;
final int _currentIndex;
ColumnArticle(this._news, this._currentIndex);
@override
Widget build(BuildContext context) {
return Column(
key: ValueKey<int>(_currentIndex),
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
_news[_currentIndex].title,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 20.0,
),
),
SizedBox(
height: 10.0,
),
Text(
_news[_currentIndex].desc,
style: TextStyle(
fontSize: 14.0,
),
),
],
);
}
}
Passing the same type of widget with different attributes will not trigger an animation since they are the same widgets for the framework. It's also mentioned in the description.
If the "new" child is the same widget type and key as the "old" child, but with different parameters, then AnimatedSwitcher will not do a transition between them, since as far as the framework is concerned, they are the same widget and the existing widget can be updated with the new parameters. To force the transition to occur, set a Key on each child widget that you wish to be considered unique (typically a ValueKey on the widget data that distinguishes this child from the others).
Here is the code from AnimatedSwitcher
that checks whether to animate or not:
if (hasNewChild != hasOldChild ||
hasNewChild && !Widget.canUpdate(widget.child, _currentEntry.widgetChild)) {
// Child has changed, fade current entry out and add new entry.
_childNumber += 1;
_addEntryForNewChild(animate: true);
}
This is the static canUpdate
method from the framework:
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
To solve this you can set individual keys to your News
widgets based on their distinct attributes (eg. text, count, value). ValueKey<T>
is just for that.
Column(
children: <Widget>[
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: Text(
'$_count',
// This key causes the AnimatedSwitcher to interpret this as a "new"
// child each time the count changes, so that it will begin its animation
// when the count changes.
key: ValueKey<int>(_count),
),
),
RaisedButton(
child: const Text('Increment'),
onPressed: () {
setState(() {
_count += 1;
});
},
),
])
That happens because the AnimatedSwitcher
will add an animation anytime it is rebuilt with a different child reference. However, in your widget lifecycle, you are always using a ColumnArticle
as a child, thus, not actually swapping any widget type, that's where the ValueKey
comes in play.
You can use the index as the reference for the key, but make sure it actually changes, otherwise it won't work and you also need to pass it to your ColumnArticle
base widget (super
).
So, your ColumnArticle
should look like this:
class ColumnArticle extends StatelessWidget {
final List<News> _news;
final int _currentIndex;
ColumnArticle(this._news, this._currentIndex) : super(key: ValueKey<int>(_currentIndex));
...
}
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