Need help in resolving an exception saying "Bad state: Tried to read a provider that threw during the creation of its value."
The Error!
The following _CastError was thrown building Consumer<CartItemCounter>(dirty, dependencies:
[_InheritedProviderScope<CartItemCounter?>]):
Null check operator used on a null value
The relevant error-causing widget was:
Consumer<CartItemCounter>
Consumer:file:///C:/Users/USER/OneDrive/WebProjects/FLUTTER/PRACTICE/UDEMY/Build_eCommerce_App/e_shop/lib/Sto re/storeHome.dart:69:30
Model File: cartItemCounter.dart
#Am I doing the wrong implementation of the null operator here in this file?
import 'package:e_shop/Config/config.dart';
import 'package:flutter/foundation.dart';
class CartItemCounter extends ChangeNotifier {
int _counter = EcommerceApp.sharedPreferences
!.getStringList(EcommerceApp.userCartList)!
.length -
1;
int get count => _counter;
Future<void> displayResult() async {
int _counter = EcommerceApp.sharedPreferences!
.getStringList(EcommerceApp.userCartList)!
.length -
1;
await Future.delayed(
Duration(milliseconds: 100),
() {
notifyListeners();
},
);
}
}
#Implementation: Consuming the Model on my home page is as below.
Positioned(
child: Stack(
children: [
Icon(
Icons.ac_unit,
size: 20.0,
color: Colors.greenAccent,
),
Positioned(
top: 3.0,
bottom: 4.0,
left: 4.0,
child: Consumer<CartItemCounter>(
builder: (context, counter, child) => Text(
EcommerceApp.sharedPreferences!
.getStringList(EcommerceApp.userCartList)!
.length -
1 as String,
//'${EcommerceApp.sharedPreferences!.getStringList
// (EcommerceApp.userCartList)!.length - 1}',
style: TextStyle(
color: Colors.white,
fontSize: 12.0,
fontWeight: FontWeight.w500),
),
),
)
You used the ! operator a lot. Your compiler is warning you, that in some cases the variables you rely on hold no value, and instead of listening to your compiler and thinking about what you might want your program to do when that is the case, you basically yelled "oh shut the f*** up" and slammed the door by putting in a !. This ! did not solve a single problem though. The variable might still not hold a value, you only yelled at the one tool trying to help you with it to stop doing so. Your program still has unfixed problems, you just suppressed early reporting and now you got late reporting... aka a crash.
Remove all ! used to suppress warnings. Then look at each warning. Why could that variable be null here?
Why is EcommerceApp.sharedPreferences nullable? Well, it's your code, you decide. If you want to keep it as nullable, what do you want to do if it's null?
getStringList returns a list, but it can also return null. For example when you start your app for the first time, there will be no data. How do you want to handle that?
You have to answer those questions, not just tell your compiler to shut up about them. And I cannot answer them for you. It's your program. You decide.
So as an example, instead of this horrible line:
EcommerceApp.sharedPreferences!
.getStringList(EcommerceApp.userCartList)!
.length - 1;
That will fail if either sharedPreferences is null or getStringList returns null, you should think and make decisions what should happen if that is the case. I know the code can be more conscise, but this is a learning exercise, so we will do it the verbose way:
int UserCartListCount() {
final source = EcommerceApp.sharedPreferences;
if(source == null) {
// we have no way to read our data, so what now?
return 0;
}
final cartList = source.getStringList(EcommerceApp.userCartList);
if(cartList == null) {
// there was no cart previously saved
return 0;
}
// this is weird... but it's your logic, I have no idea why that would be correct
return cartList.length - 1;
}
And then, you call this function instead in your code.
Related
I have upgraded my project to null-safety and it's a bit confusing, because if I wrap into a
if(someObject.field != null) {
doSomething(someObject.field); // error, can't assign String? to String
}
a method call that requires a not-nullable property and my variable I am trying to pass is nullable, then I get a type error that I cannot assign a nullable value to a not-nullable type.
But when I do
String? someObjectField = someObject!.field;
if(someObjectField != null) {
doSometing(someObjectField); // Can assign
}
it works as expected.
doSomething = (String foo) {}
For example:
class Person {
final String name;
Person(this.name);
}
Function test = () {
Map<String, String?> pers = {
'name': 'John',
};
if(pers['name'] != null) {
Person(pers['name']); // Error, even if it will be never executed
Person(pers['name'] as String); // no error
Person(pers['name']!); // no error
}
};
And if I do something like:
if (widget.event != null && widget.event.featuredImage != null)
Then it complains in the second statement that the receiver (widget.event) can be null and I need to use !, but the second statemen should never execute and it should never cause a runtime exception.
So I need to modify it to:
if (widget.event != null && widget.event!.featuredImage != null)
But then when I try to use a nested widget inside Flutter, then even if I use the if as wrapper I still need to add ! everywhere
Stack(
children: [
// Add Container with image only when event and futured image are not null
if (widget.event != null && widget.event!.featuredImage != null) ...[
Container(
height: 250,
decoration: BoxDecoration(
color: Colors.transparent,
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(widget.event!.featuredImage!), // here i Need to use ! to satisfy the compiler
),
),
),
],
],
)
Alternatively I can extract the variable into another one String? image = widget.event!.featuredImage then modify the if statement to if(image != null) and use the widget like NetworkImage(image) which works as expected.
Compared to TypeScript which detects if I checked for null in a condition above, this makes no sense to me.
Longstory short, even if I check for null value to render/not-render a component, I still need to use !.
Is there something obvious that I am missing?
Thank you in advance
Since you compared it to TypeScript, yes, you are missing something.
Typescript is a mess that works in a very limited environment and "works" is grossly overstated. For example you could write a method in typescript that takes a string and then at runtime find out it's not actually a string, surprise, it's a completely different type. The joys of JavaScript. Calling TypeScript "type safe" is correct compared to JS and ridiculous compared to actually compiled languages.
So you are missing the fact that the Dart compiler can guarantee that something is not null once you checked it. To do that, it needs additional constraints. For example, you could have getters that do not return the same value every time you call them. Your call for example could easily return different values between the first and second call, depending on the code of the getter. Or you could use inheritance and polymorphism to build some even more problematic constructs. See here for an interesting example. So you need to have a local variable, that is guaranteed to have the same value, unless explicitely changed.
Your example of if (widget.event != null && widget.event.featuredImage != null) could easily be modified to:
final image = widget?.event?.featuredImage;
Stack(
children: [
// Add Container with image only when event and futured image are not null
if (image != null) ...[
Container(
height: 250,
decoration: BoxDecoration(
color: Colors.transparent,
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(image),
),
),
),
],
],
)
Yes, you have to implement some tiny bit of logic, you cannot just slap ? and ! on your code and it runs as before. But once you understood what that little change in logic is, it is pretty easy.
I have been getting an exception at a DropdownButton widget:
There should be exactly one item with [DropdownButton]'s value: [E4, B3, G3, D3, A2, E2].
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 850 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
The DropdownButton's value in this case is a list of strings: List<String> val = ["E4", "B3", "G3", "D3", "A2", "E2"]. As can be seen in the exception.
The DropdownMenuItems also contain lists of strings as their values in the same way, through the map of a variable that is a list of these lists (List<List<String>>). One of these items is the same as the DropdownButton, as a different variable however.
I have been very confused as to why I'm getting this error, given the condition is supposedly satisfied...
After searching for solutions, the thing that makes the most sense for me right now is the "fact" that lists are not equal despite having the same elements, which means that the equality is not evaluated in the same way as other types and will return false even though 2 lists have the same content. Which would explain this exception.
I can think of 2 immediate solutions, but both of them are troublesome:
The most problematic one, but also easier, which would be to modify Flutter's base code in order to adapt the assertion to consider Lists with the same elements as equal;
The less problematic one, but surely more complicated and troublesome, which would be to either change the types of these lists and handle them in a different manner or create classes for them
Maybe I misunderstood something and/or there are simpler ways to do this.
Please correct me if I'm wrong! Any help is very much appreciated, thank you for reading!
PS: This code used to work in an older Flutter/Dart version, at the end of 2019. Maybe this issue is somehow related to null safety or some other updates?
I have faced same problem. But I solved it like this:
class MyWidget extends StatelessWidget {
final List<String> val = ["E4", "B3", "G3", "D3", "A2", "E2"];
#override
Widget build(BuildContext context) {
return FormField<int>(
builder: (FormFieldState<int> state) {
return InputDecorator(
decoration: InputDecoration(
errorStyle: TextStyle(color: Colors.redAccent, fontSize: 16.0),
hintText: "hints",
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(5.0))),
isEmpty: state.value == null,
child: DropdownButtonHideUnderline(
child: DropdownButton<int>(
value: state.value,
isDense: true,
onChanged: (int? newValue) {
state.didChange(newValue);
print(val[newValue!]);
},
items: List<int>.generate(val.length, (index) => index)
.map<DropdownMenuItem<int>>((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(val[value]),
);
}).toList(),
),
),
);
},
);
}
}
I'm trying to migrate an existing, small Flutter app to Flutter 2.12. The null safety thing is new and still confusing to me. For the most part I have been successful in resolving the errors from the migration, but I haven't found a solution for this problem. Here we go:
I have a simple ProductListclass defined as so:
class ProductList {
int count = 0;
List<Product> rows = [];
}
No errors exist in the class. In my main.dart class I am using a ListView to view the list. In the method that creates the ListTile, both of the lines that refer to the productList.rows variable show an error. Here is the code:
eturn ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Colors.black,
),
shrinkWrap: true,
padding: const EdgeInsets.all(2.0),
itemCount: productList.***rows***.length,
itemBuilder: (context, i) {
return _buildRow(productList.***rows***[i]);
});
The error message for those lines is:
The property 'rows' can't be unconditionally accessed because the receiver can be 'null'.
Try making the access conditional (using '?.') or adding a null check to the target ('!').
I have peppered those lines with every combination of '?' and '!' over time, and still no luck. I'm sure I'm overlooking something simple, but I have no idea what that is. I'm new to null safety so Im still at the bottom of the learning curve and most of the official documentation I've found reminds me of reading the K&R (obscure reference for the old programmers in the crowd). Hat am I doing wrong?
change your productList declaration to late ProductList productList;
i want to change displayed data in Flutter? I wrote a function changeDataForTest (only a function for testing the event), which should change the data displayed in Text.
But if I click on this, it isn't changed. The value of the displayed string only changes, if i add (context as Element).reassemble(); after calling the method. Is this the normal way to go, or is there a smoother way to solve my problem?
dynamic changeDataForTest(neuerWert) {
this.data = neuerWert;
}
Column(
children: [
Center(
child: Text(
this.data + this.wiegehts,
),
),
FlatButton(
textColor: Color(0xFF6200EE),
onPressed: () {
changeDataForTest('neuerWert');
(context as Element).reassemble();
},
)
],
)
Thanks
Lukas
If you're using only a small widget, you could use a StatefulWidget using the method:
setState(() {
// change your variable
})
If your widget is complex and has lots of different possible variables, I'll not recommend using setState as this method calls the build method every time is being used.
One simple and fast option, is to use ValueNotifier:
final myVariable = ValueNotifier(false); // where you can replace 'false' with any Object
and then, using it this way:
ValueListenableBuilder(
valueListenable: myVariable,
builder: (context, value, child) {
return Text(value); // or any other use of Widgets
},
);
myVariable.value = true; // if you're looking for to change the current value
finally, if you logic is truly complex and you need to scale, I'll recommend to use a StateManagement library like:
Provider
Riverpod
BloC
Others
You can find those libraries and examples over: https://pub.dev
I'm new to Flutter and am working through the intro course on Udacity. In one of the tasks, I was trying to follow the code and I can't make much sense of it. Here's the code from the solution of the project (I've cut and paste the parts that matter, and also legal disclaimer I do not own any of this code, it's from the sample Flutter Udacity project):
Widget build(BuildContext context) {
final input = Padding(
padding: _padding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(...),
_createDropdown(_fromValue.name, _updateFromConversion),
],
),
);
}
void _updateFromConversion(dynamic unitName) {
setState(() {
_fromValue = _getUnit(unitName);
});
if (_inputValue != null) {
_updateConversion();
}
}
Widget _createDropdown(String currentValue, ValueChanged<dynamic> onChanged) {
return Container(
margin: EdgeInsets.only(top: 16.0),
decoration: BoxDecoration(...),
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Theme(...),
child: DropdownButtonHideUnderline(
child: ButtonTheme(
alignedDropdown: true,
child: DropdownButton(
value: currentValue,
items: _unitMenuItems,
onChanged: onChanged,
style: Theme.of(context).textTheme.title,
),
),
),
),
);
}
Here's where I'm stuck. _updateFromConversion requires an input parameter unitName. But when they call it, in _createDropdown, they don't pass any. So how does _updateFromConversion know what unitName is? Also, is _updateFromConversion executed before _createDropdown, or is it executed when the "onChanged" property of DropdownButton is set?
Second question: they're passing that function with return type void into _createDropdown, which is expecting ValueChanged. Shouldn't this throw an error?
If someone can explain the flow of this code and what I am missing I would greatly appreciate it.
Thank you!
You seem to be misunderstanding the assignment of a variable to a function for a function call.
Let me try to show it with example code.
void _updateFromConversion(dynamic unitName) {
print("Unit name: $unitName");
}
class SomeClass {
void Function(dynamic arg) myFunction;
}
void main() {
final c = SomeClass()..myFunction = _updateFromConversion;
print("Created c. Calling its function");
c.myFunction("foo");
print("Done");
}
When you run this code, you will see this printed:
Created c. Calling its function
Unit name: foo
Done
This shows that the _updateFromConversion function is not called when you create the SomeClass instance in SomeClass()..myFunction = _updateFromConversion;. This is only an assignment (it assigns to the field myFunction the value _updateFromConversion... yes, in Dart a function itself can be a value)!
You should know that because there's no () after the function name, and function invocation in Dart always must contain the list of arguments between () even if it's empty.
So, here's where the function is invoked:
c.myFunction("foo");
See? There's a list of arguments containing a single value, "foo". That's why the function then prints Unit name: foo, because the argument unitName takes the value "foo".
TL;DR
This is a function invocation:
c.myFunction("foo");
This is NOT:
c.myFunction;
Yashvin,
In dart function can be passed as parameter to other functions. This is usually used to pass callbacks for instance.
In example you provided, function _updateFromConversion is passed as parameter onChanged to another function _createDropdown.
In that function it will be assigned to onChanged listener of DropdownButton button.
Every time value of DropdownButton changes, this function will be invoked and will be passed selected value of DropdownButton.