Snackbar not showing in flutter [duplicate] - flutter

This question already has answers here:
Scaffold.of() called with a context that does not contain a Scaffold
(15 answers)
Closed 2 years ago.
I can't seem to figure out why this isn't working. I'm supposed to get a snackbar when I tap the button. Any help? The code snippet on flutter api works fine tho
void main() => runApp(SnackBarDemo());
class SnackBarDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SafeArea(
child: Scaffold(
body: Center(
child: InkWell(
// When the user taps the button, show a snackbar.
onTap: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Tap'),
));
},
child: Container(
padding: EdgeInsets.all(12.0),
child: Text('Flat Button'),
),
),
)
)
)
);
}
}

You are calling showSnackbar in a widget on the same level of the Scaffold, so the context you are sending to this method does not contain a Scaffold,
Soltuion:
make this widget one level below the Scaffold:
void main() => runApp(SnackBarDemo());
class SnackBarDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SafeArea(
child: Scaffold(
body: Center(
child: Builder(
builder: (context)=>InkWell(
// When the user taps the button, show a snackbar.
onTap: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Tap'),
));
},
child: Container(
padding: EdgeInsets.all(12.0),
child: Text('Flat Button'),
),
),
),
)
)
)
);
}
}

Related

Flutter returns a black screen after navigating to other route

I'm writing a very simple coupon app, however I have a problem with navigating to different pages. I have two buttons, each of them is used to navigate to a different page, but Flutter is returning a black screen instead.
Here's the error: "There are multiple heroes that share the same tag within a subtree.".
Here's the video: https://streamable.com/id4nf2
Here's the code:
Hamburger
import 'package:flutter/cupertino.dart';
import 'package:makdolan_flutter/shared/home_screen_items.dart';
class Hamburger extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Hamburger'),
),
child: SafeArea(
child: HomeScreenItems(
imagePath: 'lib/assets/images/coupon_hamburger.png',
onClassicCouponPressed: () {
Navigator.pushNamed(context, '/classic-coupon');
},
onMailCouponPressed: () {},
),
),
);
}
}
Home Screen Items
import 'package:flutter/cupertino.dart';
double _sizedBoxHeight = 16.0;
double _paddingHorizontal = 8.0;
class HomeScreenItems extends StatelessWidget {
final String imagePath;
final VoidCallback onClassicCouponPressed;
final VoidCallback onMailCouponPressed;
HomeScreenItems({#required this.imagePath, this.onClassicCouponPressed, this.onMailCouponPressed});
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(imagePath),
SizedBox(height: _sizedBoxHeight),
Padding(
padding: EdgeInsets.symmetric(horizontal: _paddingHorizontal),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CupertinoButton(
child: Text('KLASYCZNY KUPON'),
color: CupertinoColors.activeBlue,
onPressed: onClassicCouponPressed,
),
SizedBox(height: _sizedBoxHeight),
CupertinoButton(
child: Text('KUPON Z MAILA'),
color: CupertinoColors.activeOrange,
onPressed: onMailCouponPressed,
)
],
),
)
],
);
}
}
Classic Coupon
import 'package:flutter/cupertino.dart';
class ClassicCoupon extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: Text('Test'),
);
}
}
I don't know the code you use to change the screen with the BottomNavigationBar but if you're preserving them with a CupertinoTabBar or PageView or anything and each page has its own CupertinoNavigationBar then by default they use a heroTag for animate from one route to another when using Nagivation.pop/push/etc
Try give them a hero tag to each one
class Hamburger extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Hamburger'),
heroTag: 'HamburgerTag' //give them a different heroTag to each one
),
child: SafeArea(
child: HomeScreenItems(
imagePath: 'lib/assets/images/coupon_hamburger.png',
onClassicCouponPressed: () {
Navigator.pushNamed(context, '/classic-coupon');
},
onMailCouponPressed: () {},
),
),
);
}
}
class ClassicCoupon extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(heroTag: 'HamburgerTag') //Give the same name so it makes the transition animation
child: Text('Test'),
);
}
}
Do the same to the other 2 pages (Jak and Lody).
class Jak extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Jak'),
heroTag: 'JakTag' //give them a different heroTag to each one
),
child: ...
);
}
}
class Lody extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Lody'),
heroTag: 'LodyTag' //give them a different heroTag to each one
),
child: ...
);
}
}
When the Navigator try to move to a new route it checks all the heroTags from the previous and the next route and start doing the animations (it cheks the one with the same name to start animating), if you don't define the tags it will use a _defaultHeroTag, and if there are 2 tags with the same value the error There are multiple heroes that share the same tag within a subtree. happens
You need to use Scaffold () for any page where you don't want black screen while navigation.
Its should be like return Scaffold (body: Column())
EDITED
Hero widget tag needs to be unique.
void main() {
runApp(
CupertinoApp(
routes: {
"Screen1": (context) => Screen1(),
"Screen2": (context) => Screen2(),
},
home: Screen1(),
),
);
}
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Hero(
tag: "Screen2",
child: GestureDetector(
onTap: () {
Navigator.pushNamed(context, "Screen2");
},
child: Image.asset("assets/images/banane.jpg")),
),
RaisedButton(
color: Colors.blue,
child: Text("go to screen2"),
onPressed: () => Navigator.of(context).pushNamed("Screen2"),
),
],
),
);
}
}
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Hero(
tag: "Screen2", child: Image.asset("assets/images/banane.jpg")),
Center(
child: RaisedButton(
color: Colors.red,
child: Text("go to screen1"),
onPressed: () => Navigator.of(context).pop(),
),
),
],
),
),
);
}
}
result click me
good luck buddy

