如何在Flutter中制作Alert Dialog晃动动画

gur*_*ack 3 dart flutter flutter-layout flutter-animation flutter-alertdialog

我想像这样摇动警报对话框:(不仅是文本,还有整个弹出对话框)

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

当用户单击按钮时,如何震动整个警报对话框?

use*_*613 8

AnimatedBuilder这可以通过和小部件来完成Transform。使用sin函数 from将之间的值dart:math映射为具有所需幅度的平滑正弦波。该期间可以直接使用本身来指定。AnimationController0.01.0durationAnimationController

要启动动画,您可以调用controller.repeat()使其无限期运行,直到您调用controller.stop(),或者您可以使用controller.forward()来运行它一次。

例如,要让它摇动 3 次然后停止,您可以这样做:

onPressed: () async {
  await _controller.forward(from: 0.0);
  await _controller.forward(from: 0.0);
  await _controller.forward(from: 0.0);
},
Run Code Online (Sandbox Code Playgroud)

这是它实际的样子(请注意 GIF 的帧速率限制):

演示动图

下面附有完整的源代码,供您作为起点。您可以调整durationdistance更改摇动动画的强度:

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(),
          ),
        ],
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)