【问题标题】:How to animate Alert dialog position in Flutter?如何在 Flutter 中设置警报对话框位置的动画?
【发布时间】:2019-11-29 14:24:56
【问题描述】:

通过这个简单的代码,我可以像下面的截图一样在屏幕底部显示对话框:

但我有三个简单的问题:

  1. 在对话框底部设置边距,例如在显示对话框时设置20.0
  2. 在关闭对话框中使用 controller.reverse()
  3. 点击对话框外关闭对话框

完整源代码:

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> {
  void showPopup() {
    showDialog(
      context: context,
      builder: (_) => PopUp(),
    );
  }

  @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:',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: showPopup,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class PopUp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => PopUpState();
}

class PopUpState extends State<PopUp> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation<double> opacityAnimation;
  Tween<double> opacityTween = Tween<double>(begin: 0.0, end: 1.0);
  Tween<double> marginTopTween = Tween<double>(begin: 300, end: 280);
  Animation<double> marginTopAnimation;

  @override
  void initState() {
    super.initState();

    controller = AnimationController(duration: const Duration(milliseconds: 300), vsync: this);
    marginTopAnimation = marginTopTween.animate(controller)
      ..addListener(() {
        setState(() {});
      });
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: opacityTween.animate(controller),
      child: Material(
        color: Colors.transparent,
        child: Container(
          margin: EdgeInsets.only(
            top: marginTopAnimation.value,
            left:20.0,
            right:20.0,
          ),
          color: Colors.red,
          child: Text("Container"),
        ),
      ),
    );
  }

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

【问题讨论】:

  • 你好,我认为margin应该很简单,你可以在Container中设置bottom:20。是你要找的吗?
  • @CopsOnRoad 我测试了它,但它不起作用
  • @CopsOnRoad 实现了改变和调整对话框高度的动画原因,不从屏幕外移动,然后设置底部边距不起作用

标签: flutter flutter-layout flutter-animation


【解决方案1】:

截图:


代码:

floatingActionButton: FloatingActionButton(
  onPressed: () {
    showGeneralDialog(
      barrierLabel: "Label",
      barrierDismissible: true,
      barrierColor: Colors.black.withOpacity(0.5),
      transitionDuration: Duration(milliseconds: 700),
      context: context,
      pageBuilder: (context, anim1, anim2) {
        return Align(
          alignment: Alignment.bottomCenter,
          child: Container(
            height: 300,
            child: SizedBox.expand(child: FlutterLogo()),
            margin: EdgeInsets.only(bottom: 50, left: 12, right: 12),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(40),
            ),
          ),
        );
      },
      transitionBuilder: (context, anim1, anim2, child) {
        return SlideTransition(
          position: Tween(begin: Offset(0, 1), end: Offset(0, 0)).animate(anim1),
          child: child,
        );
      },
    );
  },
)

【讨论】:

  • 如果你想从自定义位置开始,那么你所能做的就是将begin: Offset(0, 1)更改为begin: Offset(0.2, 1)
【解决方案2】:

从上到下都可以使用

bool _fromTop = true;

@override
Widget build(BuildContext context) {
  return Scaffold(
    floatingActionButton: FloatingActionButton(
      onPressed: () {
        showGeneralDialog(
          barrierLabel: "Label",
          barrierDismissible: true,
          barrierColor: Colors.black.withOpacity(0.5),
          transitionDuration: Duration(milliseconds: 700),
          context: context,
          pageBuilder: (context, anim1, anim2) {
            return Align(
              alignment: _fromTop ? Alignment.topCenter : Alignment.bottomCenter,
              child: Container(
                height: 300,
                child: SizedBox.expand(child: FlutterLogo()),
                margin: EdgeInsets.only(top: 50, left: 12, right: 12, bottom: 50),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(40),
                ),
              ),
            );
          },
          transitionBuilder: (context, anim1, anim2, child) {
            return SlideTransition(
              position: Tween(begin: Offset(0, _fromTop ? -1 : 1), end: Offset(0, 0)).animate(anim1),
              child: child,
            );
          },
        );
      },
    ),
  );
}

