2

I'm pretty new to flutter and i'm trying to do some animation on a PageView. to be precise, I want to animate removing an item.

I've tried serveral ways to animate it and apart from a solution, the way how you guys would solve such a problem would also be helpful for my flutter skils.

What I've tried so far:

  • Animating the padding and opacity
    • the problem with this is that when i set the padding in the setState in the onLongPress it rebuilds the widget and it overrides the padding again with the active or inactive CardPadding (i think)
  • Animating the width and height
    • I just can't seem to get both of these values to work
  • Animating the viewportFraction on the PageViewController
    • Would not know how to go about this and if it would be possible to do this only for a specific 'Page'

Below is the (stripped down) code I've written thus far.

class Main extends StatefulWidget {
  @override
  _MainState createState() => _MainState();
}

class _MainState extends State<Main> {
  int activeCard = 0;

  EdgeInsets inActiveCardPadding = EdgeInsets.symmetric(vertical: 120.0, horizontal: 20.0);
  EdgeInsets activeCardPadding = EdgeInsets.symmetric(vertical: 105.0, horizontal: 10.0);

  PageController pageController = PageController(
    initialPage: 0,
    viewportFraction: 0.8,
  );

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            PageView.builder(
              itemCount: PlantCareApp.plants.length,
              controller: pageController,
              onPageChanged: (activeCardIndex) {
                setState(() {
                  this.activeCard = activeCardIndex;
                });
              },
              itemBuilder: (context, cardIndex) {
                return AnimatedContainer(
                  padding: (activeCard == cardIndex) ? activeCardPadding : inActiveCardPadding;,
                  duration: Duration(milliseconds: 250),
                  child: PlantCard(
                    PlantCareApp.plants[cardIndex],
                    onTap: () {
                      Navigator.pushNamed(context, PlantDetailScreen.route, arguments: PlantCareApp.plants[cardIndex]);
                    },
                    onLongPress: () {
                      setState(() {
                        //
                        // ANIMATE OR TRIGGER ANIMATION HERE
                        //

                        // do the actual removing
                        /*
                          PlantCareApp.plants[cardIndex].remove(); // remove from db
                          PlantCareApp.plants.removeAt(cardIndex); // remove from List
                        */
                      });
                      //PlantCareApp.plants[cardIndex].remove();
                    },
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

Any help will be greatly appreciated! How would you guys tackle a problem like this, or how would you tackle this specific use case.

I guess actually animating viewportFraction would be the nicest because of the adjecent 'Pages' moving toward each other as well?

Thanks!

Gijs Beijer
  • 560
  • 5
  • 19

1 Answers1

2

I'm not certain if this is what you are looking for, but here goes.

One way of doing this is simply using the provided Widgets within Flutter. Two of these will help you out: AnimatedList and Dismissible.

Now, you could do something like this:

// define somewhere
final _animatedListGK = GlobalKey<AnimatedListState>();

// put in a function somewhere
return AnimatedList(
  key: _animatedListGK,
  padding: const EdgeInsets.all(0),
  initialItemCount: PlantCareApp.plants.length,
  itemBuilder: (context, index, animation) {
    return FadeTransition(
      opacity: animation,
      child: _buildDismissibleRow(context, index, PlantCareApp.plants[index])
    );
  }
);

Note: you don't have to use the _animatedListGK global key per se, it depends on whether you can use AnimatedList.of(context) or not. Although it is the easier way.

The _animatedListGK is simply a Global Key that provides access to the AnimatedList so you can perform insertions/removals with animation.

Your dismissible row might look something like:

Widget _buildDismissibleRow(BuildContext context, int index, PlantModel plantModel) {
    return Dismissible(
      key: ValueKey<String>(plantModel.someKey),
      direction: DismissDirection.startToEnd,
      background: Container(color: Colors.red),
      onDismissed: (direction) {
        // You could use:
        // AnimatedList.of(context)
        _animatedListGK.currentState.removeItem(
          index,
          (context, animation) => Container(),
          duration: Duration.zero
        );
      },
      child: _buildContent(context, index, plantModel)
    );
}

You could also do it without a dismissible row or even within the child of the dismissible row (_buildContent() for example). Something similar to:

// You could use:
// AnimatedList.of(context)
_animatedListGK.currentState.removeItem(
  index,
  (context, animation) {
    return FadeTransition(
      opacity: CurvedAnimation(parent: animation, curve: Interval(0.5, 1.0)),
      child: SizeTransition(
        sizeFactor: CurvedAnimation(parent: animation, curve: Interval(0.0, 1.0)),
        child: _builContent(context, index, plantModel)
      )
    );
  },
  duration: const Duration(milliseconds: 300)
);

Notice how the SizeTransition simply "calls itself" by calling _builContent(context, index, plantModel)? That's how you can animate the row itself (out of existence).

Be sure to watch the videos in the aforementioned documentation pages! They will help understanding certain constructs.

A preview of what the dismissible might look like: Dismissible removal

A preview of what the SizedTransition might look like: SizedTransition removal

voracious
  • 890
  • 4
  • 7
  • Hi! Thanks for your answer I'll certainly give it a try. Could you also tell me if the Animatedlist can be aligned horizontally and if snapping is possible? – Gijs Beijer Nov 21 '19 at 21:10
  • @GijsBeijer not that i know of. What do you exactly mean by snapping? – voracious Nov 22 '19 at 12:45
  • @voracios well, what I'm trying to create should work something like the Android app drawer, so horizontally swiping cards, by snapping I mean that there's always one card exactly in the middle when the animation ends – Gijs Beijer Nov 23 '19 at 10:04