Maysam
Maysam

Reputation: 7367

Creating a global floating button in flutter

Is it possible to have floating button visible throughout the life cycle of the app on top of all pages? I know that with Scaffold I can have it but it only works for that page and i'll lose it once I push a new page on the navigator stack.

Upvotes: 1

Views: 1683

Answers (3)

Nicholas
Nicholas

Reputation: 509

Another solution, very similar to @puelo's answer.

But making use of the floating action button.

@override
Widget build(BuildContext context) {
  return MaterialApp(
    routes: routes,
    initialRoute: HomePage.routeName,
    builder: (context, child) {
      return Scaffold(
        body: child,
        floatingActionButton: MyButton(),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      );
    },
  );
}

Upvotes: 1

WSBT
WSBT

Reputation: 36383

Yes, OverlayEntry is made for this purpose.

To insert it, you can do something like this:

ElevatedButton(
  child: Text("Overlay Test"),
  onPressed: () {
    final entry = OverlayEntry(
      builder: (context) => Container(
        color: Colors.blue,
      ),
    );
    Overlay.of(context)?.insert(entry);
  },
)

If you want to remove it later, you can save the entry variable and then call entry.remove() when needed.

Full example:

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 {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  OverlayEntry? _entry;
  double _left = 50;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("OverlayEntry Demo"),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ElevatedButton(
              child: Text("Add OverlayEntry"),
              onPressed: () {
                _entry = OverlayEntry(
                  builder: (context) {
                    print("build");
                    return Positioned(
                      left: _left,
                      top: 200,
                      child: Container(
                        width: 150,
                        height: 150,
                        color: Colors.grey,
                      ),
                    );
                  },
                );
                Overlay.of(context)?.insert(_entry!);
              },
            ),
            ElevatedButton(
              child: Text("Move it"),
              onPressed: () {
                _left += 10;
                _entry?.markNeedsBuild();
              },
            ),
            ElevatedButton(
              child: Text("Remove it"),
              onPressed: () => _entry?.remove(),
            ),
          ],
        ),
      ),
    );
  }
}

Upvotes: 3

puelo
puelo

Reputation: 6057

One option is to use the builder of the MaterialApp to create a Stack with your Button on top:

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      home: TestPage(),
      initialRoute: "/test",
      builder: (context, child) {
        return Scaffold(
          body: Stack(
            children: [
              child!,
              Positioned(
                    left: 0,
                    bottom: 0,
                    child: **your button here**,
              ),
            ],
          ),
        );
      },
      routes: routes(context),
    );
  }
}

Upvotes: 4

Related Questions