Flutter - How to make SnackBar to not push FloatingActionButton upward?
Asked Answered
M

3

8

How to make the Snackbar to overlap the FloatingActionButton and not push it up upward when popped up? I've attached my simplified code for reference. Thank you in advance.

class Screen extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => ScreenState();
}

class ScreenState extends State<Screen>{

  BuildContext context;

  @override
  Widget build(BuildContext context) => Scaffold(
    floatingActionButton: FloatingActionButton(
      onPressed: action,
    ),
    body: Builder(
      builder : (BuildContext context){
        this.context = context;
        return Container();
      }
    )
  );

  action() => Scaffold.of(context).showSnackBar(SnackBar(
    duration: Duration(milliseconds : 1000),
    content: Container(height: 10)
  ));
}
Muskogean answered 13/11, 2019 at 4:15 Comment(0)
M
3

Simply use multiple Scaffolds. Wrap the inner one with an outer one. Use inner to put everything belong to it, body, appBar, bottomNavigationBar, floatingActionButton and so on. Call showSnackBar from outer's body's BuildContext. If any, move drawer from inner to outer to keep it above the SnackBar.

If you prefer to popup the SnackBar like a classic toast, see Siddharth Patankar's answer. Thank you for that too.

Below is the simplified code for my answer.

  @override
  Widget build(BuildContext context) => Scaffold(
    drawer : Drawer(child: Container()),
    body: Builder(
      builder : (BuildContext context){
        this.context = context;
        return Scaffold(
          body: Container(),
          floatingActionButton: FloatingActionButton(
            onPressed: () => Scaffold.of(context).showSnackBar(SnackBar(
              duration: Duration(milliseconds : 1000),
              content: Container(height: 30),
            )),
          ),
        );
      }
    )
  );
Muskogean answered 13/11, 2019 at 9:56 Comment(0)
A
16

You can set the behavior property of the SnackBar to SnackBarBehavior.floating which will display the Snackbar above other widgets.

This should do it -

class Screen extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => ScreenState();
}

class ScreenState extends State<Screen>{

  BuildContext context;

  @override
  Widget build(BuildContext context) => Scaffold(
    floatingActionButton: FloatingActionButton(
      onPressed: action,
    ),
    body: Builder(
      builder : (BuildContext context){
        this.context = context;
        return Container();
      }
    )
  );

  action() => Scaffold.of(context).showSnackBar(SnackBar(
    duration: Duration(milliseconds : 1000),
    content: Container(height: 10)
    behavior: SnackBarBehavior.floating, // Add this line
  ));
}

See this link for more information.

Apish answered 13/11, 2019 at 4:34 Comment(6)
thank you. good solution. is there any way to make it overlaid?Muskogean
Could you please elaborate by what you mean by overlaid? Because the solution I provided already displays the SnackBar above other widgets.Apish
overlapping like in a StackMuskogean
As far as I know, it won't be possible directly with the SnackBar widget. You might need to create your own custom widget for that and then wrap it in a Stack. Also, an overlapping SnackBar is not recommended as per the official Material Design Guidelines from Google.Apish
Also, if my answer solved your problem, please click the big checkbox to accept it as the answer. Cheers!Apish
your comment is enough to answer the question. don't need to show code. Thanks anywaySlipsheet
M
3

Simply use multiple Scaffolds. Wrap the inner one with an outer one. Use inner to put everything belong to it, body, appBar, bottomNavigationBar, floatingActionButton and so on. Call showSnackBar from outer's body's BuildContext. If any, move drawer from inner to outer to keep it above the SnackBar.

If you prefer to popup the SnackBar like a classic toast, see Siddharth Patankar's answer. Thank you for that too.

Below is the simplified code for my answer.

  @override
  Widget build(BuildContext context) => Scaffold(
    drawer : Drawer(child: Container()),
    body: Builder(
      builder : (BuildContext context){
        this.context = context;
        return Scaffold(
          body: Container(),
          floatingActionButton: FloatingActionButton(
            onPressed: () => Scaffold.of(context).showSnackBar(SnackBar(
              duration: Duration(milliseconds : 1000),
              content: Container(height: 30),
            )),
          ),
        );
      }
    )
  );
Muskogean answered 13/11, 2019 at 9:56 Comment(0)
P
2

SnackBar Overlap FAB (Basic)

enter image description here

To have a SnackBar overlap a FAB, the FAB should be placed on a nested Scaffold.

