Ad

How To Make Alert Dialog Shaking Animation In Flutter

I want to shake the alert dialog like this: (not only the text, but also the entire pop up dialog)

https://www.youtube.com/watch?v=IaHMoifUBSw

How can I shake the whole alert dialog when user clicks a button?

Ad

Answer

This can be done with AnimatedBuilder and Transform widgets. Use sin function from dart:math to map the AnimationController value between 0.0 to 1.0 into a smooth sinusoidal wave with desired amplitude. The period can be specified directly using duration in the AnimationController itself.

To start the animation, you can either call controller.repeat() to make it run indefinitely, until you call controller.stop(), or you can use controller.forward() to run it once.

To let it shake 3 times then stop, for example, you can do this:

onPressed: () async {
  await _controller.forward(from: 0.0);
  await _controller.forward(from: 0.0);
  await _controller.forward(from: 0.0);
},

This is how it looks in action (please bare with the frame rate limit of GIF):

demo gif

Full source code for you to use as a starting point is attached below. You can adjust duration and distance to change the intensity of the shaking animation:

import 'dart:math';

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Test(),
    );
  }
}

class Test extends StatelessWidget {
  const Test({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Shaking Dialog Demo')),
      body: Center(
        child: ElevatedButton(
          child: Text('Show Dialog'),
          onPressed: () {
            showDialog(
              context: context,
              builder: (_) => ShakeableDialog(),
            );
          },
        ),
      ),
    );
  }
}

class ShakeableDialog extends StatefulWidget {
  final Duration duration; // how fast to shake
  final double distance; // how far to shake

  const ShakeableDialog({
    Key? key,
    this.duration = const Duration(milliseconds: 300),
    this.distance = 24.0,
  }) : super(key: key);

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

class _ShakeableDialogState extends State<ShakeableDialog>
    with SingleTickerProviderStateMixin {
  late final _controller = AnimationController(
    vsync: this,
    duration: widget.duration,
  );

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (BuildContext context, Widget? child) {
        final dx = sin(_controller.value * 2 * pi) * widget.distance;
        return Transform.translate(
          offset: Offset(dx, 0),
          child: child,
        );
      },
      child: AlertDialog(
        title: Text('Alert Dialog Title'),
        content: Text('Try these buttons!'),
        actions: [
          TextButton(
            child: Text('SHAKE 3 TIMES'),
            onPressed: () async {
              await _controller.forward(from: 0.0);
              await _controller.forward(from: 0.0);
              await _controller.forward(from: 0.0);
            },
          ),
          TextButton(
            child: Text('KEEP SHAKING'),
            onPressed: () => _controller.repeat(),
          ),
          TextButton(
            child: Text('STOP SHAKING'),
            onPressed: () => _controller.stop(),
          ),
          TextButton(
            child: Text('CLOSE'),
            onPressed: () => Navigator.of(context).pop(),
          ),
        ],
      ),
    );
  }
}
Ad
source: stackoverflow.com
Ad