Null aware assignment of map entry - dart

Is there an elegant way to write the following statement for a map?
String? img = json['image'];
if (img != null) {
json['image'] = 'https://example.com/ingredients_100x100/' + img;
}
Basically I want to append the image IFF it exists and ignore if it does not. This has the opposite effect of ??= where the assignment occurs only if the image is found to be null. Which is similar to putIfAbsent.
The best I can come up with is this:
String? img = json['image'];
json['image'] = img != null ? 'https://example.com/ingredients_100x100/' + img : null;
I just feel like this is the wrong way of writing for null safety as the word null appears twice in a line.

You could also use the update method of Map. It will throw an error if the key 'image' does not exist.
With the update method, you can also easily provide a default if the key image does not exist.
const baseImgUrl = 'https://example.com/ingredients_100x100/';
const defaultImgFilename = 'default.png';
Map<String, dynamic> updateImage(Map<String, dynamic> json) {
try {
return json
..update(
'image',
(filename) => '$baseImgUrl$filename',
);
} catch (_) { // key not in map
return json;
}
}
Map<String, dynamic> updateImageWithDefault(Map<String, dynamic> json) {
return json
..update(
'image',
(filename) => '$baseImgUrl$filename',
ifAbsent: () => '$baseImgUrl$defaultImgFilename',
);
}
void main() {
Map<String, dynamic> jsonWithoutImage = {'data': 123};
updateImage(jsonWithoutImage);
print('JSON without image: $jsonWithoutImage');
// JSON without image: {data: 123}
updateImageWithDefault(jsonWithoutImage);
print('JSON with default image: $jsonWithoutImage');
// JSON with default image: {data: 123, image: https://example.com/ingredients_100x100/default.png}
Map<String, dynamic> jsonWithImage = {'data': 123, 'image': 'apple.png'};
updateImage(jsonWithImage);
print('JSON with image: $jsonWithImage');
// JSON with image: {data: 123, image: https://example.com/ingredients_100x100/apple.png}
}

Related

How to send a GET request with an array as a parameter?

I was trying to create a function to make a GET with query parameters. I was dealing with the Mangadex API and was to send a parameter called 'manga' as an array. I created the code as follows:
Future<http.Response> getCoverArtResponse(String mangaID) async {
var queryParameters = {
'limit': '10',
'manga': [mangaID] //Here
};
var unencodedPath = '/cover';
var response = await http.get(
Uri.https(authority, unencodedPath, queryParameters),
headers: {HttpHeaders.contentTypeHeader: 'application/json'});
return response;
}
However, the response was the following error:
{"result":"error","errors":[{"id":"9c346772-7b14-5982-b4b6-7b5888522762","status":400,"title":"validation_exception","detail":"Error validating \/manga: String value found, but an array is required","context":null}]}
How am I supposed to send the parameters? So far I have tried -
'manga': [mangaID]
'manga': '[$mangaID]'
None of them seem to work.
import 'dart:async';
import 'package:wnetworking/wnetworking.dart';
class MangaDex {
static const _base = 'https://api.mangadex.org';
static FutureOr<void> _getter({required String url, required Function(JMap item, int idx) onItem}) async {
await HttpReqService.getJson<JMap>(url)
.then((response) {
var results = response?['results'];
if (results != null) {
if (results is List) {
var i = 0;
results.forEach((manga) => onItem(manga, ++i));
} else {
print(response);
}
}
});
}
static FutureOr<void> cover({int limit = 10, int offset=0, String? mangaId, String? coverId}) async {
final mangas = mangaId != null ? '&manga[]=$mangaId' : '';
final covers = coverId != null ? '&ids[]=$coverId' : '';
final url = '$_base/cover?limit=$limit&offset=$offset$mangas$covers';
await _getter(
url: url,
onItem: (item, idx) {
print('$idx) "${item['data']?['attributes']?['fileName']}"');
print(' id: ${item['data']?['id']}\n');
},
);
}
}
void main(List<String> args) async {
await MangaDex.cover(mangaId: '32d76d19-8a05-4db0-9fc2-e0b0648fe9d0', limit: 2);
print('\nJob done');
}
Result:
1) "f5873770-80a4-470e-a11c-63b709d87eb3.jpg"
id: b6c7ce9c-e671-4f26-90b0-e592188e9cd6
2) "e9f926db-b469-48c4-8cc4-a8e523ad75ca.jpg"
id: 00aae6e0-46bb-4f92-a82a-1c740789b704
Job done
Replace wnetworking package with http package, and JMap with Map<String, dynamic>
NOTE: MangaDex Documentation is lacking and misleading about how to correctly use its endpoints.

Flutter web: Values retrieved from Firestore map are truncated when added to List