【讨论】:

  • 如何设置自定义启动动画而不是零或屏幕外?在您的代码对话框中,从屏幕外部进行动画处理,现在我想知道如何设置可选位置,例如:而不是从外部开始:Offset(0.0, _fromTop ? -1 : 1) 我可以从屏幕的 30% 到 50%,position: Tween(begin: Offset(0.3, _fromTop ? -1 : 1), end: Offset(0, 0)).animate(anim1),喜欢:i.stack.imgur.com/0io74.gif
  • @DolDurma 我在之前的评论中回答过,那里更容易让你理解。
  • 这是你的意思吗:Tween&lt;double&gt; _tween = Tween(begin: 0.1, end: 1);
  • 我不太明白,因为当我尝试使用position: Tween&lt;double&gt;(begin: Offset(0.8, _fromTop ? -1 : 1)).animate(anim1), 时出现错误
【解决方案3】:

输出:

不确定我是否清楚地了解了您的问题,如果这是您正在寻找的内容,请将您的 PopUp 课程替换为我的课程。

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> {
  void showPopup() {
    showDialog(
      context: context,
      builder: (_) => PopUp(),
    );
  }

  @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:',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: showPopup,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class PopUp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => PopUpState();
}

class PopUpState extends State<PopUp> with TickerProviderStateMixin {
  AnimationController controller;
  double _bottom = 0, _fromTop = 300, _screenHeight, _containerHeight = 300;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(duration: const Duration(milliseconds: 300), vsync: this)
      ..addListener(() {
        Timer.periodic(Duration(milliseconds: 15), (timer) {
          if (_bottom < _screenHeight - _fromTop - _containerHeight) {
            _bottom += 1;
            setState(() {});
          }
        });
      });
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    _screenHeight = MediaQuery.of(context).size.height;
    return SizedBox(
      width: double.infinity,
      height: double.infinity,
      child: Stack(
        children: <Widget>[
          Positioned(
            bottom: _bottom,
            left: 0,
            right: 0,
            child: Container(height: _containerHeight, color: Colors.green),
          ),
        ],
      ),
    );
  }

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

【讨论】:

  • 如何从底部设置?不是顶部
  • 我认为这不是我想要的,请看这个屏幕截图我的意思是什么,谢谢imgur.com/a/ZWBl4Cn
  • 明白了,这解释了一切,伙计,你应该在一开始就发布,我以前会这样做,现在我不能保证,但是一旦我有足够的时间(可能今天本身),我将发布代码。
  • 你能帮助我如何关闭拖动到页面底部的对话框并在此链接中设置限制以拖动到页面底部:https://stackoverflow.com/questions/57034088/flutter-adding-more-options-for-dialogs 通过可选参数提前感谢
  • 我当然可以帮忙,但我现在不在工作岗位,也许明天我可以做。谢谢
【解决方案4】:


import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(home: CityPage()));
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Center(
            child: TextButton(
              child: const Text('Press me'),
              onPressed: () => BottomDialog().showBottomDialog(context),
            ),
          ),
        ],
      ),
    );
  }
}

class BottomDialog {
  void showBottomDialog(BuildContext context) {
    showGeneralDialog(
      barrierLabel: "showGeneralDialog",
      barrierDismissible: true,
      barrierColor: Colors.black.withOpacity(0.6),
      transitionDuration: const Duration(milliseconds: 400),
      context: context,
      pageBuilder: (context, _, __) {
        return Align(
          alignment: Alignment.bottomCenter,
          child: _buildDialogContent(),
        );
      },
      transitionBuilder: (_, animation1, __, child) {
        return SlideTransition(
          position: Tween(
            begin: const Offset(0, 1),
            end: const Offset(0, 0),
          ).animate(animation1),
          child: child,
        );
      },
    );
  }

