【问题标题】:Toggle an animation between two separate Card classes in a Dialog with Flutter使用 Flutter 在 Dialog 中的两个单独的 Card 类之间切换动画
【发布时间】:2022-01-17 23:04:00
【问题描述】:

在我的 Flutter 应用程序中,我有一个函数可以打开一个显示两个有状态卡片的对话框。我希望做到这一点,当按下一张卡片时,它会亮起并且动画会运行。然后,另一张牌会褪色。但是,在当前配置中,可以同时选择这两个选项,这在生产设置中可能会使用户感到困惑。当对话框打开时,它应该如下所示:

然后用户应该能够选择一个或另一个,并且按钮应该像这样来回切换:

但是,按照我的代码的当前设置方式,两个按钮可以同时切换,如下所示:

我无法弄清楚如何更改我的代码的工作方式以适应这种情况。我尝试过使用 Flutter 的原生 ToggleButtons 类,但我无法让它满足我在这个项目中的需求。代码如下:

class CustomRoomStateCard extends StatefulWidget {
  final bool isPublicCard; // true: card is green, false: card is red
  static bool
      choice; //true: user's room will be public, false: user's room will be private

  CustomRoomStateCard({this.isPublicCard});
  @override
  _CustomRoomStateCardState createState() => _CustomRoomStateCardState();
}

class _CustomRoomStateCardState extends State<CustomRoomStateCard>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;

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

    controller = AnimationController(
      upperBound: 1,
      duration: Duration(milliseconds: 200),
      vsync: this,
    );
    animation = ColorTween(
            begin: (widget.isPublicCard == true
                ? Colors.green[100]
                : Colors.red[100]),
            end: (widget.isPublicCard == true ? Colors.green : Colors.red))
        .animate(controller);
    controller.addListener(() {
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onTap: () {
          setState(() {
            if (widget.isPublicCard == true) {
              CustomRoomStateCard.choice = true;
            } else {
              CustomRoomStateCard.choice = false;
            }
            if (animation.isCompleted) {
              controller.reverse();
              CustomRoomStateCard.choice = false;

              print("choice is ${CustomRoomStateCard.choice}");
            } else {
              controller.forward();
              print("choice is ${CustomRoomStateCard.choice}");
            }
          });
        },
        child: Card(
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
          color: animation.value,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Padding(
                padding: EdgeInsets.all(15.0),
                child: widget.isPublicCard
                    ? Icon(Icons.radar, color: Colors.white)
                    : Icon(Icons.shield, color: Colors.white),
              ),
              Padding(
                  padding: EdgeInsets.all(15.0),
                  child: Text(
                    widget.isPublicCard ? "Public" : "Private",
                    style: kBoldText.copyWith(color: Colors.white),
                    textAlign: TextAlign.center,
                  ))
            ],
          ),
        ));
  }
}

Future<void> showPublicPrivateChoiceDialog(BuildContext context) {
  List<bool> toggledValues = [false, false]; // an idea
  return showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.all(Radius.circular(20.0))),
          title: Text(
            "Set room privacy level",
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
          content: Container(
            height: MediaQuery.of(context).size.height * 0.2,
            width: MediaQuery.of(context).size.height * 0.7,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                Expanded(
                  child: CustomRoomStateCard(
                    isPublicCard: true,
                  ),
                ),
                Expanded(
                  child: CustomRoomStateCard(
                    isPublicCard: false,
                  ),
                )
              ],
            ),
          ),
          actions: [
            TextButton(
                onPressed: () {
                  print("the choice is ${CustomRoomStateCard.choice}");
                  isBroadcasting = CustomRoomStateCard.choice ??
                      true; // default to true in case they don't press anything
                  Navigator.pop(context);
                  return;
                },
                child: Text(
                  "Create",
                  style: TextStyle(fontWeight: FontWeight.bold),
                ))
          ],
        );
      });
}

我的第一个想法是创建一个布尔变量,如果其中一张卡已经处于活动状态,则该变量为真。当我按下一张卡片时,它会检查这个变量,并相应地改变自己,但随后也必须在另一张卡片中调用setState(),我目前不知道该怎么做。我怎样才能让这两张卡来回切换而不是同时激活?任何帮助将不胜感激!

【问题讨论】:

    标签: flutter dart mobile toggle


    【解决方案1】:

    这取决于您需要对动画进行多少控制。但是如果你不需要这些控件,你可以使用 AnimatedOpacity(..) 来实现。

    看这个例子:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    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> {
      bool isPublic = true;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Container(
              child: Column(
            children: [
              AnimatedOpacity(
                duration: const Duration(milliseconds: 500),
                opacity: isPublic ? 1.0 : 0.20,
                child: Card(
                  child: InkWell(
                    onTap: () {
                      setState(() {
                        isPublic = true;
                      });
    
                      print('is public = true');
                    },
                    child: SizedBox(
                      child: Text('Public'),
                      height: 120,
                      width: 120,
                    ),
                  ),
                  color: Colors.green[600],
                ),
              ),
              SizedBox(height: 20),
              AnimatedOpacity(
                duration: const Duration(milliseconds: 500),
                opacity: !isPublic ? 1.0 : 0.20,
                child: Card(
                  child: InkWell(
                    onTap: () {
                      setState(() {
                        isPublic = false;
                      });
    
                      print('is public = false');
                    },
                    child: SizedBox(
                      child: Text('Private'),
                      height: 120,
                      width: 120,
                    ),
                  ),
                  color: Colors.red[600],
                ),
              ),
            ],
          )), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }
    
    

    【讨论】:

    • 我最终按照您的建议使用了 AnimatedOpacity 小部件,然后在对话框函数中放入了一个 StatefulBuilder 以便我可以调用 setState。如果您有兴趣,我将工作代码粘贴在这里:pastebin.com/dnMSzGwW。谢谢!
    • 太好了,我很高兴它成功了。谢谢。
    猜你喜欢
    • 2021-01-14
    • 2012-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 2017-02-26
    相关资源
    最近更新 更多