I admittedly still have poor understanding of null checks but I do understand the gist of it (I think lol). However in this case I’m quite confused why I’m getting a null check exception. I’m storing the data inside textControllers because instead of using TextFormFields, I’m using Text widget to display textController.values. The user makes changes by opening a dialog and submitting new data there. It will call setState to make the changes to the widget. Basically, the user can see the changes the made to their profile instead of having a form. It was for this reason that I choose not to use FutureBuilder because setState will recall data from the Firebase and overwrite the changes.
That’s a short summary of what I’m trying to achieve. My main issue is that I have a String? rating variable that is set a value on initState through a Future function that fetches data from Firestore. Other values are showing such as the starting_price and description. Rating seems to be the main issue for some reason. Initially, it was a integer variable and a number data type in Firestore but I changed it to String to see if the code worked. If I pass rating into a widget, it will require a null check operator since its a nullable String. It will return an null check operator used on a null value exception. It works when I pass a toString function to the variable inside Text widget but still an error when I parsed it to double for the RatingBarIndicator (flutter_rating_bar package).
I suspect it’s due to the Widget building before a value is set to rating variable but I already initialised it inside initState. How do I achieve this with or without FutureBuilder?
I removed a few lines to keep the code short but I can provide the rest if necessary.
profile_screen.dart
class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key, required this.userId});
final String userId;
@override
State<ProfileScreen> createState() => _ProfileScreenViewState();
}
class _ProfileScreenViewState extends State<ProfileScreen> {
String? rating;
@override
void initState() {
super.initState();
fetchProviderData(widget.userId);
}
Future<void> fetchProviderData(String userId) async {
final DocumentSnapshot snapshot = await FirebaseFirestore.instance
.collection('providers')
.doc(userId)
.get();
if (snapshot.exists) {
final data = snapshot.data() as Map<String, dynamic>?;
if (data != null) {
setState(() {
startPriceController.text = data['starting_price'];
descriptionController.text = data['description'];
profilePicUrl = data['profile_picture'] as String;
rating = data['rating'] as String;
services = data['services'] as List<dynamic>;
print(rating);
print(startPriceController.text);
});
}
}
}
@override
Widget build(BuildContext context) {
print(startPriceController.text); // IT SHOWS THE CORRECT VALUES
print(descriptionController.text);
print(rating);
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Flexible(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(rating.toString()),
RatingBarIndicator(
rating: double.parse(rating.toString()),
itemCount: 5,
itemSize: 15,
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Colors.amber),
),
])))
]),
],
)));
}
}
Printing the values inside widget build does show it has correct values but it’s printing twice(?)
Console
I/flutter ( 4651): LANDING: PROFILE CLICKED
I/flutter ( 4651): START PRICE:
I/flutter ( 4651): DESC:
I/flutter ( 4651): RATING:
I/flutter ( 4651): START PRICE: 20.1
I/flutter ( 4651): DESC: description here
I/flutter ( 4651): RATING: 3
Stack Trace
════════ Exception caught by widgets library ═══════════════════════════════════
The following FormatException was thrown building ProfileScreen(dirty, state: _ProfileScreenViewState#060dc):
Invalid double
The relevant error-causing widget was
ProfileScreen
landing_page.dart:197
When the exception was thrown, this was the stack
#0 double.parse (dart:core-patch/double_patch.dart:111:28)
#1 _ProfileScreenViewState.build
profile_screen.dart:219
#2 StatefulElement.build
framework.dart:4992
#3 ComponentElement.performRebuild
framework.dart:4878
#4 StatefulElement.performRebuild
framework.dart:5050
#5 Element.rebuild
framework.dart:4604
#6 ComponentElement._firstBuild
framework.dart:4859
#7 StatefulElement._firstBuild
framework.dart:5041
#8 ComponentElement.mount
framework.dart:4853
... Normal element mounting (275 frames)
#283 Element.inflateWidget
framework.dart:3863
#284 MultiChildRenderObjectElement.inflateWidget
framework.dart:6435
#285 Element.updateChild
framework.dart:3592
#286 RenderObjectElement.updateChildren
framework.dart:5964
#287 MultiChildRenderObjectElement.update
framework.dart:6460
#288 Element.updateChild
framework.dart:3570
#289 ComponentElement.performRebuild
framework.dart:4904
#290 StatefulElement.performRebuild
framework.dart:5050
#291 Element.rebuild
framework.dart:4604
#292 StatefulElement.update
framework.dart:5082
#293 Element.updateChild
framework.dart:3570
#294 ComponentElement.performRebuild
framework.dart:4904
#295 Element.rebuild
framework.dart:4604
#296 ProxyElement.update
framework.dart:5228
#297 Element.updateChild
framework.dart:3570
#298 ComponentElement.performRebuild
framework.dart:4904
#299 Element.rebuild
framework.dart:4604
#300 ProxyElement.update
framework.dart:5228
#301 _InheritedNotifierElement.update
inherited_notifier.dart:107
#302 Element.updateChild
framework.dart:3570
#303 SingleChildRenderObjectElement.update
framework.dart:6307
#304 Element.updateChild
framework.dart:3570
#305 ComponentElement.performRebuild
framework.dart:4904
#306 StatefulElement.performRebuild
framework.dart:5050
#307 Element.rebuild
framework.dart:4604
#308 StatefulElement.update
framework.dart:5082
#309 Element.updateChild
framework.dart:3570
#310 SingleChildRenderObjectElement.update
framework.dart:6307
#311 Element.updateChild
framework.dart:3570
#312 SingleChildRenderObjectElement.update
framework.dart:6307
#313 Element.updateChild
framework.dart:3570
#314 ComponentElement.performRebuild
framework.dart:4904
#315 Element.rebuild
framework.dart:4604
#316 ProxyElement.update
framework.dart:5228
#317 Element.updateChild
framework.dart:3570
#318 ComponentElement.performRebuild
framework.dart:4904
#319 StatefulElement.performRebuild
framework.dart:5050
#320 Element.rebuild
framework.dart:4604
#321 BuildOwner.buildScope
framework.dart:2667
#322 WidgetsBinding.drawFrame
binding.dart:882
#323 RendererBinding._handlePersistentFrameCallback
binding.dart:378
#324 SchedulerBinding._invokeFrameCallback
binding.dart:1175
#325 SchedulerBinding.handleDrawFrame
binding.dart:1104
#326 SchedulerBinding._handleDrawFrame
binding.dart:1015
#327 _invoke (dart:ui/hooks.dart:148:13)
#328 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
#329 _drawFrame (dart:ui/hooks.dart:115:31)
════════════════════════════════════════════════════════════════════════════════
Edit: Added null value stack
Stack Trace (null value exception)
════════ Exception caught by widgets library ═══════════════════════════════════
The following _CastError was thrown building ProfileScreen(dirty, state: _ProfileScreenViewState#30924):
Null check operator used on a null value
The relevant error-causing widget was
ProfileScreen
landing_page.dart:197
When the exception was thrown, this was the stack
#0 _ProfileScreenViewState.build
profile_screen.dart:219
#1 StatefulElement.build
framework.dart:4992
#2 ComponentElement.performRebuild
framework.dart:4878
#3 StatefulElement.performRebuild
framework.dart:5050
#4 Element.rebuild
framework.dart:4604
#5 ComponentElement._firstBuild
framework.dart:4859
#6 StatefulElement._firstBuild
framework.dart:5041
#7 ComponentElement.mount
framework.dart:4853
... Normal element mounting (275 frames)
#282 Element.inflateWidget
framework.dart:3863
#283 MultiChildRenderObjectElement.inflateWidget
framework.dart:6435
#284 Element.updateChild
framework.dart:3592
#285 RenderObjectElement.updateChildren
framework.dart:5964
#286 MultiChildRenderObjectElement.update
framework.dart:6460
#287 Element.updateChild
framework.dart:3570
#288 ComponentElement.performRebuild
framework.dart:4904
#289 StatefulElement.performRebuild
framework.dart:5050
#290 Element.rebuild
framework.dart:4604
#291 StatefulElement.update
framework.dart:5082
#292 Element.updateChild
framework.dart:3570
#293 ComponentElement.performRebuild
framework.dart:4904
#294 Element.rebuild
framework.dart:4604
#295 ProxyElement.update
framework.dart:5228
#296 Element.updateChild
framework.dart:3570
#297 ComponentElement.performRebuild
framework.dart:4904
#298 Element.rebuild
framework.dart:4604
#299 ProxyElement.update
framework.dart:5228
#300 _InheritedNotifierElement.update
inherited_notifier.dart:107
#301 Element.updateChild
framework.dart:3570
#302 SingleChildRenderObjectElement.update
framework.dart:6307
#303 Element.updateChild
framework.dart:3570
#304 ComponentElement.performRebuild
framework.dart:4904
#305 StatefulElement.performRebuild
framework.dart:5050
#306 Element.rebuild
framework.dart:4604
#307 StatefulElement.update
framework.dart:5082
#308 Element.updateChild
framework.dart:3570
#309 SingleChildRenderObjectElement.update
framework.dart:6307
#310 Element.updateChild
framework.dart:3570
#311 SingleChildRenderObjectElement.update
framework.dart:6307
#312 Element.updateChild
framework.dart:3570
#313 ComponentElement.performRebuild
framework.dart:4904
#314 Element.rebuild
framework.dart:4604
#315 ProxyElement.update
framework.dart:5228
#316 Element.updateChild
framework.dart:3570
#317 ComponentElement.performRebuild
framework.dart:4904
#318 StatefulElement.performRebuild
framework.dart:5050
#319 Element.rebuild
framework.dart:4604
#320 BuildOwner.buildScope
framework.dart:2667
#321 WidgetsBinding.drawFrame
binding.dart:882
#322 RendererBinding._handlePersistentFrameCallback
binding.dart:378
#323 SchedulerBinding._invokeFrameCallback
binding.dart:1175
#324 SchedulerBinding.handleDrawFrame
binding.dart:1104
#325 SchedulerBinding._handleDrawFrame
binding.dart:1015
#326 _invoke (dart:ui/hooks.dart:148:13)
#327 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
#328 _drawFrame (dart:ui/hooks.dart:115:31)
════════════════════════════════════════════════════════════════════════════════
2
Answers
Your output shows pretty clearly that the first time
build
is run, all your variables are not set. That is becausefetchProviderData
isasync
and not done yet, beforebuild
is called the first time.You can either use a FutureBuilder to display your result of an operation returning a Future. You can find an example of that here: What is a Future and how do I use it?
Or you can simply instruct your
build
method to react properly torating
being null. Simplest example might bebut you may want a more complicated setup so it looks good with the rest of your program.
You declare rating as a nullable string and inside the rating bar indicator rating is parsed into a double using,
first understand the use of toString(), toString() is used to convert any data type into string type for display textual representation, e.g.,
instead toString() with rating variable use null aware operator as below,