Flutter Advanced - Detailed Animation

Table of contents

animation category

1. Implicit (automatic) animation

2. Explicit animation (manual control)

3. Other animations (CustomPainter)

animation category

There are several types of animations in Flutter:

  • Implicit Animations: Predefined animations that are automatically triggered by changing widget properties, such as  AnimatedContainer, AnimatedOpacity , AnimatedPaddingetc.
  • Explicit Animations:   Manually control animations by using Animation and  classes.AnimationController
  • Physical animation: animation based on physical laws, such as  SpringSimulation, FrictionSimulation etc.

1. Implicit (automatic) animation

 AnimatedSwitcher(
          duration: const Duration(seconds: 2),
          transitionBuilder: (child, animation) {
            return FadeTransition(
              opacity: animation,
              child: ScaleTransition(
                scale: animation,
                child: child,
              ),
            );
          },
          child: const Text("Hello"),
        )

Motion tweening (the first frame (0.0) and the last frame (1.0) have been determined, if FPS 60 supplements the middle 58 frames)

 TweenAnimationBuilder(
            tween: Tween(begin: 0.0, end: 1.0),
            duration: const Duration(seconds: 5),
            builder: (BuildContext context, Object? value, Widget? child) {
              return Opacity(
                opacity: value as double,
                child: Container(
                  width: 300,
                  height: 300,
                  color: Colors.red[200],
                ),
              );
            }))
TweenAnimationBuilder(
            tween: Tween(begin: 20.0, end: 50.0),
            duration: const Duration(seconds: 5),
            builder: (context,double value, Widget? child) {
              return Container(
                  width: 300,
                  height: 300,
                  color: Colors.red[200],
                  child: Text('Hello', style: TextStyle(fontSize: value)));
            }))

2. Explicit animation (manual control)

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 4))..repeat();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: AnimatedBuilder(
            animation: _controller,
            builder: (BuildContext context, Widget? child) {
              return Opacity(
                  opacity: _controller.value,
                  child: Container(
                    width: 300,
                    height: 300,
                    color: Colors.blue,
                  ));
            }));
  }
}

If the above gradient animation does not use the classic display animation universal control AnimatedBuilder, you can use FadeTransition directly

 FadeTransition(
                    opacity: _controller,
                    child:
                        Container(width: 200, height: 200, color: Colors.blue),
                  )),

An animation that spins endlessly

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
      
  late AnimationController _controller;
  bool _loading = false;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: Center(
          child: RotationTransition(
              turns: _controller, child: const Icon(Icons.refresh, size: 100)),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            if (_loading) {
              _controller.reset();
            } else {
              _controller.repeat();
            }
            _loading = !_loading;
          },
        ));
  }
}

FadeTransition、ScaleTransition、SlideTransition

The controller connects Tween and Curve in series

ScaleTransition(
             scale: _controller.drive(Tween(begin: 0.5, end:2.0)), child: const Icon(Icons.refresh, size: 100)),
        ),
SlideTransition(
             position: _controller.drive(Tween(begin: Offset(0,0), end:Offset(1,0))), 
// position:Tween(begin: const Offset(0, 0), end: const Offset(1, 0)).animate(_controller),
child: const Icon(Icons.refresh, size: 100)),

 position:Tween(begin: const Offset(0, 0), end: const Offset(1, 0))
                      .chain(CurveTween(curve: Curves.elasticInOut))
                      .animate(_controller),
class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {

  late AnimationController _controller;
  final bool _loading = false;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 4))..repeat(reverse:true);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
          child: Column(children: [
       SlidingBox(
          controller: _controller,
          color: Colors.blue[100]!,
          interval: const Interval(0.0, 0.2),
        ),
          SlidingBox(
          controller: _controller,
          color: Colors.blue[300]!,
          interval: const Interval(0.2, 0.4),
        ),
          SlidingBox(
          controller: _controller,
          color: Colors.blue[500]!,
          interval: const Interval(0.4, 0.6),
        ),
          SlidingBox(
          controller: _controller,
          color: Colors.blue[700]!,
          interval: const Interval(0.6, 0.8),
        ),
        SlidingBox(
          controller: _controller,
          color: Colors.blue[900]!,
          interval: const Interval(0.8, 1.0),
        ),
      ])),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _controller.forward();
        },
      ),
    );
  }
}

