Reputation: 1796
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)
));
}
Upvotes: 8
Views: 12966
Reputation: 28100
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
Scaffolds
& ScaffoldMessengers
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'),
)),
),
),
),
),
);
}
}
Upvotes: 3
Reputation: 1796
Simply use multiple Scaffold
s. 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),
)),
),
);
}
)
);
Upvotes: 4
Reputation: 6033
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.
Upvotes: 16