How could use SnackBars within the Scaffold in stateless widget? - flutter

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!"),
)),
);
}
}

Related

Snackbar is not showing

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);
},
),
),
);
}
}

Unable to navigate from GetX Dialog to another screen

I have follow dialog box. When I click 'Next' I want it to navigate to GamePage() screen. But unfortunately it doesn't work.
Following is the GamePage Widget
class GamePage extends StatelessWidget {
final homeCtrl = Get.find<HomeController>();
GamePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF8fb1ca),
body: SafeArea(
child: ListView(
children: [
Padding(
padding: EdgeInsets.all(3.0.wp),
child: Row(
children: [
IconButton(
onPressed: () {
Get.back();
},
icon: const Icon(Icons.arrow_back),
),
],
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 4.0.wp),
child: Column(
children: [
SizedBox(
height: 2.0.wp,
),
Center(
child: Text(
'What ${homeCtrl.currentWord.first.wordtype} is this?',
style: TextStyle(
fontSize: 18.0.sp,
color: Colors.grey[800],
),
),
),
SizedBox(height: 10.0.wp),
WordsWidget(currentWord: homeCtrl.currentWord.first),
],
),
),
],
),
),
);
}
}
Following is the Word Widget being called from GamePage Widget
class WordsWidget extends StatelessWidget {
final currentWord;
WordsWidget({Key? key, this.currentWord}) : super(key: key);
final homeCtrl = Get.find<HomeController>();
#override
Widget build(BuildContext context) {
// var currentWord = homeCtrl.nextWord();
var shuffleword = [].obs;
shuffleword.addAll(homeCtrl.shuffleWord(currentWord.word));
TextToSpeech tts = TextToSpeech();
String language = 'en-US';
tts.setLanguage(language);
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
print('pressed here');
Get.defaultDialog(
title: 'Go to next page',
content: Container(
child: Column(
children: [
Text('You are about to move to another screen'),
ElevatedButton.icon(
onPressed: () {
Get.to(() => GamePage());
},
icon: Icon(
Icons.arrow_right,
),
label: Text('Go'))
],
),
));
},
child: Text('Open Dialog')),
],
);
}
}
Get.back() is working but not Get.to
Try
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return const GamePage();
},
),
);
},
child: Text("Next Word"),
)
Try this code -
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_memory/next_page.dart';
import 'package:image_picker/image_picker.dart';
void main() {
//check getMaterialApp is used
runApp(const GetMaterialApp(
title: 'Temp',
home: const MyApp(),
));
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Image Picker'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
print('pressed here');
Get.defaultDialog(
title: 'Go to next page',
content: Container(
child: Column(
children: [
Text('You are about to move to another screen'),
ElevatedButton.icon(
onPressed: () {
Get.to(() => NextPage());
},
icon: Icon(
Icons.arrow_right,
),
label: Text('Go'))
],
),
));
},
child: Text('Open Dialog')),
),
);
}
}
and next page is -
import 'package:flutter/material.dart';
class NextPage extends StatefulWidget {
const NextPage({ Key? key }) : super(key: key);
#override
State<NextPage> createState() => _NextPageState();
}
class _NextPageState extends State<NextPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Next Page'),
),
body: Container(
child: Center(
child: Text("this is next page"),
),
),
);
}
}
And yes, you need to insure that you are using 'GetMaterialApp'.
If you want to use GetX navigation system, you should wrap your application in a GetMaterialApp instead of MaterialApp.
So in your main use this:
class GetxApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
home: HomePage(),
);
}
}
Instead of this:
class NormalApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}

How can i navigate to another page without popping the drawer in flutter?

