DEV Community

Cover image for Create an Expandable Floating Action Button (FAB) in Flutter
Kuldeep Tarapara
Kuldeep Tarapara

Posted on • Originally published at flutteragency.com

Create an Expandable Floating Action Button (FAB) in Flutter

A key widget in the program is the floating action button. Through it, users can explore several functionalities. Use the floating Action Button to display more project-related tasks. A few buttons combined into one, however, could complicate your primary app interface.

In such an instance, we may get the help of Flutter programmers to improve the UI of our program potentially. Let’s begin by writing Flutter’s Expandable Floating Action Button (FAB) code.

A circular icon floats towards the lowest right side of the content, known as a floating button (FAB). The button will represent the main operation for the relevant material.

However, there may not always be the main action. The user may choose to perform a few crucial activities instead. In this situation, you might design an expanded FAB similar to the one in the next picture. This expanding FAB produces several additional buttons when it is tapped. Crucial activities are associated with each button.

What is included in your layout of the Floating Action Button?

The color accent property, which defaults by the FAB colors, may be altered using the theme’s color scheme. Other FAB characteristics can be set using matching XML elements or procedures, such as the ones listed below:

  • The width of the FAB is controlled using the setSize () function or the app: fabSize property.
  • Using the setRippleColor() function or the app:rippleColor property, you may change the FAB’s ripple color.
  • The FAB icon is created by using the setImageDrawable() function or the android: src property.

An ExpandableFab Widget Must Be Developed

Make an ExpandableFab stateful widget to begin with. This widget shows the main FAB and controls of related action icons expand and contract. The widget accepts input for a series of children, the optimum distance between each option, and ExpandedFab starts in the extended position. Eventually, you will supply most other buttons using the list.

@immutable
class ExpandableFab extends StatefulWidget {
  const ExpandableFab({
    super.key,
    this.initialOpen,
    required this.distance,
    required this.children,
  });
  final bool? initialOpen;
  final double distance;
  final List<widget> children;
  @override
  State<expandablefab> createState() => _ExpandableFabState();
}
class _ExpandableFabState extends State<expandablefab> {
  @override
  Widget build(BuildContext context) {
    return const SizedBox();
  }
}
</expandablefab></expandablefab></widget>
Enter fullscreen mode Exit fullscreen mode

What is Cross-fade FAB?

When the ExpandableFab is compressed, a blue edit feature appears, and a white home button does when it is enlarged. The above-mentioned buttons expand and transition amongst one another while expanding and contracting.

Use the cross-fade here between two distinct FABs to expand and shrink.

class _ExpandableFabState extends State<expandablefab> {
  bool _open = false;
  @override
  void initState() {
    super.initState();
    _open = widget.initialOpen ?? false;
  }
  void _toggle() {
    setState(() {
      _open = !_open;
    });
  }
  @override
  Widget build(BuildContext context) {
    return SizedBox.expand(
      child: Stack(
        alignment: Alignment.bottomRight,
        clipBehavior: Clip.none,
        children: [
          _buildTapToCloseFab(),
          _buildTapToOpenFab(),
        ],
      ),
    );
  }
  Widget _buildTapToCloseFab() {
    return SizedBox(
      width: 56.0,
      height: 56.0,
      child: Center(
        child: Material(
          shape: const CircleBorder(),
          clipBehavior: Clip.antiAlias,
          elevation: 4.0,
          child: InkWell(
            onTap: _toggle,
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Icon(
                Icons.close,
                color: Theme.of(context).primaryColor,
              ),
            ),
          ),
        ),
      ),
    );
  }
  Widget _buildTapToOpenFab() {
    return IgnorePointer(
      ignoring: _open,
      child: AnimatedContainer(
        transformAlignment: Alignment.center,
        transform: Matrix4.diagonal3Values(
          _open ? 0.7 : 1.0,
          _open ? 0.7 : 1.0,
          1.0,
        ),
        duration: const Duration(milliseconds: 250),
        curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
        child: AnimatedOpacity(
          opacity: _open ? 0.0 : 1.0,
          curve: const Interval(0.25, 1.0, curve: Curves.easeInOut),
          duration: const Duration(milliseconds: 250),
          child: FloatingActionButton(
            onPressed: _toggle,
            child: const Icon(Icons.create),
          ),
        ),
      ),
    );
  }
}
</expandablefab>
Enter fullscreen mode Exit fullscreen mode

Within a stack, the accessible button is placed over the close button, creating the illusion of a cross-fade when the top button emerges and vanishes.

The open button uses an AnimatedContainer with only a magnitude change and an AnimatedOpacity to produce the cross-fade animation. Once the ExpandableFab expands from folded to expanded, the open button shrinks and fades off. When the ExpandableFab transitions from extended to compressed, the open icon subsequently scales up and gradually becomes visible.

You’ll see that an IgnorePointer widget is used to surround the open button. The open button can be present even when it’s translucent, which explains why. Without the IgnorePointer, even though the close button is accessible, the open button constantly gets the tap signal.

