Save data to localStorage from api - dart

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()));

Related

how to send parametrs in signal r with several requests?

I have several separate requests to my websockets and I wanted to get data from all of these using parametrs. AnimalID in getImageAnimal - parametrs for getting image of fixed animals. here is requests I am trying to make
final hubConnection1 = HubConnectionBuilder()
.withUrl(
'http://10.10:8000/animalHub',
options: httpConnectionOptions,
)
.build();
await hubConnection1.start();
List fixationAnimals = [];
if (hubConnection1.state == HubConnectionState.Connected) {
await hubConnection1
.invoke('GetAnimalFixations')
.then((value) => fixation = value as List)
.then((value1) => hubConnection1.invoke('GetImageAnimal',
args: [imageId ?? '']).then((value2) => imageId == value2));
}
hubConnection1.onclose(({error}) {
logger?.finer(error);
});
print(imageId);
print(fixation);
return fixation;
in GetAnimalFixations I have such field:
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"animalId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"fixedTimeAnimal": "2022-08-23T07:25:29.898Z",
"imageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
]
I found out how to do it. Everything i post here is just for testing pupose, just to make sure it is really works. Please do not write like below (as I know it is a bad decision to manipulate FutureBuilder as I did below, but it is ONLY for testing purpose)
I have several requests and one of them are depended on the other one.
GetAnimalFixations (the main request)-> GetImageAnimal (depended one, where I should put arguments imageId from the first request)
STEP 1
Create two separate requests with methods above like that:
Future<List> fetchAnimalFixation() async {
final hubConnection1 = HubConnectionBuilder()
.withUrl(
'http://10.10:8000/animalHub',
options: httpConnectionOptions,
)
.build();
await hubConnection1.start();
List fixationAnimals = [];
if (hubConnection1.state == HubConnectionState.Connected) {
await hubConnection1
.invoke('GetAnimalFixations')
.then((value) => fixation = value as List);
}
hubConnection1.onclose(({error}) {
logger?.finer(error);
});
print(fixation);
return fixation;
}
Second request:
Future<String> fetchAnimalImage(String imageId) async {
final hubConnection1 = HubConnectionBuilder()
.withUrl(
'http://10.10:8000/animalHub',
options: httpConnectionOptions,
)
.build();
await hubConnection1.start();
String image = [];
if (hubConnection1.state == HubConnectionState.Connected) {
await hubConnection1
.invoke('GetImageAnimal', args[imageId])
.then((value) => image= value as String);
}
hubConnection1.onclose(({error}) {
logger?.finer(error);
});
print(image);
return image;
}
STEP 2
First preparation is over, now we a go into the second steps to achive our goal:
You need to create the screen where all data will be displayed. For my testing purpose (again: ONLY for testing and small piece of data) I use future builder:
body: FutureBuilder<List>(
future: AllFixation().fetchAnimalFixation(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: 2,
itemBuilder: (context, index) {
return Card(
child: Column(
children: [
Text(snapshot.data?[index]['name'].toString() ?? ''),
FutureBuilder<String?>(
future: AllFixation().fetchAnimalImage(
snapshot.data?[index]['imageId'].toString() ??
''),
builder: (context, snapshotImage) {
Uint8List bytes =
base64.decode(snapshotImage.data ?? '');
return Image.memory(bytes);
}),
Voila! All is ready. You can use and enjoy the picture and the name of the animal. All this via websocket and using signalR package. I higly recommend to use some state management and think that you are going to do next, because it is the solid piece of code and if someday it will need to change, it turn out to be the biggest pain.

Firebase Dynamic Link on getInitialLink in iOS (using Flutter)

I have the following code in my application:
For creating the dynamic link I have:
/// Function that creates a DynamicLink for a [SingleFashionItem] using [FirebaseDynamicLinks]
static Future<String> createFashionItemLink(
BuildContext context,
SingleFashionItem fashionItem,
) async {
// Get the package information, this function won't probably fail,
// therefore don't any errors
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
final DynamicLinkParameters parameters = DynamicLinkParameters(
uriPrefix: CUSTOM_DOMAIN,
link: Uri.parse(
"$CUSTOM_DOMAIN/item?parentID=${fashionItem.parentID}&objectID=${fashionItem.objectID}"),
androidParameters: AndroidParameters(
packageName: packageInfo.packageName,
),
iosParameters: IosParameters(
bundleId: packageInfo.packageName,
appStoreId: "123456789" // TODO change this AppStoreID
),
socialMetaTagParameters: SocialMetaTagParameters(
title:
"${AppLocalizations.of(context).translate("check_out")} ${fashionItem.name}",
description: fashionItem.description,
imageUrl: Uri.parse(fashionItem.images[0].url),
),
);
// Build a short link
return parameters
.buildShortLink()
.then((shortDynamicLink) => shortDynamicLink.shortUrl.toString())
.catchError((error) {
print("The error is: ${error.toString()}");
return null;
});
}
// Function that handles the Dynamic Link using [FirebaseDynamicLinks]
static void handleDynamicLink(BuildContext context) async {
// Get the initial dynamic link if the app is started using the link
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
print("The dynamic data is ${data.toString()}");
// Handle the dynamic link
_handleDynamicLink(context, data);
// Handle when the application is brought to the foreground when being in background
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData data) async {
_handleDynamicLink(context, data);
}, onError: (OnLinkErrorException error) async {
// Send the error to Crashlytics
FirebaseCrashlytics.instance.reportNonFatalError(
exception: error,
customMessage: "Error while handling the dynamic link: ${data.link}",
userID: "not-available",
);
});
And for handling the dynamic link:
/// Function that handles the incoming [PendingDynamicLinkData]'s deep link
static void _handleDynamicLink(
BuildContext context, PendingDynamicLinkData data) {
final Uri deepLink = data?.link;
print("The deep link is: ${deepLink.toString()}");
// Check that the deep link is null or not
if (deepLink != null) {
// Handle deepLink
var isItem = deepLink.pathSegments.contains("item");
// Check if the items path segment is contained inside the path segment
if (isItem) {
// The deep link contains the items path segment
final objectID = deepLink.queryParameters["objectID"];
final parentID = deepLink.queryParameters["parentID"];
// Check that the itemID string is not null
if (objectID != null && parentID != null)
_goToDetailedFashionItemScreen(
context,
selectedFashionItemID: objectID,
parentID: parentID,
);
}
}
}
-> I've added in my
Associated Domains applinks:example.com
URL schemes com.example.application
In the Info.plist
<key>FirebaseDynamicLinksCustomDomains</key>
<array>
<string>https://example.com/item</string>
</array>
What I'm getting in a null value from the:
final Uri deepLink = data?.link
From what I've seen in this Github issue it might be because of a race condition, however this doesn't work for me.
This is the only solution that worked for me.
You should use the package "app_links" besides "firebase_dynamic_links".
Here is my code that worked for IOS:
static Future<void> initDynamicLinkIOS(BuildContext context) async {
final appLinks = AppLinks();
final Uri? uri = await appLinks.getInitialAppLink();
if (uri != null) {
final PendingDynamicLinkData? data = await FirebaseDynamicLinks.instance.getDynamicLink(uri);
Uri? deepLink = data?.link;
if (deepLink != null) {
// do your logic here, like navigation to a specific screen
}
}
}

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;
});
});

How to "await" non-future variable?

I have DocumentReference locationDocumentRef; in my state.
I'm changing locationDocumentRef based on the references, whether I gather by querying or by adding new document.
So I have this function to check the documents, if there is one set its reference to the locationDocumentRef, or add a new one and set its ref to the locationDocumentRef. I'm resetting its value everytime by setting it to null, since I didn't want to get previous result. But it prints null.
So my question is, how can I resolve them and get the value? I think I'm resolving too early in my code, so I can't await a non-future value. How can I fix it?
void firestoreCheckAndPush() async {
setState(() {
locationDocumentRef = null;
});
bool nameExists = await doesNameAlreadyExist(placeDetail.name);
if (nameExists) {
print('name exist');
} else {
print('name will be pushed on firestore');
pushNameToFirestore(placeDetail);
}
var resolvedRef = await locationDocumentRef;
print(resolvedRef.documentID); // I get null here
}
These are the functions that I have used
Future<bool> doesNameAlreadyExist(String name) async {
QuerySnapshot queryDb = await Firestore.instance
.collection('locations')
.where("city", isEqualTo: '${name}')
.limit(1)
.getDocuments();
if (queryDb.documents.length == 1) {
setState(() {
locationDocumentRef = queryDb.documents[0].reference;
});
return true;
} else {
return false;
}
}
And the other
void pushNameToFirestore(PlaceDetails pd) async {
DocumentReference justAddedRef =
await Firestore.instance.collection('locations').add(<String, String>{
'city': '${pd.name}',
'image': '${buildPhotoURL(pd.photos[0].photoReference)}',
});
setState(() {
locationDocumentRef = justAddedRef;
});
}
there is two mistakes i saw first here
var resolvedRef = await locationDocumentRef;
why you await for locationDocumentRef,
second you dont wait for pushNameToFirestore(PlaceDetails pd) firestoreCheckAndPush() function which is weird since pushNameToFirestore(String) is sync and this means you wouldnt wait for it to finish so if you are adding a new name it would print null.
correct me if i am wrong.
you can find more about sync and future here https://www.dartlang.org/tutorials/language/futures
look at the graph at the middle of the page
Try this
Future<List<DocumentSnapshot>> doesNameAlreadyExist(String name) async {
QuerySnapshot data = await Firestore.instance
.collection('locations')
.where("city", isEqualTo: name)
.limit(1)
.getDocuments();
return data.documents;
}
void firestoreCheckAndPush() async {
var data = await doesNameAlreadyExist('yourname');
if (data.length > 0) {
print('name exist');;
print('Document id '+ data[0].documentID);
} else {
print('name will be pushed on firestore');
}
}
Take a look into following code.
void firestoreCheckAndPush() async {
DocumentReference documentReference;
var data = await doesNameAlreadyExist('yourname');
var dataRef = await doesNameAlreadyExist('yourname');
if (data.length > 0) {
print('name exist');
documentReference = dataRef[0].reference;
print('Document id ' + data[0].documentID);
documentReference = dataRef[0].reference;
print('Document reference ');
print(documentReference);
} else {
print('name will be pushed on firestore');
}
}

Resources