在扩展类型和普通类型之间颤动动画 FAB

Posted

技术标签:

【中文标题】在扩展类型和普通类型之间颤动动画 FAB【英文标题】:flutter animate FAB between extended and normal type 【发布时间】:2020-04-13 03:38:20 【问题描述】:

我想实现一个在扩展尺寸和正常尺寸之间动画的浮动操作按钮,如 androids messenger 应用程序所示:https://blog.usejournal.com/expand-collapse-fab-on-scrolling-like-googles-message-app-484df2d9d246

我怎样才能做到这一点?我目前正在研究使用 AnimatedSwitcher、FloatingActionButton.extended 和 FloatingActionButton 小部件。

【问题讨论】:

请问您找到解决方案了吗?我尝试解决相同的情况,但本主题中的任何答案都没有所需的解决方案。这意味着在扩展和折叠 FAB 和相反(如在 Gmail 中)之间的平滑动画。感谢您提前回复。 【参考方案1】:

我能够使用AnimatedSwitcherSizeTransitionFadeTransition 使其工作。请看下面的代码。

floatingActionButton:
     FloatingActionButton.extended(
        onPressed: _onFabPress,
        label: AnimatedSwitcher(
          duration: Duration(seconds: 1),
          transitionBuilder: (Widget child, Animation<double> animation) =>
          FadeTransition(
            opacity: animation,
            child: SizeTransition(child:
            child,
              sizeFactor: animation,
              axis: Axis.horizontal,
            ),
          ) ,
          child: isExtended?
          Icon(Icons.arrow_forward):
          Row(
            children: [
              Padding(
                padding: const EdgeInsets.only(right: 4.0),
                child: Icon(Icons.add),
              ),
              Text("Add To Order")
            ],
          ),
        )
      )

仅图标视图不是完全圆形的,但我相信可以通过调整填充来解决。

【讨论】:

这太棒了,而且效果很好。谢谢!【参考方案2】:

这是我使用AnimatedSwitcher 的实现。

 import 'package:flutter/material.dart';

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget 
      @override
      Widget build(BuildContext context) 
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(),
        );
      
    

    class MyHomePage extends StatefulWidget 
      @override
      _MyHomePageState createState() => _MyHomePageState();
    

    class _MyHomePageState extends State<MyHomePage> 
      bool isIcon = false;

      Widget _myWidget = FloatingActionButton(
        key: UniqueKey(),
        onPressed: () ,
        child: Icon(Icons.message),
      );
      void _d() 
        setState(() 
          _myWidget = isIcon
              ? FloatingActionButton(
                  mini: true,
                  key: UniqueKey(),
                  onPressed: () ,
                  child: Icon(Icons.messsage),
                )
              : FloatingActionButton.extended(
                  key: UniqueKey(),
                  onPressed: () ,
                  icon: Icon(Icons.message),
                  label: Text("Start chat"),
                );
          isIcon = !isIcon;
        );
      

      Widget build(context) 
        return Scaffold(
            floatingActionButton: isIcon
                ? AnimatedSwitcher(
                    duration: Duration(
                      milliseconds: 100,
                    ),
                    transitionBuilder: (Widget child, Animation<double> animation) 
                      return FadeTransition(
                        child: child,
                        opacity:
                            animation.drive(CurveTween(curve: Curves.elasticOut)),
                      );
                    ,
                    child: _myWidget,
                  )
                : AnimatedSwitcher(
                    duration: Duration(
                      milliseconds: 100,
                    ),
                    transitionBuilder: (Widget child, Animation<double> animation) 
                      return FadeTransition(
                        child: child,
                        opacity:
                            animation.drive(CurveTween(curve: Curves.easeOutQuint)),
                      );
                    ,
                    child: _myWidget,
                  ),
            appBar: AppBar(),
            body: FlatButton(
                onPressed: () 
                  _d();
                ,
                child: Text('Press here to change FAB')));
      
    

这是我的实现,为两个 FAB 保留相同的 Hero 标签。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  


class MyHomePage extends StatefulWidget 
  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
  bool isIcon = false;

  Widget _myWidget = FloatingActionButton(
    heroTag: 'd',
    key: UniqueKey(),
    onPressed: () ,
    child: Icon(Icons.message),
  );
  void _d() 
    setState(() 
      _myWidget = isIcon
          ? FloatingActionButton(
              heroTag: 'b',
              key: UniqueKey(),
              onPressed: () ,
              child: Icon(Icons.message),
            )
          : FloatingActionButton.extended(
              heroTag: 'b',
              key: UniqueKey(),
              onPressed: () ,
              icon: Icon(Icons.mesage),
              label: Text("Start chat"),
            );
      isIcon = !isIcon;
    );
  

  Widget build(context) 
    return Scaffold(
        floatingActionButton: _myWidget,
        appBar: AppBar(),
        body: FlatButton(
            onPressed: () 
              _d();
            ,
            child: Text('Press here to change FAB')));
  

两者都给出不同的结果,根据需要尝试不同的动画曲线。改变孩子的大小,设置Shape边框,或者设置mini:等于true以获得更好看的结果。

【讨论】:

【参考方案3】:

这就是我所做的

(your widget should be Stateful for this to work)

首先将布尔值isExtended 添加到您的小部件

bool isExtended = false;

然后添加一个新函数来切换isExtended值的状态

void _switchActionBar() 
    setState(
      () 
        isExtended = !isExtended;
      ,
    );
  

最后像这样初始化FloatingActionButon

FloatingActionButton.extended(
      isExtended: isExtended,
      onPressed: (),
      label: isExtended
          ? Row(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(right: 8.0),
                  child: Icon(Icons.add),
                ),
                Text("Start Editing"),
              ],
            )
          : Icon(Icons.add),
    );

就是这样,每当您调用_switchActionBar() 时,您的浮动操作按钮都会在扩展和正常之间进行动画处理。

【讨论】:

【参考方案4】:

只需在 FAB.extended 和普通 FAB 之间切换 当从普通 FAB 切换到扩展 FAB 时,它有一个动画,而不是相反。

isextend
            ? FloatingActionButton.extended(
                onPressed: _switchActionBar,
                label: Text("Text"))
            : FloatingActionButton(
                onPressed: _switchActionBar,
                child: Icon(Icons.add),
              )

【讨论】:

【参考方案5】:

有一个包可以执行您期望的行为。这里是https://pub.dev/packages/flutter_scrolling_fab_animated

【讨论】:

以上是关于在扩展类型和普通类型之间颤动动画 FAB的主要内容,如果未能解决你的问题,请参考以下文章

Flash中帧的类型有( )种。

如何为具有多种颜色颤动的文本颜色设置动画

打包数据类型与普通数据类型之间的区别

扩展类型的泛型和 Typescript 中的普通类型有啥区别?

类型'String'不是get方法颤动中类型'Null'的子类型

Linux基础:文件类型与扩展名