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 am new to Flutter, i am creating my login Screen and i want to show a Snackbar in my screen in case of OnPress of Raised Button, But i am unable to show this message to my app. How to resolve this Problem.
I also attached the error description in following to understand the main thing, i don't know how to manage it.
I used Material Button and Flat Button instead of Raised Button but Problem did not resolved.
Image
Code
import 'package:flutter/material.dart';
import 'package:flutter_hello/Signup.dart';
main() {
runApp(MaterialApp(
// theme: ThemeData(primarySwatch: Colors.red, brightness: Brightness.light),
title: "Umar",
home: new Login(),
));
}
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(28),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FlutterLogo(
size: 150,
colors: Colors.red,
),
TextFormField(
obscureText: false,
// keyboardType: TextInputType.number,
decoration: InputDecoration(
prefixIcon: Icon(Icons.person, color: Colors.grey),
hintText: 'Email',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
),
TextFormField(
obscureText: true,
obscuringCharacter: "*",
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outline, color: Colors.grey),
hintText: 'Password',
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
),
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Colors.red)),
color: Colors.red,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(40, 8, 40, 8),
onPressed: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text("Sending Message"),
));
},
child: Text(
"Login",
style: TextStyle(
fontSize: 20.0,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Don't have account?"),
Padding(
padding: EdgeInsets.all(4),
),
GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => Signup())),
child: Text(
"SignUp",
style: TextStyle(
color: Colors.red, fontWeight: FontWeight.w600),
),
),
],
)
],
),
),
),
);
}
}
Error
Here is my Error Message shown in Android studio.
Handler: "onTap"
Recognizer:
TapGestureRecognizer#07f5f
════════════════════════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by gesture ═══════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
Scaffold.of() called with a context that does not contain a Scaffold.
No Scaffold ancestor could be found starting from the context that was passed to Scaffold.of(). This usually happens when the context provided is from the same StatefulWidget as that whose build function actually creates the Scaffold widget being sought.
There are several ways to avoid this problem. The simplest is to use a Builder to get a context that is "under" the Scaffold. For an example of this, please see the documentation for Scaffold.of():
https://api.flutter.dev/flutter/material/Scaffold/of.html
A more efficient solution is to split your build function into several widgets. This introduces a new context from which you can obtain the Scaffold. In this solution, you would have an outer widget that creates the Scaffold populated by instances of your new inner widgets, and then in these inner widgets you would use Scaffold.of().
A less elegant but more expedient solution is assign a GlobalKey to the Scaffold, then use the key.currentState property to obtain the ScaffoldState rather than using the Scaffold.of() function.
The context used was: Login
state: _LoginState#f029c
When the exception was thrown, this was the stack:
#0 Scaffold.of (package:flutter/src/material/scaffold.dart:1451:5)
#1 _LoginState.build.<anonymous closure> (package:flutter_hello/main.dart:57:28)
#2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:992:19)
#3 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1098:38)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:184:24)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#07f5f
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(196.3, 545.3)
finalLocalPosition: Offset(81.3, 20.2)
button: 1
sent tap down
Do it like this,
void showInSnackBar(String value) {
FocusScope.of(context).requestFocus(new FocusNode());
_scaffoldKey.currentState?.removeCurrentSnackBar();
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(
value,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
backgroundColor: Colors.blue,
duration: Duration(seconds: 3),
));
}
The Problem is that the context that you are using does not contain a Scaffold, because the Scaffold is created after the context. You can fix this problem by using a Builder-Widget to wrap your content.
Like this:
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context) {
// return your body here
},
),
);
}
}
Related
I am just learning and can't wrap my head around something.
I am building a simple app but the app requires the first thing shown is a splash screen of some sorts.
Upon tapping the only button on the SplashScreen, ideally it would load the rest of the app however I also want my app inside to work around a bottomNavBar.
I have done the Bottom Navigation Bar on my own and it works so I can cycle between my pages, but my main.dart is pointing towards my Splash_Screen. Where as in the Nav model I am using, main points to the Nav.dart file.
How do I get my app to launch in this sequence: Splash_Screen --> when buttom tapped --> go inside where Bottom Navigation Bar will be leading to it's respective 3 pages.
Thanks in advance.
EDIT: CODE
main.dart
import 'package:flutter/material.dart';
import 'package:offroad/screens/splash_screen.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen(),
);
}
}
SplashScreen.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:offroad/screens/home_screen.dart';
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
Color mainColor = Color(0xFFF1330A);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/splash.jpg'),
fit: BoxFit.cover,
),
),
child: Container(
color: Colors.black54,
child: Stack(
children: <Widget>[
Container(),
Positioned(
bottom: 90,
child: Container(
width: MediaQuery.of(context).size.width,
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => Home(),
),
);
},
child: Container(
margin: EdgeInsets.symmetric(horizontal: 80),
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: mainColor,
),
child: Center(
child: Text(
'Entrar',
style: TextStyle(
fontSize: 25,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
),
Positioned(
bottom: 230,
child: Container(
width: MediaQuery.of(context).size.width,
child: Text(
'Tu mundo 4x4\n empieza aqui!',
style: GoogleFonts.amiri(
height: 1.2,
textStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 40,
),
),
textAlign: TextAlign.center,
),
),
),
],
),
),
),
);
}
}
Once my splash page loads and the button is tapped I want it to now go inside and load this, which is what I had working seperately but with the main.dart pointing to the "nav.dart"
Not sure what you are trying to achieve exactly but you should use the routes or onGenerateRoute in MaterialApp to define your routes.
In that case you can get rid of the home and set the initial route to Splash Screen and the default as nav.
You can then navigate to your tab component using a named route defined in your routes or onGenerateRoute.
I'm just a student try to make school project and don't know codding very well. I stuck in somewhere, everything was just fine but then flutter stopped updating my codes in home.dart file. I update my codes change some variables but it does not working, it uses the old code. But other .dart files is working and getting update. I sent the code, I changed AppBar part but its not updating. and I'm getting an error from old code.
class _HomeState extends State<Home> {
List<PopularTourModel> popularTourModels = new List();
List<CountryModel> country = new List();
#override
void initState() {
country = getCountrys();
popularTourModels = getPopularTours();
super.initState();
}
#override
void didUpdateWidget(oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget != widget) {
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Samsun Tur'),
actions: [
],
elevation: 0.0,
),
body: SingleChildScrollView(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"BETA",
style: TextStyle(
fontSize: 28,
color: Colors.black54,
fontWeight: FontWeight.w600),
),
SizedBox(
height: 8,
),
Text(
"Tarihi Mekanlar:",
style: TextStyle(
fontSize: 20,
color: Colors.black54,
fontWeight: FontWeight.w600),
),
SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
print('ÇALIŞTI, mı acaba?');
},
child: Container(
height: 240,
child: ListView.builder(
itemCount: country.length,
shrinkWrap: true,
physics: ClampingScrollPhysics(),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index)
{
return CountryListTile(
label: country[index].label,
countryName: country[index].countryName,
noOfTours: country[index].noOfTours,
rating: country[index].rating,
imgUrl: country[index].imgUrl,
);
}),
),
),
SizedBox(
height: 8,
),
Text(
"Size en yakın yerler!",
style: TextStyle(
fontSize: 20,
color: Colors.black54,
fontWeight: FontWeight.w600),
),
SizedBox(
height: 16,
),
ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: popularTourModels.length,
itemBuilder: (context, index) {
return PopularTours(
desc: popularTourModels[index].desc,
imgUrl: popularTourModels[index].imgUrl,
title: popularTourModels[index].title,
price: popularTourModels[index].price,
rating: popularTourModels[index].rating,
mekan: popularTourModels[index].mekan,
desclong: popularTourModels[index].desclong,
latitude: popularTourModels[index].latitude,
longitude: popularTourModels[index].longitude,
distanceToUser: popularTourModels[index].distanceToUser,
);
})
],
),
),
),
);
}
}
════════ Exception caught by gesture ═══════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
Could not find a generator for route RouteSettings("lib/views/home", null) in the _WidgetsAppState.
Make sure your root app widget has provided a way to generate
this route.
Generators for routes are searched for in the following order:
1. For the "/" route, the "home" property, if non-null, is used.
2. Otherwise, the "routes" table is used, if it has an entry for the route.
3. Otherwise, onGenerateRoute is called. It should return a non-null value for any valid route not handled by "home" and "routes".
4. Finally if all else fails onUnknownRoute is called.
Unfortunately, onUnknownRoute was not set.
When the exception was thrown, this was the stack:
#0 _WidgetsAppState._onUnknownRoute.<anonymous closure> (package:flutter/src/widgets/app.dart:1175:9)
#1 _WidgetsAppState._onUnknownRoute (package:flutter/src/widgets/app.dart:1190:6)
#2 NavigatorState._routeNamed (package:flutter/src/widgets/navigator.dart:3388:36)
#3 NavigatorState.pushNamed (package:flutter/src/widgets/navigator.dart:3425:20)
#4 NavigatorState.popAndPushNamed (package:flutter/src/widgets/navigator.dart:3479:12)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#39fcc
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(26.9, 51.3)
finalLocalPosition: Offset(26.9, 27.3)
button: 1
sent tap down
════════════════════════════════════════════════════════════════════════════════════════════════════
Lost connection to device.
There is a simple structure with a Bottom Modal Sheet that contains an input TextField to add a Text into a List and a button to submit the value.
When the text field is focused, it automatically opens the soft keyboard that covers the submit button.
When I dismiss the keyboard in order to press the button, the value in the text field is still visible but the sent value is null.
If I press the button without dismissing the keyboard, the value is correctly sent.
The question is: how can I dismiss the keyboard and still be able to send the typed value after pressing the submit button?
Here is the code:
1) On the main screen, the floating action button shows the modal bottom sheet.
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => AddTaskScreen(),
},
2) On the AddTaskScreen class, there is a Column with the content of the modal bottom sheet inside a container.
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Add your next Task',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
TextField(
textAlign: TextAlign.center,
autofocus: true,
onChanged: (value) {
newTaskTitle = value;
},
),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'ADD',
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
color: Colors.lightBlueAccent,
onPressed: () {
print(newTaskTitle);
},
),
],
),
),
In this simplified version, the value from the TextField is printed in the console when pressing the button. If I press the button without hiding the keyboard it works fine; If I hide the keyboard it passes a null value.
Thanks in advance for your help.
I had the same issue, I resolved it by simply converting called class to extend StatefullWidget instead of StatelessWidget.
In your case converting class AddTaskScreen() to extend StatefullWidget.
Okay, the easiest way to do this is by supplying a TextEditingController to the child class.
So for your case, you can first create a TextEditingController in the Parent Class, then pass it to the child class. And in the TextField inside child class set the controller: The controller you have passed
Parent Class.....
//// other codes ////
TextEditingController textEditingController = TextEditingController();
return Scafold{
FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => AddTaskScreen(textEditingController),
},
};
And in the child class
class ChildClass extends StatelessWidget(
final TextEditingController textEditingController;
ChildClass({this.textEditingController});
///then inside build method///
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Add your next Task',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
TextField(
textAlign: TextAlign.center,
autofocus: true,
controller: textEditingController, /// here add the controller
onChanged: (value) {
newTaskTitle = value;
},
),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'ADD',
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
color: Colors.lightBlueAccent,
onPressed: () {
print(newTaskTitle);
},
),
],
),
),
Now you can access what was written in the TextField by simply calling textEditingController.value.text from anywhere between these two classes.
Just moving the declarations of text controllers work for me.
....
class _AddPlaceScreenState extends State<AddPlaceScreen> {
final controllerTittlePlace = TextEditingController();
final controllerDescriptionPlace = TextEditingController();
final controllerLocationPlace = TextEditingController();
....
So my question is: Is it posible to run function from state in other script?
E.g:
I have 2 scripts, lets say i have main.dart and loadingScreen.dart
main.dart
//...
OutlineButton(
child: Text("Status", style: TextStyle(color: Colors.white, fontSize: 17), textAlign: TextAlign.center,),
borderSide: BorderSide(color: Colors.grey[400]),
onPressed: () async {
Navigator.pushNamed(context, '/loadingScreen', arguments: {"text": "Checking\nstatus"});
//...
// <--- Here I want to run updateLoadingText from loadingScreen.dart
Navigator.pop(context);
}
),
//...
loadingScreen.dart
class _LoadingScreenState extends State<LoadingScreen> {
Map data = {};
String loadingText;
updateLoadingText(newText){
//...
}
#override
Widget build(BuildContext context) {
//...
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
backgroundColor: Colors.grey[800],
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SpinKitCubeGrid(
color: Colors.white,
size: 80,
),
SizedBox(height: 24,),
Text(
loadingText,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 35,
),
)
],
),
)
),
);
}
}
Is it possible to run updateLoadingText in main.dart?
Thx for help!
You can do this by passing a key to the widget and then using the key to access the widget's state. (You will need to make the state class public to do this.)
main.dart
// In the class fields
final loadingScreenKey = GlobalKey<LoadingScreenState>();
// Where you build LoadingScreen
LoadingScreen(
key: loadingScreenKey,
// ...
),
// Button code
OutlineButton(
// ...
onPressed: () async {
// ...
loadingScreenKey.currentState.updateLoadingText(...),
},
),
DISCLAIMER
Calling a state method from another widget directly like this is generally considered bad form since this will produce highly coupled and decentralized spaghetti code. As such, you should instead look into a system where you notify the widget to change via an impartial service. There are a lot of ways to do this, such as with an event bus, a ChangeNotifier, or with a state management library like provider or flutter_bloc.
I would like to achieve Android like TextField validation in Flutter.
I tried the TextField docs of Flutter and the InputDecoration has a property of errorText but is displays error at the bottom of textfield. I want to achieve something like below in Flutter
You can use a TextFormField along with a custom Tooltip inside a Stack to achieve this effect. In the decoration property for the TextFormField you can use the suffixIcon property of the InputDecoration class to pass the error icon.
And you can use a bool variable to show/hide the tooltip message when the validation error occurs.
Example code for the TextFormField:
TextFormField(
decoration: InputDecoration(
//Set the different border properties for a custom design
suffixIcon: IconButton(
icon: Icon(Icons.error, color: Colors.red),
onPressed: () {
setState(() {
showTooltip = !showTooltip; //Toggles the tooltip
});
},
),
),
validator: (String value) {
if(value.isEmpty) {
setState(() {
showTooltip = true; //Toggles the tooltip
});
return "";
}
}
);
You can wrap the above code along with your custom tooltip widget code with a Stack to achieve the error tooltip effect.
Below is a quick working example. You can design your own custom tooltip widget and use it in your code.
Example:
class LoginAlternate extends StatefulWidget {
#override
_LoginAlternateState createState() => _LoginAlternateState();
}
class _LoginAlternateState extends State<LoginAlternate> {
GlobalKey<FormState> _formKey = GlobalKey();
bool showTooltip = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey,
body: Container(
padding: EdgeInsets.symmetric(
horizontal: 100,
vertical: 100
),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Stack(
alignment: Alignment.topRight,
overflow: Overflow.visible,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderSide: BorderSide.none
),
suffixIcon: IconButton(
icon: Icon(Icons.error, color: Colors.red,),
onPressed: () {
setState(() {
showTooltip = !showTooltip;
});
},
),
hintText: "Password"
),
validator: (value) {
if(value.isEmpty) {
setState(() {
showTooltip = true;
});
return "";
}
},
),
Positioned(
top: 50,
right: 10,
//You can use your own custom tooltip widget over here in place of below Container
child: showTooltip
? Container(
width: 250,
height: 50,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all( color: Colors.red, width: 2.0 ),
borderRadius: BorderRadius.circular(10)
),
padding: EdgeInsets.symmetric(horizontal: 10),
child: Center(
child: Text(
"Your passwords must match and be 6 characters long.",
),
),
) : Container(),
)
],
),
RaisedButton(
child: Text("Validate"),
onPressed: () {
_formKey.currentState.validate();
},
),
],
),
),
),
);
}
}
I think you are talking about ToolTip
You can use this library or Go through Flutter doc
new Tooltip(message: "Hello ToolTip", child: new Text("Press"));
you can use the library super_tooltip #
You can customize this type of error by using overlay widget. Error can be shown with overlay widget and that error icon can be changed using inputDecoration of TextField.
Here is the link for understanding overlay widget--
https://medium.com/saugo360/https-medium-com-saugo360-flutter-using-overlay-to-display-floating-widgets-2e6d0e8decb9