class SlidingBox extends StatelessWidget {
  final AnimationController controller;
  final Color color;
  final Interval interval;

  const SlidingBox(
      {Key? key,
      required this.controller,
      required this.color,
      required this.interval})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
        position: Tween(begin: Offset.zero, end: const Offset(0.2, 0))
            .chain(CurveTween(curve: Curves.bounceOut))
            .chain(CurveTween(curve: interval))
            .animate(controller),
        //CurveTween(curve: const Interval(0.8, 1.0)) 控制动画从最后的1/5时间开始到结束
        child: Container(width: 300, height: 100, color: color));
  }
}

Custom breathing light animation 

 

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

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  late AnimationController _expansionController;
  late AnimationController _opacityController;

  @override
  void initState() {
    _expansionController = AnimationController(vsync: this);
    _opacityController = AnimationController(vsync: this);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: Center(
          child: FadeTransition(
            opacity: Tween(begin: 1.0, end: 0.5).animate(_opacityController),
            child: AnimatedBuilder(
                animation: _expansionController,
                builder: (BuildContext context, Widget? child) {
                  return Container(
                    height: 200,
                    width: 200,
                    decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      color: Colors.blue,
                      gradient: RadialGradient(colors: [
                        Colors.blue[600]!,
                        Colors.blue[100]!,
                      ], stops: [
                        _expansionController.value,
                        _expansionController.value + 0.1
                      ]),
                    ),
                  );
                }),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () async {
            _expansionController.duration = const Duration(seconds: 4);
            _expansionController.forward();
            await Future.delayed(const Duration(seconds: 4));
            _opacityController.duration = const Duration(milliseconds: 1750);
            _opacityController.repeat(reverse: true);
            await Future.delayed(const Duration(seconds: 7));
            _opacityController.reset();
            _expansionController.duration = const Duration(seconds: 8);
            _expansionController.reverse();
          },
        ));
  }
}

3. Other animations (CustomPainter)

direct manipulation of the underlying

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

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  final List<Snowflake> _snowflakes =
      List.generate(100, (index) => Snowflake());

  @override
  void initState() {;
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 4))
          ..repeat();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: Center(
          child: Container(
            width: double.infinity,
            height: double.infinity,
            decoration: const BoxDecoration(
              gradient: LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  colors: [Colors.blue, Colors.lightBlue, Colors.white],
                  stops: [0.0, 0.75, 0.95]),
            ),
            child: AnimatedBuilder(
              animation: _controller,
              builder: (BuildContext context, Widget? child) {
                for (var snow in _snowflakes) {
                  snow.fall();
                }
                return CustomPaint(
                  painter: MyPainter(_snowflakes),
                );
              },
            ),
          ),
        ));
  }
}

class MyPainter extends CustomPainter {
  final List<Snowflake> _snowflakes;
  MyPainter(this._snowflakes);

  @override
  void paint(Canvas canvas, Size size) {
    final whitePaint = Paint()..color = Colors.white;
    canvas.drawCircle(size.center(const Offset(0, 100)), 60.0, whitePaint);
    canvas.drawOval(
        Rect.fromCenter(
            center: size.center(const Offset(0, 260)), width: 230, height: 250),
        whitePaint);
    _snowflakes.forEach((snowflake) {
      canvas.drawCircle(
          Offset(snowflake.x, snowflake.y), snowflake.radius, whitePaint);
    });
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

class Snowflake {
  double x = Random().nextDouble() * 400;
  double y = Random().nextDouble() * 800;
  double radius = Random().nextDouble() * 2 + 2;
  double velocity = Random().nextDouble() * 4 + 2;
  void fall() {
    y += velocity;
    if (y > 800) {
      y = 0;
      x = Random().nextDouble() * 400;
      radius = Random().nextDouble() * 2 + 2;
      velocity = Random().nextDouble() * 4 + 2;
    }
  }
}

Guess you like

Origin blog.csdn.net/RreamigOfGirls/article/details/131377796