  Widget _buildDialogContent() {
    return IntrinsicHeight(
      child: Container(
        width: double.maxFinite,
        clipBehavior: Clip.antiAlias,
        padding: const EdgeInsets.all(16),
        decoration: const BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(16),
            topRight: Radius.circular(16),
          ),
        ),
        child: Material(
          child: Column(
            children: [
              const SizedBox(height: 16),
              _buildImage(),
              const SizedBox(height: 8),
              _buildContinueText(),
              const SizedBox(height: 16),
              _buildEmapleText(),
              const SizedBox(height: 16),
              _buildTextField(),
              const SizedBox(height: 16),
              _buildContinueButton(),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildImage() {
    const image =
        'https://user-images.githubusercontent.com/47568606/134579553-da578a80-b842-4ab9-ab0b-41f945fbc2a7.png';
    return SizedBox(
      height: 88,
      child: Image.network(image, fit: BoxFit.cover),
    );
  }

  Widget _buildContinueText() {
    return const Text(
      'Continue with account',
      style: TextStyle(
        fontSize: 22,
        fontWeight: FontWeight.w500,
      ),
    );
  }

  Widget _buildEmapleText() {
    return const Text(
      'example.com',
      style: TextStyle(
        fontSize: 16,
        fontWeight: FontWeight.w500,
      ),
    );
  }

  Widget _buildTextField() {
    const iconSize = 40.0;
    return Container(
      height: 60,
      padding: const EdgeInsets.symmetric(horizontal: 16),
      decoration: BoxDecoration(
        color: Colors.white,
        border: Border.all(width: 1, color: Colors.grey.withOpacity(0.4)),
        borderRadius: const BorderRadius.all(Radius.circular(8)),
      ),
      child: Row(
        children: [
          Container(
            width: iconSize,
            height: iconSize,
            decoration: BoxDecoration(
              shape: BoxShape.circle,
              color: Colors.grey[200],
            ),
            child: const Center(
              child: Text('Е'),
            ),
          ),
          const SizedBox(height: 16),
          Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: const [
              Text(
                'elisa.g.beckett@gmail.com',
                style: TextStyle(
                  fontWeight: FontWeight.w600,
                ),
              ),
              Text('**********'),
            ],
          )
        ],
      ),
    );
  }

  Widget _buildContinueButton() {
    return Container(
      height: 40,
      width: double.maxFinite,
      decoration: const BoxDecoration(
        color: Color(0xFF3375e0),
        borderRadius: BorderRadius.all(Radius.circular(8)),
      ),
      child: RawMaterialButton(
        onPressed: () {
          Navigator.of(context, rootNavigator: true).pop();
        },
        child: const Center(
          child: Text(
            'Continue',
            style: TextStyle(
              color: Colors.white,
              fontWeight: FontWeight.w500,
            ),
          ),
        ),
      ),
    );
  }
}

【讨论】:

    【解决方案5】:
                          InkWell(
                        onTap: () => showGeneralDialog(
                          barrierLabel: "Barrier",
                          barrierDismissible: true,
                          barrierColor: Colors.black.withOpacity(0.5),
                          transitionDuration: Duration(milliseconds: 700),
                          context: context,
                          pageBuilder: (_, __, ___) {
                            return Align(
                              alignment: Alignment.center,
                              child: Container(
                                height: 300,
                                margin: EdgeInsets.only(left: 40, right: 40),
                                child: Column(
                                  children: [
                                    Container(
                                      height: 60,
                                      width: double.infinity,
                                      child: Center(
                                        child: Text(
                                          "xxxxx",
                                          style: TextStyle(
                                              decoration: TextDecoration.none,
                                              color: Colors.white,
                                              fontSize: 20),
                                        ),
                                      ),
                                      decoration: BoxDecoration(
                                        color: Colors.blue,
                                        borderRadius: BorderRadius.only(
                                            topLeft: Radius.circular(12),
                                            topRight: Radius.circular(12)),
                                      ),
                                    ),
                                  ],
                                ),
                                decoration: BoxDecoration(
                                  color: Colors.white,
                                  borderRadius: BorderRadius.circular(12),
                                ),
                              ),
                            );
                          },
                          transitionBuilder: (_, anim, __, child) {
                            return SlideTransition(
                              position:
                                  Tween(begin: Offset(0, 1), end: Offset(0, 0))
                                      .animate(anim),
                              child: child,
                            );
                          },
                        ),
                        child: Container(
                          margin: EdgeInsets.only(right: 6),
                          child: Text(
                            "(-) Discount : " +
                                itemValues["discount"]["discount_amount"]
                                    .toString(),
                            style: TextStyle(
                              color: Colors.red,
                              fontWeight: FontWeight.bold,
                              fontSize: 12,
                            ),
                          ),
                        ),
                      )[If you want animated  dialog like this you can use this][1]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-15
      • 2013-09-17
      • 2021-05-25
      • 2022-01-20
      • 2014-12-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多