Flutter: Shuffle BuiltList in Package built_value - flutter

I've developed an app that receives questions from an API.
The client uses built_value for checking.
Is there a way to shuffle the objects in
BuiltList<Aufgabe>fragen ?
This is what the client should do.
abstract class Aufgabenbundle
implements Built<Aufgabenbundle, AufgabenbundleBuilder> {
String get nameDerUebung;
String get dateiNameDerPdf;
BuiltList<Aufgabe> get fragen; //These objects should be mixed
Aufgabenbundle._();
factory Aufgabenbundle([updates(AufgabenbundleBuilder b)]) = _$Aufgabenbundle;
String toJson() {
return json
.encode(serializers.serializeWith(Aufgabenbundle.serializer, this));
}
static Aufgabenbundle fromJson(String jsonString) {
return serializers.deserializeWith(
Aufgabenbundle.serializer, json.decode(jsonString));
}
static Serializer<Aufgabenbundle> get serializer => _$aufgabenbundleSerializer;
}
abstract class Aufgabe
implements Built<Aufgabe, AufgabeBuilder> {
String get title;
String get frage;
int get schwierigkeit;
int get antwortZeit;
BuiltList<String> get vorgegebeneAntworten;
int get richtigeAntwort;
#nullable
int get angehakteWert;
Aufgabe._();
factory Aufgabe([updates(AufgabeBuilder b)]) = _$Aufgabe;
static Serializer<Aufgabe> get serializer => _$aufgabeSerializer;
}

The BuiltList is immutable, so you can't shuffle it directly. As with other mutating operations, you need to rebuild it instead.
When you do that, you get a ListBuilder which implements the List interface, including the shuffle method.
So:
var fragen = aufgabenbundle.fragen;
var gemischteFragen = fragen.rebuild((b) => b.shuffle());

Related

How to serialize private fields in json-serializable?

Minimal reproducible code:
#JsonSerializable()
class A {
final int _x;
A(int x) : _x = x;
factory A.fromJson(Map<String, dynamic> json) => _$AFromJson(json);
}
Note:
I don't want to make my private field _x public or define a public x getter.
This PR addresses what you want:
https://github.com/google/json_serializable.dart/pull/1256/files#diff-0acaf4c472e452d1e5d215a15fcd2266ccd02ab6abdfac0080c2fca845eb9096
You will be able to explicitly set includeFromJson and includeToJson on the private fields you want to include.
Example:
class X {
#JsonKey(includeFromJson: true, includeToJson: true)
int _includeMeToTheJsonParsing;
}
It was merged November 30th. Latest package version is v6.5.4, released at October 25th. So you will need to wait a little bit if you want the official release. Otherwise, you can point directly to the latest commit if you need it ASAP.
If you don't want to make your private field public or define a public getter, you can still use a serialization library like json_serializable or built_value to serialize and deserialize the class, but you will need to define a custom toJson method that manually serializes the private field. Here's an example of using json_serializable with a custom toJson method:
import 'package:json_annotation/json_annotation.dart';
part 'my_class.g.dart';
#JsonSerializable()
class MyClass {
final int _x;
MyClass(this._x);
Map<String, dynamic> toJson() => {
'x': _x,
};
factory MyClass.fromJson(Map<String, dynamic> json) => MyClass(json['x'] as int);
}
You can then use the fromJson and toJson methods to serialize and deserialize your class:
import 'dart:convert';
void main() {
// Serialize to JSON
MyClass obj = MyClass(42);
String json = jsonEncode(obj);
// Deserialize from JSON
MyClass obj2 = MyClass.fromJson(jsonDecode(json));
}

[DART]: Telling dart generics that an attribute will exist when used

