LLCampos
LLCampos

Reputation: 325

How to add fade in/out on state transition

By default, when there's a state change on a StatefulWidget, there's no animation on state transition. Is there any way to had some? For example, on transition, I want to fade out the old state and fade in the new state.

I know how to add transition animation between routes, but couldn't find a way to do it between state transitions.

Has an example, let's use the Flutter default app:

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(
        primarySwatch: Colors.blue,
      ),
      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> {
  int _counter = 0;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

So, each time I click the button the number changes immediately. What I would like to see is the old number fade out and the new number fade in.

Upvotes: 4

Views: 11716

Answers (2)

LLCampos
LLCampos

Reputation: 325

This answer, using AnimatedOpacity works ok. But I found using AnimatedSwitcher much simpler and cleaner.

(...)
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            'You have pushed the button this many times:',
          ),
          AnimatedSwitcher(
            duration: const Duration(milliseconds: 500),
            transitionBuilder: (Widget child, Animation<double> animation) {
              return FadeTransition(child: child, opacity: animation);
            },
            child: Text(
              '$_counter',
              key: ValueKey<int>(_counter),
              style: Theme.of(context).textTheme.display1,
            ),
          ),
        ],
      ),
    ),
(...)

See here for more information about AnimatedSwitcher.

Upvotes: 6

Argel Bejarano
Argel Bejarano

Reputation: 622

It will depends how you can manage this, it could be using an animationController and workin with the values of an opacity widgets you will need to add the Mixin with SingleTickerProviderStateMixin so the Animation works.

That is one of the wait you could do it but also if you just want an fadeout fadein you can work with an AnimatedOpacity.

Like this:

import 'dart:async';
import 'package:flutter/material.dart';

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

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

class _HomePageState extends State<HomePage> {
  int _counter = 0;
  double _opacity = 1;

  void _incrementCounter() {
    setState(() => _opacity = 0);
    Future.delayed(Duration(milliseconds: 500),() =>
      setState(() {
        _counter++;
        _opacity = 1;
      }
    ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            AnimatedOpacity(
              opacity: _opacity,
              duration: Duration(milliseconds: 500),
              child: Text(
                '$_counter',
                style: Theme.of(context).textTheme.display1,
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

This is a easy way to do a fadeOur/FadeIn as i said if you need a complete animation and more control in it, definitely you should use an AnimationController.

Upvotes: 3

Related Questions