flutter animate FAB between extended and normal type
Asked Answered
S

6

10

I would like to implement a floating action button that animates between extended and normal sizes, as seen in androids messenger app: https://blog.usejournal.com/expand-collapse-fab-on-scrolling-like-googles-message-app-484df2d9d246

How can I achieve this? I'm currently looking into using AnimatedSwitcher, FloatingActionButton.extended and FloatingActionButton widgets.

Stockjobber answered 20/12, 2019 at 16:17 Comment(1)
Did you find any solution, please? I try to solve the same case, but any answer in this topic doesn't have a required solution. It means smooth animation between extended and collapsed FAB and opposite (as in Gmail). Thank you for your reply in advance.Aubree
N
2

There is a package that does the behavior you expect. Here it is https://pub.dev/packages/flutter_scrolling_fab_animated

Nephrotomy answered 4/10, 2021 at 0:5 Comment(0)
G
14

I was able to get it to work using an AnimatedSwitcher with SizeTransition and FadeTransition. Please see code below.

enter image description here

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")
            ],
          ),
        )
      )

The icon only view isn't fully circular, but I believe that can be fixed by adjusting padding.

Gilberte answered 12/10, 2020 at 9:53 Comment(2)
This is awesome and works perfectly. Thanks!Curson
is there a way to make it round? I tried to make it with extendedPadding: const EdgeInsets.only(left: 15, right: 8),, I guess there sould be better solutionWanonah
R
3

This is my implementation using 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')));
      }
    }

This is my implementation by keeping the same Hero tag for the two FABs.

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')));
  }
}

Both give different results, try out with different animation curves as you like. Alter the size of the child ,setting Shape border, or setting mini: equal to true to get a better looking result .

Resentment answered 20/12, 2019 at 20:33 Comment(0)
G
3

here is what I did

(your widget should be Stateful for this to work)

first add a boolean isExtended to your Widget

bool isExtended = false;

then add a new function to switch the state of your isExtended value

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

last initialise the FloatingActionButon like that

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),
    );

and that's it, whenever you call _switchActionBar() your Floating Action button will animate between extended and normal.

Gybe answered 17/5, 2020 at 18:14 Comment(0)
M
2

Just change between FAB.extended and normal FAB It has an animation when switching from the normal FAB to the extented FAB, not viseversa.

isextend
            ? FloatingActionButton.extended(
                onPressed: _switchActionBar,
                label: Text("Text"))
            : FloatingActionButton(
                onPressed: _switchActionBar,
                child: Icon(Icons.add),
              )
Mirepoix answered 17/5, 2020 at 18:36 Comment(0)
N
2

There is a package that does the behavior you expect. Here it is https://pub.dev/packages/flutter_scrolling_fab_animated

Nephrotomy answered 4/10, 2021 at 0:5 Comment(0)
I
1

I managed to come upon this in a Github discussion this approach does not require you to install any packages.

Animated FAB Widget

Widget build(BuildContext context) {
     return Scaffold(
          floatingActionButton: FloatingActionButton.extended(
          extendedIconLabelSpacing: isFabExtended ? 10 : 0,
          extendedPadding: isFabExtended ? null : const EdgeInsets.all(16),
          onPressed: createHandler,
          label: AnimatedSize(
            duration: const Duration(milliseconds: 250),
            child: isFabExtended ? const Text("Create Action") : const SizedBox(),
          ),
          icon: const Icon(Icons.add),
        )
     );
     child: ...,
}

Here isFabExtended denotes whether the FAB should also contain the label. Feel free to use your own logic to toggle this.

Inoperative answered 29/3, 2024 at 7:32 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.