简体   繁体   中英

How to repeatedly apply animation to a Stateless Widget in Flutter when the widget's content is updated based on new input?

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 :

在此处输入图像描述

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(() {});
},

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM