Uchenna Ndukwe
Uchenna Ndukwe

Reputation: 71

Back and Forth animation in Flutter

I am trying to do an animation to make a green box move back and forth. Trying to use onEnd and curve. So I want the green box to go by its self to the right and back. It will be moving back and forth and I will also have to put something in the setState.

import 'package:flutter/material.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(
  
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  double _moveX = 0.0;

  void _incrementCounter() {
    setState(() {
      _moveX++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
              child: AnimatedAlign(
                duration: Duration(microseconds: 300),
                curve: Curves.bounceIn,
                alignment: Alignment(_moveX, -0.0),
                child: Container(
                  width: 50,
                  height: 50,
                  color: Colors.green,
                ),
              ),
            )
          ],
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Upvotes: 0

Views: 2537

Answers (2)

Hrvoje Čukman
Hrvoje Čukman

Reputation: 447

Here is the example of a local notification that is displayed for 3 seconds with back and forth animation.

import 'package:flutter/material.dart';
import 'package:romobili_poc/app/constants/AppConstants.dart';

class LevelNotification extends StatefulWidget {
  @override
  _LevelNotificationState createState() => _LevelNotificationState();
}

class _LevelNotificationState extends State<LevelNotification>
    with SingleTickerProviderStateMixin {
  AnimationController levelNotificationController;
  Animation<Offset> levelNotificationOffset;

  @override
  void initState() {
    levelNotificationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 500),
    );
    levelNotificationOffset = Tween<Offset>(
      begin: Offset(0.0, -1.0),
      end: Offset(0.0, 0.0),
    ).animate(levelNotificationController);

    levelNotificationOffset.addStatusListener((status) {
      print(status);
      Future.delayed(
        const Duration(seconds: 3),
        () => levelNotificationController.reverse(),
      );
    });
    super.initState();
  }

  @override
  void dispose() {
    levelNotificationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.topCenter,
      child: SlideTransition(
        position: levelNotificationOffset,
        child: InkWell(
          onTap: () {
            levelNotificationController.forward();
          },
          child: Container(
            color: Colors.red,
            height: 100,
            width: double.infinity,
          ),
        ),
      ),
    );
  }
}

Upvotes: 1

Christopher Moore
Christopher Moore

Reputation: 17123

In the onEnd property of the AnimatedAlign widget, trigger the animation to go back with a decrementCounter method of some kind, identical to _incrementCounter, but decreasing instead.

void _decrementCounter() {
  setState(() {
    _moveX--;
  });
}

To trigger the animation initially, you should increment the position value in initState, but it must be registered as a post frame callback to ensure the widget is mounted, and also to trigger the animation properly in AnimatedAlign. Ex.

@override
void initState() {
  super.initState();
  WidgetsBinding.addPostFrameCallback(_incrementCounter);
}

Since you also want to make this animation repeat, you need to conditionally call either _incrementCounter or _decrementCounter based on the last called method. This can be easily done just by checking the current position. Ex.

onEnd: () {
  if(_moveX.round() == 0) {//round might not be able to be used depending on the range of values you want to animate between
    _incrementCounter();
  }
  else {
    _decrementCounter();
  }
}

This could likely be done more simply with an AnimationController as it has a repeat method, but it might not benefit you enough to be used in this case.

Upvotes: 0

Related Questions