I have a future that is used a few different times on some pages and I'm trying to include it instead and reference it when needed to cut down on the code overhead.
I've created a working future and wrapped it inside a class, the problem is that Flutter states that
"2 positional argument(s) expected, but 0 found."
I've tried String and Function type declarations for the client variable and I am including them, but I'm not sure what else I'm missing here?
FetchCats.getCats(client: http.Client(), filter: filter);
class FetchCats {
String client; <-- this shouldn't be string but I don't know what else to declare it as
int catType;
FetchCats({Key? key, required this.client, required this.catType});
Future<List<CatDetails>> getCats(http.Client client, int catType) async {
var ct = catType;
var catResults;
var response = await client.get(Uri.parse('/cats/breeds/$ct/'));
if (response.statusCode == 200) {
catResults = compute(convertCatDetails, response.body);
} else {
print("error");
}
return catResults;
}
}
List<CatDetails> convertCatDetails(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed
.map<CatDetails>((json) => CatDetails.fromJson(json))
.toList();
}
Your function is defined using positional parameters, rather than named parameters, but you are calling it with named arguments.
Here are a few changes that should allow you to use the class as I think you're intending:
It's not necessary to store catType on the class, since that's something you would probably change between requests - so it makes more sense to only pass it into the getCats function.
To fix the positional parameter issue, you can also change catType into a named parameter.
You don't need a Key parameter on the constructor - those are usually used with Widgets.
The type of the client should be http.Client, not String.
With those changes, your class should look something like this:
class FetchCats {
final http.Client client;
FetchCats({required this.client});
Future<List<CatDetails>> getCats({required int catType}) async {
int ct = catType;
var catResults;
var response = await client.get(Uri.parse('/cats/breeds/$ct/'));
if (response.statusCode == 200) {
catResults = compute(convertCatDetails, response.body);
} else {
print("error");
// Return an empty list, rather than the uninitialized catResults
return [];
}
return catResults;
}
}
Related
I am trying to create model and parse json data from api
for that i created the model class you can see below
class FeatureModel {
String? PlanFeatures;
bool? FeatureStatus;
FeatureModel({this.PlanFeatures, this.FeatureStatus});
FeatureModel.fromJson(parsonJson) {
PlanFeatures = parsonJson['PlanFeatures'];
FeatureStatus = parsonJson['FeatureStatus'];
}
}
now i am trying to parse json with the help of loop
let me show you my method
List<FeatureModel> featureModel = [];
Uri featureAPI = Uri.parse(
planFeatureApi);
apiCall() async {
try {
http.Response response = await http.get(featureAPI);
// print(response.statusCode);
if (response.statusCode == 200) {
var decode = json.decode(response.body);
print(decode);
for (var i = 0; i < decode.length; i++) {
print(i);
featureModel.add(
FeatureModel.fromJson(decode[i]),
);
}
}
} catch (e) {}
}
I am calling it here
onPressed: () async{
await apiCall();
}
but the problem is here
loop is not working while parsing data
in that particular code i remains on 0 only
when i removes featureModel.add( FeatureModel.fromJson(decode[i]), ); i started increaing till 10
please let me know if i am making any mistake or what
thanks in advance
Here is the sample of api respone
[{"PlanFeatures":"Video Link Sharing","FeatureStatus":"true"},{"PlanFeatures":"Email \u0026amp; Telephonic Support","FeatureStatus":"true"},{"PlanFeatures":"Remove Pixeshare Branding","FeatureStatus":"false"},{"PlanFeatures":"Add Custom logo on uploaded photos","FeatureStatus":"false"},{"PlanFeatures":"Get Visitor Info","FeatureStatus":"false"},{"PlanFeatures":"Mobile Apps","FeatureStatus":"false"},{"PlanFeatures":"Send Questionnaries","FeatureStatus":"false"},{"PlanFeatures":"Create \u0026amp; Send Quotation","FeatureStatus":"false"},{"PlanFeatures":"Online Digital Album Sharing","FeatureStatus":"false"},{"PlanFeatures":"Analytics","FeatureStatus":"false"}]
thanks
I found many errors, first, the fromJson is not a factory constructor and doesn't return a class instance from the JSON.
the second one is that the bool values from the sample you added are String not a bool so we need to check over it.
try changing your model class to this:
class FeatureModel {
String? PlanFeatures;
bool? FeatureStatus;
FeatureModel({this.PlanFeatures, this.FeatureStatus});
factory FeatureModel.fromJson(parsonJson) {
return FeatureModel(
PlanFeatures: parsonJson['PlanFeatures'],
FeatureStatus: parsonJson['FeatureStatus'] == "false" ? false : true,
);
}
}
The argument type 'List' can't be assigned to the parameter type 'Iterable'. I am facing the issue while try to add data to a list.
getBatchDates() async {
try {
isLoading(true);
var batchDates = await api.getUserEnrollmentBatches();
if (batchDates != null) {
return BatchDate.assignAll(batchDates);
}
} finally {
isLoading(false);
}
}
Make sure api.getUserEnrollmentBatches() is returning List<BatchDate>, then use _myList.addAll(batchDates) instead BatchDate.assignAll(batchDates).
var List<BatchDate> _myList = []; // Something inside
List<BatchDate> getBatchDates() async {
try {
isLoading(true);
var batchDates = await api.getUserEnrollmentBatches();
if (batchDates != null) {
return _myList.addAll(batchDates);
}
} finally {
isLoading(false);
}
}
A List class implemets Iterable class. They arnot the same thing. You have to change your List to Iterable. But also if your class BatchRecord isn't a subclass of BatchTiming you also won't be able to assign it to parameter
I'm trying to prepend the string )]}',\n to any response body that's JSON. I thought that an IAsyncResultFilter would be what I needed to use, but I'm not having luck. If I use the below code, it appends the text to the response since calling await next() writes to the response pipe. If I try and look at the context before that though, I can't tell what the response will actually be to know if it's JSON.
public class JsonPrefixFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var executed = await next();
var response = executed.HttpContext.Response;
if (response.ContentType == null || !response.ContentType.StartsWith("application/json"))
return;
var prefix = Encoding.UTF8.GetBytes(")]}',\\n");
var bytes = new ReadOnlyMemory<byte>(prefix);
await response.BodyWriter.WriteAsync(bytes);
}
}
Thanks to timur's post I was able to come up with this working solution.
public class JsonPrefixFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var response = context.HttpContext.Response;
// ASP.NET Core will always send the contents of the original Body stream back to the client.
var originalBody = response.Body;
// We want to write into a memory stream instead of the actual response body for now.
var ms = new MemoryStream();
response.Body = ms;
// After this call the body is written into the memory stream and the properties
// of the response object are populated.
await next();
if (response.ContentType != null && response.ContentType.StartsWith("application/json")) {
var prefix = Encoding.UTF8.GetBytes(")]}',\\n");
var prefixMemoryStream = new MemoryStream();
await prefixMemoryStream.WriteAsync(prefix);
await prefixMemoryStream.WriteAsync(ms.ToArray());
prefixMemoryStream.Seek(0, SeekOrigin.Begin);
// Now put the stream back that .NET wants to use and copy the memory stream to it.
response.Body = originalBody;
await prefixMemoryStream.CopyToAsync(response.Body);
} else {
// If it's not JSON, don't muck with the stream, so just put things back.
response.Body = originalBody;
ms.Seek(0, SeekOrigin.Begin);
await ms.CopyToAsync(response.Body);
}
}
}
Update:
I never liked the above, so I switched to this solution. Instead of calling AddJsonOptions, I took inspiration from ASP.NET's formatter to use this instead:
public class XssJsonOutputFormatter : TextOutputFormatter
{
private static readonly byte[] XssPrefix = Encoding.UTF8.GetBytes(")]}',\n");
public JsonSerializerOptions SerializerOptions { get; }
public XssJsonOutputFormatter()
{
SerializerOptions = new() {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
ReferenceHandler = ReferenceHandler.IgnoreCycles
};
SupportedEncodings.Add(Encoding.UTF8);
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
}
public override sealed async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
ArgumentNullException.ThrowIfNull(context, nameof(context));
ArgumentNullException.ThrowIfNull(selectedEncoding, nameof(selectedEncoding));
var httpContext = context.HttpContext;
var objectType = context.Object?.GetType() ?? context.ObjectType ?? typeof(object);
var responseStream = httpContext.Response.Body;
try {
await responseStream.WriteAsync(XssPrefix);
await JsonSerializer.SerializeAsync(responseStream, context.Object, objectType, SerializerOptions, httpContext.RequestAborted);
await responseStream.FlushAsync(httpContext.RequestAborted);
} catch (OperationCanceledException) when (context.HttpContext.RequestAborted.IsCancellationRequested) {
}
}
}
Now, when you call .AddControllers() you just set that as the first output formatter:
services.AddControllers(options => {
options.Filters.Add(new ProducesAttribute("application/json"));
options.OutputFormatters.Insert(0, new XssJsonOutputFormatter());
});
Obviously you could improve this to take serialization options in the constructor, but all my project would work exactly like the above so I just hardcoded it right in.
You could've used Seek on a steam to rewind it. Issue is, you can only keep adding onto default HttpResponseStream, it does not support seeking.
So you can employ the technique from this SO answer and temporarily replace it with MemoryStream:
private Stream ReplaceBody(HttpResponse response)
{
var originBody = response.Body;
response.Body = new MemoryStream();
return originBody;
}
private async Task ReturnBodyAsync(HttpResponse response, Stream originalBody)
{
response.Body.Seek(0, SeekOrigin.Begin);
await response.Body.CopyToAsync(originalBody);
response.Body = originalBody;
}
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var originalBody = ReplaceBody(context.HttpContext.Response); // replace the default stream with MemoryStream
await next(); // we probably dont care about the return of this call. it's all in the context
var response = context.HttpContext.Response;
if (response.ContentType == null || !response.ContentType.StartsWith("application/json"))
return;
var prefix = Encoding.UTF8.GetBytes(")]}',\\n");
var bytes = new ReadOnlyMemory<byte>(prefix);
response.Body.Seek(0, SeekOrigin.Begin); // now you can seek. but you will notice that it overwrites the response so you might need to make extra space in the buffer
await response.BodyWriter.WriteAsync(bytes);
await ReturnBodyAsync(context.HttpContext.Response, originalBody); // revert the reference, copy data into default stream and return it
}
this is further complicated by the fact that you need to restore reference to original stream, so you have to careful around that.
This SO answer has a bit more context.
i will import images BASE64 stored in DB.
code :
profileimage()async{
var userimage1 = await DBHelper().getuserIMAGE1('roro');
print(userimage1);
if(userimage1 == Null){
print('Empty');
}else{
setState(() {
userimage1.map((e) {
tmpimage = e['image0'];
}).toList();
print(tmpimage);
_TmpBytesImage = Base64Decoder().convert(tmpimage);
print(_TmpBytesImage);
return Image.memory(_TmpBytesImage);
});
}
}
File pimage = profileimage(); << error
and i got error 'flutter: Only static members can be accessed in initializers'
how can i do?
You need to call like below.
Future.delayed(Duration.zero, () {
// your code
});
The following items appear wrong:
Your return statement is inside a setstate() function so returns a value from that function.
processImage probably should be
Static Future processImage()
The call should be something like below but not at class level. It also needs to of type Image not of type File.
pimage = await processImage();
If there is nothing in the database, what do you want to return?
I tried to fetch data from the internet with moviedb API, I followed the tutorial at https://flutter.io/cookbook/networking/fetch-data/
but I'm getting the below error.
Invalid argument(s): Illegal argument in isolate message : (object is a closure - Function 'createDataList':.)
This my code
Future<List<DataModel>> fetchData() async{
final response = await http.get("https://api.themoviedb.org/3/movie/now_playing?api_key=d81172160acd9daaf6e477f2b306e423&language=en-US");
if(response.statusCode == 200){
return compute(createDataList,response.body.toString());
}
}
List<DataModel> createDataList(String responFroJson) {
final parse = json.decode(responFroJson).cast<Map<String, dynamic>>();
return parse.map<DataModel> ((json) => DataModel.fromtJson(json)).toList();
}
Screenshot of the error message
compute can only take a top-level function, but not instance or static methods.
Top-level functions are functions declared not inside a class
and not inside another function
List<DataModel> createDataList(String responFroJson) {
...
}
class SomeClass { ... }
should fix it.
https://docs.flutter.io/flutter/foundation/compute.html
R is the type of the value returned. The callback argument must be a top-level function, not a closure or an instance or static method of a class.
As per today (2020. Aug) the compute is working fine with static methods.
For me, the issue was that I was trying to return a http.Response object from the compute() methods.
What I did is I've created a simplified version of this class, containing what I need:
class SimpleHttpResponse {
String body;
int statusCode;
Map<String, String> headers;
}
Then I've updated the original method from this:
static Future<http.Response> _executePostRequest(EsBridge bridge) async {
return await http.post(Settings.bridgeUrl, body: bridge.toEncryptedMessage());
}
to this:
static Future<SimpleHttpResponse> _executePostRequest(EsBridge bridge) async {
http.Response result = await http.post(Settings.bridgeUrl, body: bridge.toEncryptedMessage());
if (result == null) {
return null;
}
SimpleHttpResponse shr = new SimpleHttpResponse();
shr.body = result.body;
shr.headers = result.headers;
shr.statusCode = result.statusCode;
return shr;
}
Worked like charm after this change. Hope this helps somebody ranning into similar problem.