Ora

How do you make a floating widget in Flutter?

Published in Flutter UI Elements 6 mins read

To make a floating widget in Flutter, you can employ several methods, ranging from using third-party packages designed for this purpose to leveraging Flutter's core layout widgets like Stack and Positioned, or more advanced Overlay widgets for global floating elements. The simplest approach often involves using a dedicated package like getwidget or building it with Stack and Positioned.

How to Make a Floating Widget in Flutter

Creating a floating widget in Flutter allows you to display content that hovers above other parts of your UI, providing an overlay experience. This is ideal for notifications, interactive pop-ups, or custom persistent buttons.

Utilizing External Packages for Floating Widgets

One of the quickest ways to implement a floating widget is by using a pre-built solution from a third-party package. These packages often abstract away the complexities, offering a straightforward API.

Introducing GFFloatingWidget

The getwidget package provides a robust and easy-to-use GFFloatingWidget that allows you to display content that floats above the main body of your screen.

  1. Add the dependency: First, add the getwidget package to your pubspec.yaml file:

    dependencies:
      flutter:
        sdk: flutter
      getwidget: ^latest_version # Use the latest stable version

    Then, run flutter pub get.

  2. Import the package: In your Dart file, import the necessary library:

    import 'package:getwidget/getwidget.dart';
  3. Implement GFFloatingWidget: You can then use GFFloatingWidget within your Scaffold's body property. It takes two main arguments: child for the floating content and body for the main content underneath.

    import 'package:flutter/material.dart';
    import 'package:getwidget/getwidget.dart';
    
    class FloatingWidgetExample extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Floating Widget Demo'),
          ),
          body: GFFloatingWidget(
            child: Container(
              padding: EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.blueAccent,
                borderRadius: BorderRadius.circular(8),
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withOpacity(0.2),
                    spreadRadius: 2,
                    blurRadius: 5,
                    offset: Offset(0, 3),
                  ),
                ],
              ),
              child: Text(
                'This is a floating text',
                style: TextStyle(color: Colors.white, fontSize: 16),
              ),
            ),
            body: SingleChildScrollView(
              child: Column(
                children: [
                  // Your main screen content goes here
                  Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Text(
                      'Body content: This text represents the main content of your screen. The floating widget will appear above this, positioned according to its default settings or custom properties.',
                      style: TextStyle(fontSize: 18),
                    ),
                  ),
                  Container(height: 200, color: Colors.grey[200]),
                  Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Text(
                      'Scroll down to see more body content. The floating widget remains visible.',
                      style: TextStyle(fontSize: 18),
                    ),
                  ),
                  Container(height: 300, color: Colors.grey[300]),
                  Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Text(
                      'End of body content.',
                      style: TextStyle(fontSize: 18),
                    ),
                  ),
                ],
              ),
            ),
            // You can also customize position and behavior
            // verticalPosition: 50.0,
            // horizontalPosition: 20.0,
          ),
        );
      }
    }

    The child property renders the actual floating element, while the body property contains the primary content of your screen. This approach simplifies the creation of overlay components without complex state management for positioning.

Crafting Custom Floating Widgets with Core Flutter Widgets

Flutter's powerful layout system allows you to create highly customized floating widgets using basic widgets.

Using Stack and Positioned

The Stack widget is excellent for layering widgets on top of each other, while Positioned widgets allow you to precisely control the location of children within a Stack.

import 'package:flutter/material.dart';

class CustomFloatingWidgetExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Custom Floating Widget'),
      ),
      body: Stack(
        children: [
          // Main content of the screen
          SingleChildScrollView(
            child: Column(
              children: [
                Container(
                  height: 200,
                  color: Colors.teal[100],
                  alignment: Alignment.center,
                  child: Text('Main Content Area 1', style: TextStyle(fontSize: 20)),
                ),
                Container(
                  height: 200,
                  color: Colors.teal[200],
                  alignment: Alignment.center,
                  child: Text('Main Content Area 2', style: TextStyle(fontSize: 20)),
                ),
                Container(
                  height: 200,
                  color: Colors.teal[300],
                  alignment: Alignment.center,
                  child: Text('Main Content Area 3', style: TextStyle(fontSize: 20)),
                ),
                SizedBox(height: 100), // Space for floating widget if at bottom
              ],
            ),
          ),
          // The custom floating widget
          Positioned(
            bottom: 20, // Distance from the bottom of the Stack
            right: 20,  // Distance from the right of the Stack
            child: GestureDetector(
              onTap: () {
                print('Floating custom button tapped!');
                // Implement your desired action here
              },
              child: Container(
                padding: EdgeInsets.all(15),
                decoration: BoxDecoration(
                  color: Colors.redAccent,
                  shape: BoxShape.circle,
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(0.3),
                      spreadRadius: 3,
                      blurRadius: 7,
                      offset: Offset(0, 3), // changes position of shadow
                    ),
                  ],
                ),
                child: Icon(Icons.add, color: Colors.white, size: 30),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
  • Best Practices:
    • Place your main screen content as the first child of the Stack. If it's scrollable, wrap it in a SingleChildScrollView.
    • Use Positioned widgets for each floating element. You can specify top, bottom, left, right, width, and height properties to control their exact placement.
    • Consider wrapping your floating widget in a GestureDetector to make it interactive.

Leveraging the Overlay Widget

For floating widgets that need to appear over the entire application, irrespective of the current Scaffold or navigation stack, Flutter's Overlay widget is the way to go. This is commonly used for custom dialogs, app-wide loading indicators, or contextual menus.

Using OverlayEntry and Overlay involves:

  1. Creating an OverlayEntry which describes the content to be overlaid.
  2. Inserting this OverlayEntry into the Overlay state, typically accessed via Overlay.of(context).
  3. Removing the OverlayEntry when no longer needed.
// Example of how to show/hide an OverlayEntry
void showCustomOverlay(BuildContext context) {
  OverlayEntry? overlayEntry; // Make it nullable and outside to be accessible

  overlayEntry = OverlayEntry(
    builder: (context) => Positioned(
      top: 50,
      left: 50,
      child: Material( // Material is important for tap handling and elevation
        elevation: 4,
        borderRadius: BorderRadius.circular(10),
        child: Container(
          padding: EdgeInsets.all(16),
          color: Colors.amber,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Text('This is an App-wide Overlay!', style: TextStyle(fontSize: 18)),
              SizedBox(height: 10),
              ElevatedButton(
                onPressed: () {
                  overlayEntry?.remove(); // Remove the overlay when button is tapped
                },
                child: Text('Dismiss'),
              ),
            ],
          ),
        ),
      ),
    ),
  );

  Overlay.of(context)?.insert(overlayEntry);
}
  • When to use Overlay:
    • When you need a widget that floats above all other widgets in the app, including AppBars and Drawers.
    • For highly dynamic or transient floating elements that need precise control over their lifecycle.

Built-in Floating Elements

Flutter also provides a common built-in floating widget for specific UI patterns.

FloatingActionButton

The FloatingActionButton (FAB) is a material design button that floats above the UI, typically in the bottom-right corner, and is used for a primary action on the screen.

import 'package:flutter/material.dart';

class FabExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FAB Example'),
      ),
      body: Center(
        child: Text(
          'Press the floating action button!',
          style: TextStyle(fontSize: 20),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          print('FAB Pressed!');
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('You pressed the FAB!')),
          );
        },
        child: Icon(Icons.add),
        backgroundColor: Colors.green,
        tooltip: 'Add new item', // Appears on long press
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, // Custom position
    );
  }
}
  • Customization: You can control its position using floatingActionButtonLocation and its appearance with properties like backgroundColor, child, and tooltip.

Choosing the Right Approach

Selecting the best method depends on the complexity and scope of your floating widget:

Method Use Case Pros Cons
GFFloatingWidget Simple page-level overlays Easy to implement, abstracts positioning Relies on external package, less fine-grained control
Stack & Positioned Custom floating elements within a Scaffold or specific layout area Highly flexible, precise control over positioning Requires manual positioning, can be complex for dynamic elements
Overlay App-wide, temporary overlays Overlays entire app, flexible lifecycle management More complex to implement, requires OverlayEntry management
FloatingActionButton Standard primary action button Built-in, adheres to Material Design Limited to primary action button use case, fixed appearance

Each method provides distinct advantages for different scenarios, allowing you to create engaging and functional floating elements in your Flutter applications.