class BasicOverlapFAB extends StatelessWidget {
  const BasicOverlapFAB();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Basic Overlap'),
      ),
      body: Scaffold( // ← Nested
        body: Center(child: Text('SnackBar will overlap FAB'),),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.keyboard_double_arrow_down),
          onPressed: () => ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('This overlaps the FAB'),)
          ),
        ),
      )
    );
  }
}

This works because a SnackBar is shown on the highest Scaffold nearest to a ScaffoldMessenger. The nearest ScaffoldMessenger is included in MaterialApp by default.

In the above code, the Scaffold showing the SnackBar is parent/above the Scaffold with a FAB, thus Snack overlaps FAB.

MaterialApp
  ScaffoldMessenger
    Scaffold ← Snacks here
      Scaffold
        FAB

Nested Scaffolds & ScaffoldMessengers

SnackBar overlap FAB

This example has several nested Scaffolds.

Scaffolds are like panes (layers) of glass. We can stack one on top of another. And we can show FABs, SnackBars and other widgets on each layer.

To make the layers of Scaffolds easier to see, each Scaffold is smaller and is outlined with a different color: blue, green, red. Red is the furthest down. (Nested panes go underneath.)

Scaffolds don't provide a ScaffoldMessenger by default, but MaterialApp provides a default ScaffoldMessenger. Most of the time we're interacting with this default "root" ScaffoldMessenger, but we can add our own.

For each layer of Scaffold in this example (AnotherScaffold custom class) we manually added its own ScaffoldMessenger. Each also has a FAB that shows SnackBars on its own ScaffoldMessenger.

Copy/Paste & play around with this in an emulator to get a feel for how the Scaffolds/ScaffoldMessengers form layers & how they overlap & interact.

class NestedScaffoldsPage extends StatelessWidget {
  const NestedScaffoldsPage();

  @override
  Widget build(BuildContext context) {
    return Container( // unnecessary container just for border color / educational visibility
      decoration: BoxDecoration(
          border: Border.all(color: Colors.blue)
      ),
      child: Scaffold(// Root Scaffold for FAB #0
        appBar: AppBar(
          title: Text('Nested Scaffolds'),
        ),
        body: AnotherScaffold(// Nested Scaffold & FAB #1
          level: 1,
          color: Colors.green,
          body: AnotherScaffold(// Nested Scaffold & FAB #2
            level: 2,
            color: Colors.red,
            body: Center(child: Text('3 Total Scaffolds')),
          ),
        ),
        floatingActionButton: FloatingActionButton( // FAB #0
          child: Text('0'),
          onPressed: () => ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('Snack level 0'),
                duration: const Duration(seconds: 2),
                //behavior: SnackBarBehavior.floating, // SnackBar will show above FAB
                //behavior: SnackBarBehavior.fixed, // default, pushes FAB up
              )
          ),
        ),
      ),
    );
  }
}

class AnotherScaffold extends StatelessWidget {
  final int level;
  final Widget body;
  final Color color;
  final GlobalKey<ScaffoldMessengerState> smState;

  AnotherScaffold({
    required this.level,
    required this.color,
    required this.body,
  }) : smState = GlobalKey<ScaffoldMessengerState>();
  /// This GlobalKey, given to a ScaffoldMessenger, will uniquely identify that
  /// ScaffoldMessenger.  We can then use it to show SnackBars.

  @override
  Widget build(BuildContext context) {
    return ScaffoldMessenger(
      key: smState, // use this key to show SnackBars on this ScaffoldMessenger
      child: Container( // unnecessary container just for border color / educational visibility
        margin: EdgeInsets.all(5),
        decoration: BoxDecoration(
          border: Border.all(color: color),
        ),
        child: Scaffold(
          body: body,
          floatingActionButton: Padding(
            padding: EdgeInsets.only(right: (level) * 40),
            child: FloatingActionButton(
              heroTag: GlobalKey(), // unique tag req'd for multiple FABs
              backgroundColor: color,
              child: Text('$level'),
              onPressed: () => // using smState key to show a SnackBar on that ScaffoldMessenger
                  smState.currentState!.showSnackBar(SnackBar(
                    duration: Duration(milliseconds: 2500),
                    content: Text('Snack level $level'),
                  )),
            ),
          ),
        ),
      ),
    );
  }
}
Psychotomimetic answered 26/8, 2022 at 0:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.