Thinking & coding: making my code Null safe.
Hey everyone,
I was able to start a programming session today. In this session I have built in the necessary security features in my code.
The errors and exeptions, I wanted to catch today were the errors that were possible reading my data from the FireBase backend. And there were a few.
- Protect against reading non-existent database elements. In others words, the Null are intercepted.
- Catching a possible empty String, and this with the NetWorkImage Widget. In such a way that no more exception errors are possible when offering an incorrect or empty URL.
Just a brief overview. My app only reads data. No data is written from the app to the backend. The backend is therefore managed by an administrator (the customer), who completes the database. In this case, therefore, a list of events and the data of these events. So I also have to secure the app against possible human error. I know you can secure that through the backend by building a structured portal that catches these errors. But then there must still be the same security on the app side.
How did I get started.
I have an Event collection in the backend. And in this collection I have several documents. And these documents in turn consist of data elements. All my data elements are currently Strings. I have some regular String elements, but 1 element is a string that has a URL in it. And this to be able to refer to the images in the FireStorage. Below is a view of my backend.
Imagine, my software requests a data element from the backend that does not exist, for example. That could be if the administrator of the backend for example forgets to create a certain data element. Then the backend for that request will forward a NULL to my app. And in my case this NULL would eventually end up in my Text Widgets. This would result in a fatal error that would cause my app to crash. We don't want that.
Okay, read documentation, let's get started. Uh, but here comes the first question. Where can I best try to intercept that NULL?
- At the Text Widgets? But then I have to repeat a lot of code. For each text display again. That does not seem like such an ideal plan to me.
- In the library where I have the FireBaseService, which reads out all my data?
- Maybe in my own Event data model. Where do I map and organize the data that I read via the FireBase Service?
- Or do I have to address this in the StreamProvider?
So a lot of questions before I have written 1 line of code. Because I had absolutely no idea I contacted my friend Max Weber from Flutter Explained. Together we discussed this via chat, and then it turned out in the case of my app, with structure as arguments, the best thing to do was to tackle the problem in my Event Data Model.
No sooner said than done.
Now which syntax will I use, well I have tried several options. From "if - else" statements to abbreviated syntax. I chose "Null-aware operators".
From line 26 below you can see the code I added to check the Null.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import 'package:my_first_production/owner_data/owner_data.dart'; // defining the event data model class with the name Event // Events has to be the collection name inside Firestore collections class Events { final String eventId; final String eventTitle; final String eventDate; final String eventSubTitle; final String eventText; final String eventImage; Events( {this.eventId, this.eventTitle, this.eventDate, this.eventSubTitle, this.eventText, this.eventImage}); // json from firestore to dart factory Events.fromJson(Map<String, dynamic> json) { return Events( eventId: json['eventId'] ?? noTextData, eventTitle: json['eventTitle'] ?? noTextData, eventDate: json['eventDate'] ?? noTextData, eventSubTitle: json['eventSubTitle'] ?? noTextData, eventText: json['eventText'] ?? noTextData, eventImage: json['eventImage'] ?? noImageData, ); } |
So if you have default data to fall back on, you can use this syntax.
Below my used fallback constants. Declared in a different library.
1 2 | const String noTextData = 'no data retreived'; const String noImageData = 'https://firebasestorage.googleapis.com/v0/b/ |
Now the next problem.
Now I have written the code to intercept the Null and give it a fallback value.
I have 1 data element 'eventImage' which is also of type string, but it contains a URL. It is the URL pointing to my images in the FireBase Storage. If this string is empty or does not contain a valid URL, I get an error. And again we don't want this.
So I also created a fallback Url, see above.
Since I don't get it done to also do the empty string check in EventMode, I wrote this check in the StreamBuilder.
I have chosen the "isEmpty" method here. line 24.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | StreamBuilder<List<Events>>( stream: lijstjes, builder: (BuildContext context, AsyncSnapshot<List<Events>> snapshot) { if (snapshot.hasError) { return Text('Something went wrong'); } if (snapshot.connectionState == ConnectionState.waiting) { return Text("Loading"); } return Expanded( child: ListView.builder( itemCount: snapshot.data.length, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.all(8.0), child: EventCard( titleCard: snapshot.data[index].eventTitle, subTitleCard: snapshot.data[index].eventSubTitle, dateCard: snapshot.data[index].eventDate, discriptionCard: snapshot.data[index].eventText, imageCard: snapshot.data[index].eventImage.isEmpty ? noImageData : snapshot.data[index].eventImage , ), ); }, ), ); }) |
And with these my possible data errors have been caught. I regret not being able to perform this last check in the EventModel, where the other checks are done. It had been much more structured that way.
So I hope you can follow in my explanation. If not, or if you know of better solutions, please leave a message in the comments.
Thank you in advance for reading my adventures in Flutter.
Until the next.
Do, believe and be happy,
Stefaan
Comments
Post a Comment