How to set state after dispose() in flutter?

I have 2 pages, in the first page I have a button which is on click will open second page, in second page I have variable number = 999; so when I back to the first page I want to show the number print(number); or display on Text(number) How to do it with dispose() ?
#override
void dispose() {
super.dispose();
// send data to the first page
}
thanks for your answer
You can simply do this with the help of a navigator.
Navigator.push returns a Future that completes after calling
Navigator.pop on the Second Screen with the value passed.
e.x code:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Returning Data',
home: HomeScreen(),
));
}
class HomeScreen extends StatelessWidget {
String _resultNumber = '';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Returning Data Demo'),
),
body: Center(child: SelectionButton()),
);
}
}
class SelectionButton extends StatefulWidget {
#override
_SelectionButtonState createState() => _SelectionButtonState();
}
class _SelectionButtonState extends State<SelectionButton> {
String _resultNumber;
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
onPressed: () => _navigateAndDisplaySelection(context),
child: Text('Pick an option, any number!'),
),
Text(_resultNumber ?? ''),
]);
}
_navigateAndDisplaySelection(BuildContext context) async {
// Navigator.push returns a Future that completes after calling
// Navigator.pop on the Selection Screen.
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SelectionScreen()),
);
_resultNumber = result;
}
}
class SelectionScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Pick a number'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: () {
// Close the screen and return "Yep!" as the result.
Navigator.pop(context, '999');
},
child: Text('999 Number'),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: () {
// Close the screen and return "Nope!" as the result.
Navigator.pop(context, '500');
},
child: Text('550 Number'),
),
)
],
),
),
);
}
}
With dispose() you need override the back pressed, to do this wrap the Scaffold in WillPopScope widget.
return WillPopScope(
onWillPop: () {
_backPressed();
return Future.value(false);
},
child: Scaffold(
appBar: AppBar(
title: Text('your text'),
),
body: Center(),
),
);
void _backPressed() {
Navigator.pop(context, '999');
}

How to go from one screen to another clearing all the previous screens from stack?

