How do I set the height of SnackBar in Flutter?
I have this code from flutter documentation:
import 'package:flutter/material.dart';
void main() => runApp(const SnackBarDemo());
class SnackBarDemo extends StatelessWidget {
const SnackBarDemo({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SnackBar Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('SnackBar Demo'),
),
body: const SnackBarPage(),
),
);
}
}
class SnackBarPage extends StatelessWidget {
const SnackBarPage({super.key});
#override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
final snackBar = SnackBar(
content: const Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Action name',
onPressed: () {
// Some code to undo the change.
},
),
);
// Find the ScaffoldMessenger in the widget tree
// and use it to show a SnackBar.
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
child: const Text('Show SnackBar'),
),
);
}
}
And this is the result:
How do I prevent the snack bar to be that big?
I want the text and the action to be on the same line.
Thanks
You can put that button in content, like this:
onPressed: () {
final snackBar = SnackBar(
content: Row(
children: [
Expanded(
child: Text('Yay! A SnackBar!'),
),
TextButton(
onPressed: () {
if (mounted) { // <==== add this for hide snackBar
ScaffoldMessenger.of(context).hideCurrentSnackBar();
}
// Some code to undo the change.
},
child: Text(
'Action name',
style: TextStyle(color: Theme.of(context).primaryColor),
),
)
],
),
);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
This is automatic. If there is enough space for both message and action items, it will be shown on same line.
You can try changing the padding of Snackbar. You can try making it float but overall, it is there for responsiveness.
Here is how the framework calculates if items should be placed in single row or across multiple rows.
Related
I have a class extends StatelessWidget. When try call SnackBars in Scaffold like:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
splashRadius: 18,
icon: const Icon(Icons.thumb_up),
tooltip: 'Like the app',
onPressed: () {
final SnackBar snackBar = SnackBar(
duration: const Duration(seconds: 1),
content: const Text('Registered as you like the application.'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {},
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
),
],
title: const Text('My Application'),),
body: const Center(
child: Text("Hello World!"),)
);
}
}
when run the application show error like:
Exception has occurred.
FlutterError (No ScaffoldMessenger widget found.
MyApp widgets require a ScaffoldMessenger widget ancestor.
The specific widget that could not find a ScaffoldMessenger ancestor was:
MyApp
The ancestors of this widget were:
[root]
Typically, the ScaffoldMessenger widget is introduced by the MaterialApp at the top of your application widget tree.)
Wrap your app in an MaterialApp, like
void main() => runApp(MaterialApp(home: MyApp()));
use this code
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
GlobalKey<ScaffoldMessengerState> key = GlobalKey<ScaffoldMessengerState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ScaffoldMessenger(
key: key,
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
splashRadius: 18,
icon: const Icon(Icons.thumb_up),
tooltip: 'Like the app',
onPressed: () {
final snackBar = SnackBar(
content: Text(
"message",
// style: Theme.of(context).textTheme.regularWhite14,
),
duration: const Duration(seconds: 5),
action: SnackBarAction(
label: 'OK',
textColor: CustomColorScheme.whiteColor,
onPressed: () {},
),
);
key.currentState!.showSnackBar(snackBar);
},
),
],
title: const Text('My Application'),
),
body: const Center(
child: Text("Hello World!"),
)),
),
);
}
}
I found the answer from the youtube video as the following:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
final _scaffoldkey = GlobalKey<ScaffoldMessengerState>();
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
scaffoldMessengerKey: _scaffoldkey,
home: Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
splashRadius: 18,
icon: const Icon(Icons.thumb_up),
tooltip: 'Like the app',
onPressed: () {
final SnackBar snackBar = SnackBar(
duration: const Duration(seconds: 1),
content:
const Text('Registered as you like the application.'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {},
),
);
ScaffoldMessengerState? scaffold = _scaffoldkey.currentState;
scaffold!.showSnackBar(snackBar);
},
),
],
title: const Text('My Application'),
),
body: const Center(
child: Text("Hello World!"),
),
),
);
}
}
I found the solution for the problem but first: why the error happened?
It Fails because ScaffoldMessenger.of(context) doesn't find anything above this widget's context.
the solution to use Builder class as follows:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
actions: <Widget>[
Builder(
builder: (BuildContext context) {
return IconButton(
splashRadius: 18,
icon: const Icon(Icons.thumb_up),
tooltip: 'Like the app',
onPressed: () {
final SnackBar snackBar = SnackBar(
duration: const Duration(seconds: 1),
content: const Text(
'Registered as you like the application.'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {},
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
);
},
),
],
title: const Text('My Application'),
),
body: const Center(
child: Text("Hello World!"),
)),
);
}
}
I am new to flutter,Here I can't find why my snackbar is not showing in the ui.I tried exactly like the documentation .
Scaffold(
body: Center(
child: ElevatedButton(
child: const Text('Show SnackBar'),
onPressed: () {
final snackBar = SnackBar(
content: const Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {},
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
),
),
));
The problem is, that the call to ScaffoldMessenger.of(context).showSnackBar(snackBar) makes Flutter search the widget tree for ancestors of type ScaffoldMessenger and it's not able to find one.
This happens, because you pass in the BuildContext of the widget under which the Scaffold is declared. But it searches in the opposite direction.
One solution to this is, to wrap the call to ScaffoldMessenger.of in a Builder widget, wich introduces a new BuildContext. Then Flutter is able to find a ScaffoldMessenger in the widget tree and use it to show the SnackBar.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
home: Scaffold(
body: Center(
child: Builder(
builder: (context) {
return ElevatedButton(
child: const Text('Show SnackBar'),
onPressed: () {
final snackBar = SnackBar(
content: const Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {},
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
);
}
),
),
),
);
}
}
Check out the Builder documentation, it explains everything about those .of() methods: https://api.flutter.dev/flutter/widgets/Builder-class.html
This is a sample code for displaying SnackBar in flutter
import 'package:flutter/material.dart';
void main() => runApp(const SnackBarDemo());
class SnackBarDemo extends StatelessWidget {
const SnackBarDemo({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SnackBar Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('SnackBar Demo'),
),
body: const SnackBarPage(),
),
);
}
}
class SnackBarPage extends StatelessWidget {
const SnackBarPage({super.key});
#override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
final snackBar = SnackBar(
content: const Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
// Some code to undo the change.
},
),
);
// Find the ScaffoldMessenger in the widget tree
// and use it to show a SnackBar.
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
child: const Text('Show SnackBar'),
),
);
}
}
Photo:
Try the below code, You may missed forgot to wrap your scaffold
with MaterialApp();
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyWidget(),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: const Text('Show SnackBar'),
onPressed: () {
final snackBar = SnackBar(
content: const Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {},
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
),
),
);
}
}
I am using flutter for quite some time and recently use Get to implement state management.
I am facing a problem when opening a loading dialog 1st and then message dialog. Then I want to dismiss the loading dialog, but the message dialog is the one that keep dismiss.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class HomeController extends GetxController {
Future<void> openAndCloseLoadingDialog() async {
showDialog(
context: Get.overlayContext,
barrierDismissible: false,
builder: (_) => WillPopScope(
onWillPop: () async => false,
child: Center(
child: SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(
strokeWidth: 10,
),
),
),
),
);
await Future.delayed(Duration(seconds: 3));
Get.dialog(
AlertDialog(
title: Text("This should not be closed automatically"),
content: Text("This should not be closed automatically"),
actions: <Widget>[
FlatButton(
child: Text("CLOSE"),
onPressed: () {
Get.back();
},
)
],
),
barrierDismissible: false,
);
await Future.delayed(Duration(seconds: 3));
Navigator.of(Get.overlayContext).pop();
}
}
The above code dismisses the 2nd dialog, not the 1st dialog which what I want.
Can anyone give advice on this matter.
The reason why the AlertDialog is being dismissed instead of CircularProgressIndicator is because AlertDialog is on the top of the stack. What you can do here is to call Navigator.of(Get.overlayContext).pop(); to dismiss CircularProgressIndicator prior to displaying the AlertDialog.
Sample code based from the snippets provided.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
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> {
int _counter = 0;
final HomeController c = Get.put(HomeController());
void _incrementCounter() {
c.openAndCloseLoadingDialog();
// setState(() {
// _counter++;
// });
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class HomeController extends GetxController {
Future<void> openAndCloseLoadingDialog() async {
showDialog(
context: Get.overlayContext,
barrierDismissible: false,
builder: (_) => WillPopScope(
onWillPop: () async => false,
child: Center(
child: SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(
strokeWidth: 10,
),
),
),
),
);
await Future.delayed(Duration(seconds: 3));
// Dismiss CircularProgressIndicator
Navigator.of(Get.overlayContext).pop();
Get.dialog(
AlertDialog(
title: Text("This should not be closed automatically"),
content: Text("This should not be closed automatically"),
actions: <Widget>[
FlatButton(
child: Text("CLOSE"),
onPressed: () {
Get.back();
},
)
],
),
barrierDismissible: false,
);
// await Future.delayed(Duration(seconds: 3));
// Navigator.of(Get.overlayContext).pop();
}
}
I use it with bottomSheet(), but it will also work fine with Dialog. Just add an argument to Get.back(closeOverlays: true):
Get.bottomSheet(
WillPopScope(
onWillPop: () async {
Get.back(closeOverlays: true);
return false;
},
child: const QuestionWidget(),
);
Try to use closeOverlays param this way :
Get.back(closeOverlays: true);
Can I create something similar to Toasts in Flutter ?that that appear after an event and clickable toast
example of what i want :
here is an example from the docs,
class SnackBarDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SnackBar Demo',
home: Scaffold(
appBar: AppBar(
title: Text('SnackBar Demo'),
),
body: SnackBarPage(),
),
);
}
}
class SnackBarPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
onPressed: () {
final snackBar = SnackBar(
content: Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
// Some code to undo the change.
},
),
);
// Find the Scaffold in the widget tree and use
// it to show a SnackBar.
Scaffold.of(context).showSnackBar(snackBar);
},
child: Text('Show SnackBar'),
),
)
}
}
also i would recommend using this package as it has more customization and you don't have to worry about the scaffold context.
I am trying to do this kind of thing with Flutter :
https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F0B3T7oTWa3HiFcHBDaTlreHdVZGc%2Fitem-selection-selecting-items.mp4
var gestureTemp = GestureDetector(
onLongPress: (){
print('LONG PRESSED');
//CHANGE APPBAR
},
child: Padding(
padding: EdgeInsets.only(right:8),
child: Chip(
avatar: CircleAvatar(
backgroundColor: Colors.grey.shade800,
child: icon
),
label: Text(space.label, style: TextStyle(fontSize: 12, color:Colors.grey.shade800))
),
));
It detects the long press, but I don't know how to change my appbar...
Any ideas ?
EDIT: Here is what I do
var appBar1 = AppBar(...);
var appBar2 = AppBar(...);
var appBar = appBar1;
My appBar is displayed in my Scaffold.
On my GestureDetector :
onLongPress: (){
print('LONG PRESSED');
setState(() {
appBar = appBar2;
});
},
Welcome to StackOverflow!
The approach you described sounds quite right. Here is a standalone example so you can double check your code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: MyPage());
}
}
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
static final AppBar _defaultBar = AppBar(
title: Text('Inbox'),
leading: Icon(Icons.menu),
actions: <Widget>[Icon(Icons.search), Icon(Icons.more_vert)],
backgroundColor: Colors.black,
);
static final AppBar _selectBar = AppBar(
title: Text('1'),
leading: Icon(Icons.close),
actions: <Widget>[
Icon(Icons.flag),
Icon(Icons.delete),
Icon(Icons.more_vert)
],
backgroundColor: Colors.deepPurple,
);
AppBar _appBar = _defaultBar;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _appBar,
body: Center(
child: RaisedButton(
child: Text('Switch!'),
onPressed: () {
setState(() {
_appBar = _appBar == _defaultBar
? _selectBar
: _defaultBar;
});
},
),
),
);
}
}