I want to transition between pages while having the drawer on top of everything so that the page changes and the drawer doesn't pop, it must always stay on top of everything unless I want to hide it.
Use state managers like Redux, Bloc, Provider.
Create a separate widget for the drawer and just use in anywhere you need to.
Example: Manage the Drawer State with a Provider
Controller
import 'package:flutter/material.dart';
class MenuController extends ChangeNotifier {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
GlobalKey<ScaffoldState> get scaffoldKey => _scaffoldKey;
void controlMenu() {
if (!_scaffoldKey.currentState!.isDrawerOpen) {
_scaffoldKey.currentState!.openDrawer();
}
}
}
Adding State Management to the Widget tree
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.light().copyWith(
scaffoldBackgroundColor: bgColor,
textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme)
.apply(bodyColor: Colors.white),
canvasColor: secondaryColor,
),
debugShowCheckedModeBanner: false,
home: MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MenuController(),
),
],
child: HomeScreen(),
),
);
}
}
Creating The Drawer Widget
class SideMenu extends StatelessWidget {
const SideMenu({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
children: [
DrawerHeader(
child: Image.asset("assets/images/logo.png"),
),
DrawerListTile(
title: "Dashboard",
svgSrc: "assets/icons/menu_dashbord.svg",
press: () {},
),
DrawerListTile(
title: "Pages",
svgSrc: "assets/icons/page.svg",
press: () {},
),
DrawerListTile(
title: "Applications",
svgSrc: "assets/icons/application.svg",
press: () {},
),
DrawerListTile(
title: "UI Components",
svgSrc: "assets/icons/ui.svg",
press: () {},
),
DrawerListTile(
title: "Widgets",
svgSrc: "assets/icons/widget.svg",
press: () {},
),
DrawerListTile(
title: "Forms",
svgSrc: "assets/icons/forms.svg",
press: () {},
),
DrawerListTile(
title: "Charts",
svgSrc: "assets/icons/chart.svg",
press: () {},
),
DrawerListTile(
title: "Settings",
svgSrc: "assets/icons/menu_setting.svg",
press: () {},
),
],
),
);
}
}
class DrawerListTile extends StatelessWidget {
const DrawerListTile({
Key? key,
// For selecting those three line once press "Command+D"
required this.title,
required this.svgSrc,
required this.press,
}) : super(key: key);
final String title, svgSrc;
final VoidCallback press;
#override
Widget build(BuildContext context) {
return ListTile(
onTap: press,
horizontalTitleGap: 0.0,
leading: SvgPicture.asset(
svgSrc,
color: primaryColor,
height: 16,
),
title: Text(
title,
style: TextStyle(color: primaryColor),
),
);
}
}
Use of Drawer in your pages
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<MyAboutPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
key: context.read<MenuController>().scaffoldKey,
appBar: AppBar(
title: Text('About Page'),
),
drawer: SideMenu(),
);
}
}
Control state if you want to open drawer on click
IconButton(
icon: Icon(Icons.menu),
onPressed: context.read<MenuController>().controlMenu,
),

Flutter navigator not working : OnPress not showing second screen

This is my code don't know why its not moving to screen 2 on button press:
Am beginner in dart/flutter please help. On press of button it's not showing screen2
//main.dart
import 'package:flutter/material.dart';
import 'screen_1.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Screen1(),
);
}
}
//screen_1.dart
import 'package:flutter/material.dart';
import 'screen_2.dart';
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen 1'),
),
body: Container(
child: Center(
child: RaisedButton(
color: Colors.pink,
child: Text(
'Go to Screen 2',
style: TextStyle(
color: Colors.white,
),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return Screen2();
},
),
);
},
),
),
),
);
}
}
//screen_2.dart
import 'package:flutter/material.dart';
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text('Screen 2'),
),
body: Center(
child: RaisedButton(
color: Colors.blue,
child: Text('Go Back To Screen 1'),
onPressed: () {},
),
),
);
}
}
onPressed of Screen1 class should show Screen2 but it's not happening.
I tried this and it does move to Screen2:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Screen1(),
);
}
}
//screen_1.dart
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen 1'),
),
body: Container(
child: Center(
child: RaisedButton(
color: Colors.pink,
child: Text(
'Go to Screen 2',
style: TextStyle(
color: Colors.white,
),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return Screen2();
},
),
);
},
),
),
),
);
}
}
//screen_2.dart
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text('Screen 2'),
),
body: Center(
child: RaisedButton(
color: Colors.blue,
child: Text('Go Back To Screen 1'),
onPressed: () {},
),
),
);
}
}
This may be because StatelessWidget is part of material.dart yet you failed to import it in the first dart file.

How do I access BuildContext outside of a stateful or stateless widget?

I created a class that extends the AppBar class in Flutter so I can reuse it whenever I need it.
My problem is how do I access the Stateful/Stateless widget build context?
class AppBarLayout extends AppBar {
static final AppController _appController = new AppController();
final GlobalKey<ScaffoldState> _scaffoldKey;
final String appBarTitle;
AppBarLayout(this.appBarTitle,this._scaffoldKey): super(
title: Text(appBarTitle),
leading: IconButton(
onPressed: () => _scaffoldKey.currentState.openDrawer(),
iconSize: 28,
icon: Icon(Icons.menu,color: Colors.white),
),
actions: <Widget>[
IconButton(
onPressed: () => _appController.signOut().then((_) {
_appController.navigateTo(context, new GoogleSignView());
}),
icon: Icon(Icons.account_box),
padding: EdgeInsets.all(0.0),
),
],
);
}
You would need to wrap your Scaffold in a Staless or Stateful widget, so you can get the context, e.g.
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> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBarLayout(GlobalKey(debugLabel: 'someLabel'), appBarTitle: 'The Title', context: context,),
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.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class AppBarLayout extends AppBar {
final GlobalKey<ScaffoldState> _scaffoldKey;
final String appBarTitle;
final BuildContext context;
AppBarLayout(this._scaffoldKey, {this.appBarTitle, this.context}): super(
title: Text(appBarTitle),
leading: IconButton(
onPressed: () => _scaffoldKey.currentState.openDrawer(),
iconSize: 28,
icon: Icon(Icons.menu,color: Colors.white),
),
actions: <Widget>[
IconButton(
onPressed: () {
print('Button pressed');
},
icon: Icon(Icons.account_box),
padding: EdgeInsets.all(0.0),
),
],
);
}
Here I'm using a very similar Widget of what you have.