In my Flutter Web application I am retrieving values from the map timeslots in Firestore.
This is what the data looks like:
But, instead of retrieving the whole list of values, I get a truncated list like this:
[Mo-Washing-(09:00-10:00, 10:00-11:00, 11:00-12:00, ..., 20:00-21:00, 21:00-22:00)]
Below I have included the 2 functions responsible for retrieving the data and adding it to the list object
static List object = [];
static Map<String, dynamic> timeDetails = {};
static Map<String, dynamic> userDetails = {};
checkExists(docuID) async {
return await firestore()
.collection('environments')
.doc(docuID)
.get()
.then((val) {
userDetails.addAll(val.data());
}).whenComplete(() async {
fs.DocumentSnapshot snapShot = await firestore()
.collection('environments')
.doc(docuID)
.collection('Washing')
.doc('monday')
.get();
if (snapShot == null || !snapShot.exists) {
print('does not exist');
} else {
await getData(docuID, 'Washing');
}
setState(() {});
});
}
getData(docuID, machineName) async {
return await firestore()
.collection('environments')
.doc(docuID)
.collection(machineName)
.doc('monday')
.get()
.then((val) {
timeDetails.addAll(val.data());
}).whenComplete(() {
object.add('Mo-$machineName-${timeDetails['timeslots'].values}');
print(object);
setState(() {});
});
}
This also happens in debugPrint. Would anyone know why this is happening and how I could solve it? Any help on this would be appreciated!
Neither the workaround as mentioned on Github nor debugPrint worked for me, but I managed to solve this by adding .toList() to my getData function:
getData(docuID, machineName) async {
return await firestore()
.collection('environments')
.doc(docuID)
.collection(machineName)
.doc('monday')
.get()
.then((val) {
timeDetails.addAll(val.data());
}).whenComplete(() {
//toList() is added here to .add
object.add('Mo-$machineName-${timeDetails['timeslots'].values.toList()}');
print(object);
setState(() {});
});
}
Output:
[Mo-Washing-[09:00-10:00, 10:00-11:00, 11:00-12:00, 12:00-13:00, 13:00-14:00, 14:00-15:00, 15:00-16:00, 16:00-17:00, 17:00-18:00, 18:00-19:00, 19:00-20:00, 20:00-21:00, 21:00-22:00]

Flutter : Capture the Future response from a http call as a normal List

I am trying to make my previously static app, dynamic by making calls to the backend server.
This is what my service looks like
String _url = "http://localhost:19013/template/listAll";
Future<List<TemplateInfoModel>> fetchTemplates() async {
final response =
await http.get(_url);
print(response.statusCode);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
Iterable l = json.decode(response.body);
List<TemplateInfoModel> templates = l.map((i) => TemplateInfoModel.fromJson(i)).toList();
return templates;
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
And my model looks like this :
class TemplateInfoModel {
final String templateId;
final String messageTag;
final String message;
final String messageType;
TemplateInfoModel({this.templateId, this.messageTag, this.message,
this.messageType});
factory TemplateInfoModel.fromJson(Map<String, dynamic> json) {
return TemplateInfoModel ( templateId: json['templateId'], messageTag : json['messsageTag'], message : json ['message'] , messageType : json['messageType']);
}
}
I have a utils method within which I am capturing the http request/response data; which would then use that to create a DropDown Widget ( or display it within a text)
My earlier dummy data was a list; I am wondering how best I can convert this Future> to List
class SMSTemplatingEngine {
var _SMSTemplate; //TODO this becomes a Future<List<TemplateInfoModels>>
// TemplateInfoService _templateInfoService;
SMSTemplatingEngine(){
_SMSTemplate=fetchTemplates();
}
// var _SMSTemplate = {
// 'Reminder':
// 'Reminder : We’re excited to launch a new feature on our platform that will revolutionize your Facebook marketing and triple your ROI. Visit url.com to learn more',
// 'New Message':
// 'New Message: We’re excited to launch a new feature on our platform that will revolutionize your Facebook marketing and triple your ROI. Visit url.com to learn more',
// 'Connecting Again':
// 'Connecting Again : We’re excited to launch a new feature on our platform that will revolutionize your Facebook marketing and triple your ROI. Visit url.com to learn more',
// };
List<String> getKeys(){
List<String> smsKeys = new List();
for ( var key in _SMSTemplate.keys)
smsKeys.add(key);
return smsKeys;
}
String getValuePerKey(String key){
return _SMSTemplate['${key}'];
}
}
P.S. I have looked at some posts but I was completely bowled over since I am a Flutter Newbie.
Is there an easier way for this to happen.
Thanks,
The widget which would display the content from the http call
var templateMessagesDropDown = new DropdownButton<String>(
onChanged: (String newValue) {
setState(() {
templateMsgKey = newValue;
print("Selcted : ${templateMsgKey.toString()} ");
});
},
// value: _defaultTemplateValue,
style: textStyle,
//elevation: 1,
hint: Text("Please choose a template"),
isExpanded: true,
//
items: smsEngine.getKeys().map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
I am wondering how best I can convert this Future to List
Future<List<TemplateInfoModel>> fetchTemplates() should return a List that you're expecting. You may want to consider either using FutureBuilder or StreamBuilder to update the UI elements on your screen. Or if you're not keen on using either of those, you can just call the Future and update the List on your current screen.
List<TemplateInfoModel> _listTemplateInfoModel = [];
...
fetchTemplates().then((value){
setState((){
_listTemplateInfoModel = value;
});
});

Dart - How to multiple http.post request with different bodies

I found this: Optimal way to make multiple independent requests to server in Dart
But my issue is a bit different.
I want to make multiple posts with different bodies but I get the same results, which is related to the last element of types list.
final List<String> types = ['completed', 'approval', 'process', 'available'];
Think about this list, I'm always getting 'completed' type results.
Future<List<dynamic>> fetchAllRequests() async {
int i = 0;
userInfo['type'] = types.first;
return Future.wait(
types.map(
(t) => client.post(Api.requests, body: userInfo).then(
(response) {
if (i < types.length - 1) {
userInfo['type'] = types[++i];
}
Map<String, dynamic> m = jsonDecode(response.body);
dataItemsLists.add(m['DataItems']);
print('${m['DataItems']}');
},
),
),
);
}
Also, I want to manipulate the body inside the map() fun, but this is not working:
types.map((t){client.post)(Api.requests, body: userInfo).then()...}
Error log:
NoSuchMethodError: The method 'then' was called on null.
Receiver: null
Tried calling: then<dynamic>(Closure: (dynamic) => Null, onError:
Closure: (dynamic, StackTrace) => Null)
While this is working:
types.map((t) => client.post)(Api.requests, body: userInfo).then()...
So I manipulate in a verbose mode the body as you see in my first code block up above instead of like this way:
Future<List<dynamic>> fetchAllRequests() async {
return Future.wait(
types.map((String t) {
userInfo['type'] = t;
client.post(Api.requests, body: userInfo).then(
(response) {
Map<String, dynamic> m = jsonDecode(response.body);
dataItemsLists.add(m['DataItems']);
print('${m['DataItems']}');
},
);
}),
);
}
If you use {} instead of =>, then you need to explicitly return
Here the result of .map(...) is null because nothing is returned
types.map((t){client.post)(Api.requests, body: userInfo).then()...}
Either use
types.map((t) => client.post)(Api.requests, body: userInfo).then()...;
or
types.map((t){return client.post)(Api.requests, body: userInfo).then()...}
Similar in your last code block
client.post(Api.requests, body: userInfo).then(
should be
return client.post(Api.requests, body: userInfo).then(

Save data to localStorage from api

I want to save data on Local Storage to make app works offline.
I want to get data from api, json data and save them to local storage,
PostList and Post are my classes
Future <PostsList> fetchPostsList(language) async {
final response =
await http.get('https://sas-survey.urbanway.net/api/questions/1/${language}');
if(response.statusCode == 200){
// storagepost.setItem('resultPost', response.body);
return PostsList.fromJson(json.decode(response.body));
}else{
// return storagepost.getItem('resultPost');
throw Exception('Filed to load post');
}
}
final LocalStorage storagee = new LocalStorage('result_app');
static PostsList resultsListEn = new PostsList();
_addItemEn( String id,
String question,
String photo,
List answers,
String categoryid) {
setState(() {
final resulten = new Post(id: id, title: question, image: photo, answers: answers, category: categoryid);
print(resulten.title.toString());
resultsListEn.posts.add(resulten);
_saveToStorageEn();
});
}
_saveToStorageEn() {
widget.storagee.setItem('en', resultsListEn.toJsonEncodable());
}
new FutureBuilder<PostsList>(
future: fetchPostsList(en),
builder: (context, snapshot){
if(snapshot.hasData){
List<Post> questionsEn =snapshot.data.posts;
questionsEn.forEach((question){
_addItemEn(question.id, question.title, question.image, question.answers, question.category);
});
return Center();
}else if(snapshot.hasError){
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
this is my code what im missing?
You can use Shared Preferences for basic data. Add a toJson and fromJson method to your models PostList and Post and serialize it using json.decode/encode and save it as a string. When reading it back convert it back and use it. Examples of how I did it below. Your code is not complete enough to make adjustments to.
get postValue {
var jsonContent = sharedPreferencesInstance.getString('post-key');
if(jsonContent != null) {
return Post.fromJson(json.decode(jsonContent));
}
return null;
}
set postValue(Post post) => sharedPreferencesInstance.setString(
'post-key', json.encode(post.toJson()));

Resources