Reputation: 704
I am trying to apply some animation on a set of grid items in my app. For this purpose I am using the package: https://pub.dev/packages/flutter_staggered_animations
When the screen loads, the grid items appear in a fade in manner as set in the AnimationConfiguration.staggeredGrid()
However, when I update the filters or sort criterion, by clicking on relevant radio buttons, checkboxes or moving sliders, the updated grid items appear without any animation. I want the updated grid items to appear with the same type of animation as they do when the page initially loads.
I understand that the grid items are displayed in a stateless widget. How do I get to animate the updated grid items? Is there a way to convert the buildResult()
to a stateful widget? If so, how to do it?
Full Code:
import 'package:flutter/material.dart';
import 'package:switch_circle_color/model/girls.dart';
import 'package:switch_circle_color/screens/gender.dart';
import 'package:switch_circle_color/screens/girls_cart.dart';
import 'package:switch_circle_color/screens/selected_girl_details.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import '../global_variables/global_variables.dart';
class GirlsScreen extends StatefulWidget {
GirlsScreen({Key? key}) : super(key: key);
@override
State<GirlsScreen> createState() => _GirlsScreenState();
}
class _GirlsScreenState extends State<GirlsScreen> {
int distance = 15;
int age = 25;
String sortCriterion = "distance";
bool isSingle = false;
bool isSerious = false;
void changeDistance(double num) {
setState(() {
distance = num.round();
populateFilteredGirls(distance, age);
//buildResult(context);
});
}
void changeAgeLimit(double a) {
setState(() {
age = a.round();
populateFilteredGirls(distance, age);
//buildResult(context);
});
}
List<Girls> allGirls = [
Girls("Reshmita", 25, 33, "Married", "Serious"),
Girls("Ankita", 17, 26, "Single", "Serious"),
Girls("Rupali", 42, 28, "Single", "Casual"),
Girls("Monica", 50, 24, "Single", "Casual"),
Girls("Sakshi", 9, 27, "Married", "Casual"),
];
List<Girls> filteredGirlsbyDistance = [];
List<Girls> filteredGirlsbyAge = [];
List<Girls> filteredGirls = [];
List<Girls> filteredGirlsbySerious = [];
List<Girls> filteredGirlsbySingle = [];
//List<Girls> girlsCart = [];
void addGirlToCart(Girls girl) {
girlsCart.add(girl);
cartValue = cartValue + girl.age;
}
void removeGirlsfromCart(Girls girl) {
girlsCart.remove(girl);
cartValue = cartValue - girl.age;
}
String selectedGirlName = "";
int? selectedGirlDistance;
@override
void initState() {
super.initState();
//filteredGirls = allGirls;
distance = 20;
age = 28;
sortCriterion = "distance";
isSingle = false;
isSerious = false;
(sortCriterion == "distance")
? allGirls.sort((a, b) => a.distance.compareTo(b.distance))
: allGirls.sort((a, b) => a.age.compareTo(b.age));
populateFilteredGirls(distance, age);
}
void populateFilteredGirls(int dis, int ag) {
filteredGirlsbyDistance.clear();
filteredGirlsbyAge.clear();
filteredGirlsbySingle.clear();
filteredGirlsbySerious.clear();
filteredGirls.clear();
//int len = filteredGirls.length;
for (int i = 0; i < allGirls.length; i++) {
if (allGirls[i].distance <= dis) {
filteredGirlsbyDistance.add(allGirls[i]);
}
}
filteredGirls = filteredGirlsbyDistance;
for (int i = 0; i < filteredGirls.length; i++) {
if (filteredGirls[i].age <= ag) {
filteredGirlsbyAge.add(filteredGirls[i]);
}
}
filteredGirls = filteredGirlsbyAge;
//len = filteredGirls.length;
if (isSingle == true) {
for (int i = 0; i < filteredGirls.length; i++) {
if (filteredGirls[i].status.toLowerCase() == "single") {
filteredGirlsbySingle.add(filteredGirls[i]);
}
}
filteredGirls = filteredGirlsbySingle;
}
if (isSerious == true) {
for (int i = 0; i < filteredGirls.length; i++) {
if (filteredGirls[i].lookingFor.toLowerCase() == "serious") {
filteredGirlsbySerious.add(filteredGirls[i]);
}
}
filteredGirls = filteredGirlsbySerious;
}
//filteredGirls = filteredGirls.toSet().toList();
}
Widget buildResult(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(5.0),
child: AnimationLimiter(
child: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 5,
mainAxisSpacing: 5,
childAspectRatio: 2,
children: List.generate(filteredGirls.length, (index) {
return AnimationConfiguration.staggeredGrid(
columnCount: 2,
position: index,
duration: const Duration(milliseconds: 375),
child: ScaleAnimation(
child: FadeInAnimation(
child: ListTile(
leading: InkWell(
child: const Icon(Icons.girl_outlined),
onTap: () {
removeGirlsfromCart(filteredGirls[index]);
},
),
trailing: InkWell(
child: Text("${filteredGirls[index].distance} km away"),
onTap: () {
addGirlToCart(filteredGirls[index]);
},
),
title: Text("${filteredGirls[index].name}"),
subtitle: Text(
"${filteredGirls[index].age} years old, ${filteredGirls[index].status}, ${filteredGirls[index].lookingFor}"),
onTap: () {
// setState(() {
// selectedGirlName = filteredGirls[index].name;
// selectedGirlDistance = filteredGirls[index].distance;
// });
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SelectedGirlDetails(
girl: filteredGirls[index],
)));
},
),
),
),
);
}),
//itemCount: ,
//itemBuilder: (BuildContext context, int index) {
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Padding(padding: EdgeInsets.all(20)),
// ElevatedButton(
// onPressed: () {
// Navigator.push(context,
// MaterialPageRoute(builder: (context) => GenderScreen()));
// },
// child: Text("Go to Gender Screen")),
// Padding(padding: EdgeInsets.all(30)),
Text("Set max distance"),
Padding(padding: EdgeInsets.all(10)),
Slider(
min: 1.0,
max: 100.0,
divisions: 100,
activeColor: Colors.green,
inactiveColor: Colors.orange,
label: 'Set distance value',
value: distance.toDouble(),
onChanged: (value) {
changeDistance(value);
},
),
Padding(padding: EdgeInsets.all(10)),
Text("Current distance is $distance kms"),
Padding(padding: EdgeInsets.all(10)),
Text("Set max age"),
Padding(padding: EdgeInsets.all(10)),
Slider(
min: 18.0,
max: 60.0,
divisions: 42,
activeColor: Colors.green,
inactiveColor: Colors.orange,
label: 'Set age limit',
value: age.toDouble(),
onChanged: (value) {
changeAgeLimit(value);
},
),
Padding(padding: EdgeInsets.all(10)),
Text("Age limit is $age years"),
Padding(padding: EdgeInsets.all(10)),
Text("Sort by:"),
Padding(padding: EdgeInsets.all(7.5)),
ListTile(
//minLeadingWidth: 30,
title: Text("Age"),
leading: Radio(
value: "age",
groupValue: sortCriterion,
onChanged: (value) {
setState(() {
sortCriterion = value.toString();
allGirls.sort((a, b) => a.age.compareTo(b.age));
populateFilteredGirls(distance, age);
//buildResult(context);
});
},
),
),
Padding(padding: EdgeInsets.all(7.5)),
ListTile(
//minLeadingWidth: 30,
title: Text("Distance"),
leading: Radio(
value: "distance",
groupValue: sortCriterion,
onChanged: (value) {
setState(() {
sortCriterion = value.toString();
allGirls.sort((a, b) => a.distance.compareTo(b.distance));
populateFilteredGirls(distance, age);
//buildResult(context);
});
},
),
),
Padding(padding: EdgeInsets.all(10)),
Text("Is Single?"),
Padding(padding: EdgeInsets.all(2.5)),
InkWell(
onTap: () {
setState(() {
isSingle = !isSingle;
populateFilteredGirls(distance, age);
//buildResult(context);
});
},
child: (isSingle == false)
? Icon(Icons.check_box_outline_blank)
: Icon(Icons.check_box),
),
Padding(padding: EdgeInsets.all(5)),
Text("Is Serious?"),
Padding(padding: EdgeInsets.all(2.5)),
InkWell(
onTap: () {
setState(() {
isSerious = !isSerious;
populateFilteredGirls(distance, age);
//buildResult(context);
});
},
child: (isSerious == false)
? Icon(Icons.check_box_outline_blank)
: Icon(Icons.check_box),
),
Padding(padding: EdgeInsets.all(10)),
buildResult(context),
Padding(padding: EdgeInsets.all(25)),
ElevatedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => GirlsCart()));
},
child: Text("Go to Girls Cart")),
Padding(padding: EdgeInsets.all(25)),
],
),
));
}
}
Video for reference:
Upvotes: 0
Views: 352
Reputation: 63689
Cant see any controller for this. You can rebuild the full widget by changing the widget key. But try to find better way.
Create a variable that will change used as key value. While you like to animate, change the assign new value on key variable. I am using int count = 0;
On state class
int count = 0;
And use onkey
body: AnimationLimiter(
key: ValueKey("list $count"),
When ever like to animate increase the counter.
onPressed: () {
count++;
setState(() {});
},
Upvotes: 1