Not sure if I'm using generics properly but is there a way I can let <T> know that it has (or will have) a certain attribute when it's used? This wouldn't be a problem if it weren't generics but since it's not I keep getting the error The getter 'id' isn't defined for the type 'T & Object'.
class Foo<T> {
List<T> items = [];
removeById(int id) {
items.removeWhere((T element) => element.id! == id); // Error
}
}
You can have either static (compile-time) checks or runtime checks. For compile-time checks, you would need to parameterize your generic on some base interface. For example:
abstract class HasId {
int get id;
}
class MyClass implements HasId {
#override
final int id;
MyClass(this.id);
}
class Foo<T extends HasId> {
...
}
If you really want duck-typing, that inherently requires using dynamic to disable static type-checking and relying on runtime checks:
class Foo<T> {
List<T> items = [];
void removeById(int id) {
items.removeWhere((element) => (element as dynamic).id! == id);
}
}
If there's a possibility that instances of T might not have an id member, you will need to catch a potential NoSuchMethodError yourself.

How to send dynamic data in Retrofit API calls in flutter?

I want to add dynamic data in my api calls so using retrofit I am trying this:
#RestApi(baseUrl: Constants.baseUrl)
abstract class AppServiceClient {
factory AppServiceClient(Dio dio, {String baseUrl}) = _AppServiceClient;
#GET("/get-all-country")
Future<AllCountryResponse> getCountries();
#GET("/get-state/{input}")
Future<AllCountryResponse> getStates(#Path("input") instance<StateId>().fetchCountryId);
}
This is my statId class:
class StateId {
StateId();
String countryId = "";
String get fetchCountryId => countryId;
setCountryId(String value) {
countryId = value;
}
}
I want to pass a dynamic data in the get-state api. How can I do this?
I found the solution to this, it was adding field to the parameter
#GET("/get-state/{id}")
Future<AllCountryResponse> getStates(#Path("id") String id);

Calling constructor of a class from type object in dart

I'm getting an error like
The expression doesn't evaluate to a function, so it can't be invoked.
Is there any workaround to achieve this I have different classes being returned from a map according to the key.
void main() {
Map<String, Type> map = {'user': User};
Type T = map['user']!;
User a = T(5);
print(a.id);
}
class User {
User(this.id);
final int id;
}

Passing constructor as argument in Flutter

I have API communication service in my Flutter app with 10+ different services, and 100+ API calls that heed to parse data. In order to reuse code I've decided to create some common parsing code that is going to parse data from API:
ApiResponse handleObjectResponse({
#required http.Response serverResponse,
#required Function objectConstructor,
}) {
if (serverResponse.statusCode == 200) {
dynamic responseObject = objectConstructor(json.decode(serverResponse.body));
return ApiResponse(responseObject: responseObject);
} else {
ApiError error = responseHasError(serverResponse.body);
return ApiResponse(error: error);
}
}
This way I am able to parse JSON object from API in a reusable way no matter what the Object class is, just by passing constructor function to this method.
When I call this method in any of the Services I've created for fetching data like this:
handleObjectResponse(serverResponse: response, objectConstructor: ChartData.fromJson);
I get error: The getter 'fromJson' isn't defined for the class 'ChartData'.
Try importing the library that defines 'fromJson', correcting the name to the name of an existing getter, or defining a getter or field named 'fromJson'.
Where I think the problem is is in this model class and factory statement, but I don't know how to fix it:
class ChartData {
List<ChartDataPoint> points;
ChartData({
this.points,
});
factory ChartData.fromJson(Map<String, dynamic> json) {
List jsonPoints = json["data"];
return ChartData(
points: List.generate(jsonPoints.length,
(i) => ChartDataPoint.fromJsonArray(jsonPoints[i])));
}
}
You cannot pass constructors as functions. You need to create a function what will call the constructor instead:
(int a) => Foo(a);
Just a 2022 update: since 2.15 it's possible by Class.new, see the complete issue: https://github.com/dart-lang/language/issues/216.
class A {
final String a;
const A(this.a);
#override
String toString() => 'A($a)';
}
class B {
final String b;
const B(this.b);
#override
String toString() => 'B($b)';
}
void main() {
final List<Object Function(String)> constructors = [A.new, B.new];
for (final Object Function(String) constructor in constructors) {
final Object instance = constructor('My Constructor Parameter');
if (instance is A) {
print(instance.toString());
}
}
}
Note that if you're using named params, both class constructors must have the same param name, otherwise the constructor signatures won't match and then it will generate this static error:
The element type X can't be assigned to the list type Y.