In my Flutter project, I have three screens - Screen0, Screen1, Screen2. Now, from the screen0 , I can go to the screen1 with a button click, then from screen1 we can go to screen2 with a button. In screen2, I have a button which I have used to go back to screen0. As Screen0 is the initial screen, I want to clear all the previous screens when I come back to Screen0 and don't want to have any back option like in this image-
And here's my code-
main.dart
import 'package:flutter/material.dart';
import 'screen0.dart';
import 'screen1.dart';
import 'screen2.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context)=> Screen0(),
'/first': (context)=> Screen1(),
'/second': (context)=> Screen2(),
},
);
}
}
I have set all the routes in my main.dart file. Then in the Screen0, I have a button to go to screen1 like below-
screen0.dart
import 'package:flutter/material.dart';
class Screen0 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Text('Screen 0'),
),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
color: Colors.red,
child: Text('Go to screen 1'),
onPressed: (){
Navigator.pushNamed(context, '/first');
},
),
],
),
),
);
}
}
In the screen1, I have a button to go to screen2-
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Text('Screen 1'),
),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
color: Colors.red,
child: Text('Go to screen 2'),
onPressed: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return Screen2();
})
);
},
),
],
),
),
);
}
}
Now, in the screen2, i have the button to go to screen0 -
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Text('Screen 2'),
),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
color: Colors.red,
child: Text('Go to screen 0'),
onPressed: (){
Navigator.pop(context, Screen2);
Navigator.pushNamed(context, '/');
},
),
],
),
),
);
}
}
I have tried some solution like using Navigator.pushAndRemoveUntil given in the below link-
Flutter - Navigate to a new screen, and clear all the previous screens
But this solution didn't work for me.
So, it would be nice if someone help me out this code to solve the problem.
use pushNamedAndRemoveUntil
for example
Navigator.of(context).pushNamedAndRemoveUntil('/screen4', (Route<dynamic> route) => false);

route on tab leads to Black Screen

I defined the ontap method to navigate to SecondRoute class but it leads to the black screen.to resolve this issue I also replace the materialApp with Scaffold but didn't find luck
class FirstMain extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body:TabviewWidget(),
drawer: Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Item 2'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
],
),
),
);
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}
I expected to open SecondRoute but appears black screen

How to remove the second appbar in Flutter

I am trying to build a demo chat app with Flutter. After my main screen, I am using Navigator.push to go to the details screen.
Screenshot of problem:
build method of 1st screen:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Chat Thread App"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () {
Navigator.pushNamed(context, '/settings');
},
)
],
),
body: isLoading
? Center(
child: CircularProgressIndicator(),
)
: new ChatThreadListCard(messageThreads: _messageThreadLists, user: _user,),
);
}
code of Navigator.push method:
Navigator.push(context, MaterialPageRoute(
builder: (context) => ChatDetailsScreen(threadModel: new ThreadModel(
user.id,
user.fullName,
user.pic,
"otherId",
"otherName",
"otherPic",
post.threadId
)
),
),);
build method of 2nd screen, where the problem is produced:
return Scaffold(
appBar: AppBar(
title: Text("Chat demo"),
),
body: WillPopScope(
child: isLoading
? Center(
child: CircularProgressIndicator(),
)
: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: <Widget>[
SizedBox(
width: 300,
height: 300,
),
Column(
children: <Widget>[
buildChat(),
buildInput(),
],
)
],
),
onWillPop: onBackPress,
),
);
the problem turns out to be, i was creating a MaterialApp widget in scaffold's body. so, when the onTap method was called, the new screen was replaced insdie the MaterialApp's area. didnt replace the whole screen.
the trick was to remove the return new MaterialApp().
thanks everyone.
I'm guessing something isn't working right with where you're setting up the Material App?
app.dart:
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage());
}
}
home_page and second_page
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
#override
State createState() => HomePageState();
}
class HomePageState extends State<HomePage> with TickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Page'),
),
body: Container(
child: Center(child: RaisedButton(child: Text('Forward'), onPressed: () async {
await Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()));
},)),
));
}
}
class SecondPage extends StatefulWidget {
#override
State createState() => SecondPageState();
}
class SecondPageState extends State<SecondPage> with TickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second Page'),
),
body: Container(
child: Center(child: RaisedButton(child: Text('Backward'), onPressed: () {
Navigator.of(context).pop();
},)),
));
}
}
Which produces: