When I was using the old api for showing the snackbar, the snackbar used to disappear when the user goes back to the previous page.
With the new ScaffoldMessenger.of(context).showSnackBar()
, the snackbar stays visible.
Also, when I add a snackbar action for removing the snackbar, pressing on the hide
action, does not hide the snackbar after going back to the previous page.
How can I remove the snackbar after the user leaves the current page?
void _successSnackbar(BuildContext context, String message) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(FontAwesomeIcons.checkCircle, color: Colors.green[400], size: 35),
const SizedBox(width: 15),
Flexible(child: Text(message, style: TextStyle(color: Colors.green[400], fontSize: 14.5))),
],
),
action: SnackBarAction(
label: 'Hide',
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
duration: Duration(seconds: 2),
),
);
Solution 1: moulte
Instead of passing the context and look for the ScaffoldMessenger pass directly the scaffoldMessenger:
void _successSnackbar(ScaffoldMessengerState scaffoldMessengerState , String message) {
scaffoldMessengerState.hideCurrentSnackBar();
scaffoldMessengerState.showSnackBar(
SnackBar(
content: Row(
children: [
Icon(FontAwesomeIcons.checkCircle, color: Colors.green[400], size: 35),
const SizedBox(width: 15),
Flexible(child: Text(message, style: TextStyle(color: Colors.green[400], fontSize: 14.5))),
],
),
action: SnackBarAction(
label: 'Hide',
onPressed: () {
scaffoldMessengerState.hideCurrentSnackBar();
},
),
duration: Duration(seconds: 2),
),
);
To close the snackBar automatically when the page is poped you will have to put a scaffoldMessengerState.hideCurrentSnackBar(); on a will pop callback or in a naavigator observer
Solution 2: chunhunghan
You can copy paste run full code below
Reason
from official document https://flutter.dev/docs/release/breaking-changes/scaffold-messenger
The ScaffoldMessenger
creates a scope in which all descendant Scaffolds register to receive SnackBars
, which is how they persist across these transitions
so In your case, root ScaffoldMessenger
provided by the MaterialApp
let all descendant Scaffolds
receive SnackBars
Solution
unless a new ScaffoldMessenger
scope is created further down the tree. By instantiating your own ScaffoldMessenger
, you can control which Scaffolds receive SnackBars
To auto hide SnackBar
when pop from second page, you can in second page wrap Scaffold
with ScaffoldMessenger
and in my example also need Builder
code snippet in Page2
Widget build(BuildContext context) {
return ScaffoldMessenger(
child: Scaffold(
...
child: Column(
children: [
Text("Second route"),
Builder(builder: (BuildContext context) {
return ElevatedButton(
onPressed: () {
_successSnackbar(context, "messge of page 2");
},
child: Text('show message'));
}),
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Page 1',
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Page2()),
);
},
child: Text('Go to page 2'))
],
),
),
);
}
}
class Page2 extends StatefulWidget {
@override
_Page2State createState() => _Page2State();
}
class _Page2State extends State<Page2> {
void _successSnackbar(BuildContext context, String message) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(Icons.check, color: Colors.green[400], size: 35),
const SizedBox(width: 15),
Flexible(
child: Text(message,
style:
TextStyle(color: Colors.green[400], fontSize: 14.5))),
],
),
action: SnackBarAction(
label: 'Hide',
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
duration: Duration(seconds: 20),
),
);
}
@override
Widget build(BuildContext context) {
return ScaffoldMessenger(
child: Scaffold(
appBar: AppBar(
title: Text("page 2"),
),
body: Center(
child: Column(
children: [
Text("Second route"),
Builder(builder: (BuildContext context) {
return ElevatedButton(
onPressed: () {
_successSnackbar(context, "messge of page 2");
},
child: Text('show message'));
}),
ElevatedButton(
onPressed: () {
Navigator.pop(
context,
);
},
child: Text('Pop'))
],
),
)),
);
}
}
Solution 3: flomaster
The reason is that we pass the wrong context
to ScaffoldMessenger.of()
method. We need context
of a child of Scaffold
.
You can wrap your widget in Builder
to get the right context
.
In my case it was not possible, so another solution is to use a GlobalKey
.
In your widget State
class add this line:
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
, use _scaffoldMessengerKey.currentState.showSnackBar()
to show snackbar, and wrap your Scaffold
with ScaffoldMessenger
:
ScaffoldMessenger(
key: _scaffoldMessengerKey,
child: Scaffold(...)
)