By default, the Scaffold in Flutter animates the floating action button (FAB) when changing a FAB while the app is running.

scaling animation

How can I disable this animation?

The documentation references the FloatingActionButtonAnimator.scaling animation which scales the button when it changes:

/// Animator to move the [floatingActionButton] to a new [floatingActionButtonLocation]. /// /// If null, the [ScaffoldState] will use the default animator, [FloatingActionButtonAnimator.scaling]. final FloatingActionButtonAnimator floatingActionButtonAnimator;

However, there is no indication on how to disable the scaling animation completely.

Full example code with the issue:

import 'dart:async';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Timer _timer;
  bool showFirst = true;

  @override
  void initState() {
    _timer = Timer.periodic(new Duration(seconds: 2), (Timer t) {
      setState(() {
        showFirst = !showFirst;
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(),
      floatingActionButtonLocation: showFirst
          ? FloatingActionButtonLocation.centerDocked
          : FloatingActionButtonLocation.endDocked,
      floatingActionButton: Padding(
        padding: EdgeInsets.only(top: 100.0),
        child: Column(
          children: <Widget>[
            Text('Floating Action Button Title'),
            showFirst
                ? FloatingActionButton.extended(
                    heroTag: 'unique',
                    icon: Icon(Icons.filter_1),
                    label: Text('First FAB'),
                    onPressed: () {},
                  )
                : FloatingActionButton.extended(
                    heroTag: 'unique2',
                    icon: Icon(Icons.filter_2),
                    label: Text('Second FAB'),
                    onPressed: () {},
                  ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }
}

Adding a different hero tag to each FAB doesn't affect the animation.


Solution 1: dshukertjr

floatingActionButtons in Flutter have property named heroTag, and every floatingActionButtons have the same default value for it. Giving each floatingActionButtons unique heroTag will prevent the animation from happening.

Scaffold(
    floatingActionButton: FloatingActionButton(
      heroTag: "somethingUnique",
      onPressed: () {},
      child: Icon(Icons.add,),
    ),
    body: Container(),
)


Solution 2: Saed Nabil

You need to extend FloatingActionButtonAnimator and override its methods, check the following code,

import 'dart:async';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Timer _timer;
  bool showFirst = true;

  @override
  void initState() {
    _timer = Timer.periodic(new Duration(seconds: 2), (Timer t) {
      setState(() {
        showFirst = !showFirst;
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(),
      floatingActionButtonAnimator: NoScalingAnimation(),
      floatingActionButtonLocation: showFirst
          ? FloatingActionButtonLocation.centerDocked
          : FloatingActionButtonLocation.endDocked,
      floatingActionButton: Padding(
        padding: EdgeInsets.only(top: 100.0),
        child: Column(
          children: <Widget>[
            Text('Floating Action Button Title'),
            showFirst
                ? FloatingActionButton.extended(
              heroTag: 'unique',
              icon: Icon(Icons.filter_1),
              label: Text('First FAB'),
              onPressed: () {},
            )
                : FloatingActionButton.extended(
              heroTag: 'unique2',
              icon: Icon(Icons.filter_2),
              label: Text('Second FAB'),
              onPressed: () {},
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }
}

class NoScalingAnimation extends FloatingActionButtonAnimator{
  double _x;
  double _y;
  @override
  Offset getOffset({Offset begin, Offset end, double progress}) {
  _x = begin.dx +(end.dx - begin.dx)*progress ;
  _y = begin.dy +(end.dy - begin.dy)*progress;
    return Offset(_x,_y);
  }

  @override
  Animation<double> getRotationAnimation({Animation<double> parent}) {
    return Tween<double>(begin: 1.0, end: 1.0).animate(parent);
  }

  @override
  Animation<double> getScaleAnimation({Animation<double> parent}) {
    return Tween<double>(begin: 1.0, end: 1.0).animate(parent);
  }
}

enter image description here

You can control the animation behavior by changing what every method returns. for ex. you can make the fab jump from left to right without animation, by changing getOffset method to be

  @override
  Offset getOffset({Offset begin, Offset end, double progress}) {
    if (progress == 1.0){
      return end;
    }else{
      return begin;
    }
  }