How to make a widget with An Action Button?

The ExpandableFab expandable buttons all have the same style. They have white symbols on blue circles. To be more explicit, the icon color is ColorScheme.onSecondary, while the button colour combination is ColorScheme.secondary.

Create the ActionButton standalone widget to showcase these circular buttons.

@immutable
class ActionButton extends StatelessWidget {
  const ActionButton({
    super.key,
    this.onPressed,
    required this.icon,
  });
  final VoidCallback? onPressed;
  final Widget icon;
  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Material(
      shape: const CircleBorder(),
      clipBehavior: Clip.antiAlias,
      color: theme.colorScheme.secondary,
      elevation: 4.0,
      child: IconButton(
        onPressed: onPressed,
        icon: icon,
        color: theme.colorScheme.onSecondary,
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

How to increase and Decrease the Action Buttons?

The child ActionButtons should pop out from beneath the open FAB when enlarged. When the kid ActionButtons collapse, they should travel back beneath the open FAB. Each ActionButton must be explicitly placed in the (x,y) coordinates for this mobility, and animation must be used to choreograph modifications to those (x,y) places over time.

To regulate the speed in alignment with the several ActionButtons expand and collapse, provide an AnimationController and a Visual Effect.

class _ExpandableFabState extends State<expandablefab>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final Animation<double> _expandAnimation;
  bool _open = false;
  @override
  void initState() {
    super.initState();
    _open = widget.initialOpen ?? false;
    _controller = AnimationController(
      value: _open ? 1.0 : 0.0,
      duration: const Duration(milliseconds: 250),
      vsync: this,
    );
    _expandAnimation = CurvedAnimation(
      curve: Curves.fastOutSlowIn,
      reverseCurve: Curves.easeOutQuad,
      parent: _controller,
    );
  }
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  void _toggle() {
    setState(() {
      _open = !_open;
      if (_open) {
        _controller.forward();
      } else {
        _controller.reverse();
      }
    });
  }
}
</double></expandablefab>
Enter fullscreen mode Exit fullscreen mode

Then, add a brand-new stateless widget named _ExpandingActionButton and set it up to animate and reposition each ActionButton individually. The ActionButton is offered as a child widget, which is a general widget.

@immutable
class _ExpandingActionButton extends StatelessWidget {
  const _ExpandingActionButton({
    required this.directionInDegrees,
    required this.maxDistance,
    required this.progress,
    required this.child,
  });
  final double directionInDegrees;
  final double maxDistance;
  final Animation<double> progress;
  final Widget child;
  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: progress,
      builder: (context, child) {
        final offset = Offset.fromDirection(
          directionInDegrees * (math.pi / 180.0),
          progress.value * maxDistance,
        );
        return Positioned(
          right: 4.0 + offset.dx,
          bottom: 4.0 + offset.dy,
          child: Transform.rotate(
            angle: (1.0 - progress.value) * math.pi / 2,
            child: child!,
          ),
        );
      },
      child: FadeTransition(
        opacity: progress,
        child: child,
      ),
    );
  }
}
</double>
Enter fullscreen mode Exit fullscreen mode

The Positioned widget, which essentially places the child at a precise (x,y) coordinate inside the enclosing stack, is the most crucial component of the _ExpandingActionButton. Every time the motion changes, the AnimatedBuilder forces the Oriented widget to rebuild. The FadeTransition widget coordinates each ActionButton’s emergence and departure during expansion and contraction.

Because _ExpandingActionButton uses a positioned widget, it is implied that _ExpandingActionButton may only be utilized as a primary child of a stack. This is because position and stack have an explicit connection. To finish the exercise, utilize the unique _ExpandingActionButton widget inside the ExpandableFab. Your FAB may now be expanded.

Interactive illustration

Start the app:

Tap the FAB, which is indicated by an Edit icon in the lower-right corner. It spreads out again to 3 buttons before being replaced by the close button, symbolized by an X.

When you tap the close button, the enlarged buttons return to the usual FAB, and the X is substituted with the Edit icon.
Once more, expanding the FAB, select one of the three satellite icons to display a dialogue box describing what that button does.

Conclusion

In this article, you have learnt how to create the Expandable Floating Action Button, and the code we have provided will help you develop the FAB. If you still have queries or questions that come to your mind related to Flutter app development, then connect with the best company like Flutter agency. Click here!

Frequently Asked Questions (FAQs)

1. What is the fab in the UI in Flutter?

A floating action button (FAB) is a circular button that will trigger a direct action in the app’s UI. This page will describe how to add the FAB to your layout, customize the appearance, and will respond to button taps.

2. How are action buttons functional?

An action button is an object on the slide that will act whenever it is clicked. It does actions like jumping to other slides or playing the sound. These action buttons are mainly used for self-running presentations.

3. In which view does the action button work?

If you wish to add an action button to every slide, you can do this with a feature named Slide Master View. However, using the Slide Master View is complex if you haven’t used it before. If you are going to edit your slides in this manner, then first learn the basics of Master Slider View.

Top comments (0)