I have the following code where I want to make sure my status will be localized in app, so I wanted to parse current locale and get value in needed language, but what it basically do, is simply remembering first language (locale) value parsed in it and staying with it till I restart my app
enum BillStatus {
sent,
processed,
...
}
extension BillStatusExtension on BillStatus {
static Map<BillStatus, String> names = {
BillStatus.sent: S.current.sent,
BillStatus.processed: S.current.processed,
...
};
String? get name => names[this];
}
S.current means AppLocalizationDelegate provided with intl package
When you do:
extension BillStatusExtension on BillStatus {
static Map<BillStatus, String> names = {
BillStatus.sent: S.current.sent,
BillStatus.processed: S.current.processed,
...
};
String? get name => names[this];
}
You're creating a names Map that's lazily initialized with whatever the values are of S.current.sent, S.current.processed, etc. are at the time names is first accessed. When the locale changes, that Map will never be updated and still will be referencing the same String objects.
You should do one of:
Look up S.current.sent, S.current.processed, etc. dynamically. An easy way to do this would be to change your Map to store Functions instead of Strings:
extension BillStatusExtension on BillStatus {
static Map<BillStatus, String Function()> names = {
BillStatus.sent: () => S.current.sent,
BillStatus.processed: () => S.current.processed,
...
};
String? get name => names[this]?.call();
}
Alternatively explicitly reinitialize the Map when the locale changes. This is more work and likely is overkill in this case, but it's a technique that you can use if recomputing values on every lookup is too expensive:
extension BillStatusExtension on BillStatus {
static Map<BillStatus, String> names = {};
static String _lastLocale = '';
static void _initializeNames() {
names[BillStatus.sent] = S.current.sent;
names[BillStatus.processed] = S.current.processed;
...
}
String? get name {
if (locale != _lastLocale) {
_initializeNames();
_lastLocale = locale;
}
return names[this];
}
}
Instead of reinitializing your Map lazily, you also could register a callback to do it immediately when the locale changes. See Is it possible to listen for system language change in Flutter?
Your code should look like this:
void main() {
final status = BillStatus.sent;
print(status.name); // Sent
}
enum BillStatus { sent }
class BillStatusHelper {
static const Map<BillStatus, String> names = {
BillStatus.sent: 'Sent',
};
}
extension BillStatusExtension on BillStatus {
String get name => BillStatusHelper.names[this] ?? 'Unknown';
}
Related
I have use case where Server can return a list that sometimes can be empty and sometimes can contain one or multiple values. For example: list1 = [text1, text2]. I have created an EnumClass where I have serialized text1 but I don't want to serialize text2 because it's not important for my case. I just want to serialize one specific value, text1 in this case. At the moment, this is giving me DeserializationError built_json_serializers.dart in BuiltJsonSerializers._deserialize because text2 is not serialized. Is there a way to ignore other values? Because the problem is, in this list can come many different values that I really don't care if they come or not.
Relevant code snippets(simplified):
abstract class Fields
implements Built<Fields, FieldsBuilder> {
factory Fields([void Function(FieldsBuilder) updates]) =
_$Fields;
Fields._();
static Serializer<Fields> get serializer =>
_$fieldsSerializer;
BuiltList<Field> get list1;
}
class Field extends EnumClass {
const Field._(String name) : super(name);
static BuiltSet<Field> get values => _$values;
static Field valueOf(String name) => _$valueOf(name);
static Serializer<Field> get serializer => _$fieldSerializer;
static const Field text1 = _$text1;
}
extension FieldEx on Field {
String get stringValue {
switch (this) {
case Field.text1:
return 'text1';
}
return '';
}
}
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;
}
}
so i recently started learning dart and I've found something kinda interesting.
why do we use constructors and getters/setters when we can achieve same results without them? (atleast when used for basic things).
class v1{
var name;
int age;
v1(this.name, this.age);
info(){
print("my name is $name and i am $age");
}
}
class v2{
var name = "bash";
int age = 100;
info(){
print("my name is $name and i am $age");
}
}
class v3{
var namee;
int agee;
String get name => namee;
int get age => agee;
set name(String name) => this.namee = name;
set age(int age) => this.agee = age;
info(){
print("my name is $name and i am $age");
}
}
void main(){
var x = v1("bash", 100);
x.info(); //my name is bash am i am 100
var z = v2();
var Z = v2();
Z.name = "vert";
Z.age = 20;
z.info(); //my name is bash and i am 100
Z.info(); //my name is vert and i am 100
var y = v3();
y.name = "rizz";
y.age = 40;
y.info(); //my name is rizz and i am 40
}
Here's a more correct version of your class:
class User {
final bool _isMale;
String _name;
int _age;
User(this._isMale, this._name, this._age);
bool isMale => _isMale;
String get name => _name;
int get age => _age;
set name(String name) {
// Sometimes you may want to update other properties here.
// For example:
// _isUpdated = true;
_name = name;
}
set age(int age) {
_age = age;
}
void info() {
print("my name is $name and i am $age");
}
}
Constructors are useful when you want to assign initial values to the class fields. They are essential if you need to assign final fields, as they are assignable only on class initialization (see _isMale field).
Setters are useful when you want to update other fields along with the field that's being modified.
Getters protect the internal state from being modified outside. In this example, nobody can change _isMale field.
You don't need to use getters and setters unless you have to.
You use getters and setters if you need to store the data in a private field, or if you want to modify it when saving or returning the value.
class Abc {
String _field;
String _otherField;
String anotherField; // No getters and setters required for this.
String get field => _field;
set field(String field) => _field = field;
String get otherField => "The value of otherField is: " + _otherField;
set otherField(String otherField) => _otherField = "[String] " + otherField;
}
As for constructors, you use them to initialize the object with custom values. When you need to work with immutable objects (which use final variables), you'll have to use constructors to set their initial value. You can also modify the incoming value according to your need before storing it,
class Def {
final field; // Dart generates getters for this field, but it's value can't be modified once the object is instantiated.
final _otherField; // No getters for this.
Def(String field, String otherField) {
this.field = "[String] $field"
this._otherField = "[String] $otherField"
}
String describeMe() {
return "[Def]: field: $field, _otherField: $_otherField"
}
}
Looking for a clean way to parse out data from an API.
The API returns data for creating “Boat” and “Car” models wrapped up in metadata.
{
“timestamp” “0000”,
“data”: {
“count” “1”,
“results” [
{
“name”: “boatyMcBoatFace”
}
]
}
}
I want to create 2 classes for the metadata and also the boat/car data using custom fromJson() methods.
Current implementation is something like this
Class Boat {
String name;
Boat.fromJson(json) {
this.name = json[‘name’]
}
static List<Boat> listFromJson(List<dynamic> json) {
return json.map((c) => Boat.fromJson(c)).toList();
}
}
ResponseModel<T> {
String timestamp
DataModel data
ResponseModel.fromJson(json) {
this.timestamp = json[‘timestamp’]
this.data = DataModel<T>.fromJson(json[‘data’])
}
}
DataModel<T> {
String count
List<T> results
DataModel.fromJson(json) {
this.count = json[‘count’]
this.results = json[‘results’] // change this, see below
}
}
Currently I'm creating a ResponseModel, which in turn creates a DataModel.
But then I'm manually creating and setting Boats using:
// yes
final res = methodThatMakesHttpRequest();
final apiResponse = ResponseModel<Boat>.fromJson(res);
// no
final boats = Boat.listFromJson(apiResponse.data.results);
apiResponse.data.results = boats;
Ideally I would remove those last two lines, and instead have Boats get created dynamically within DataModel.fromJson with something like
DataModel.fromJson(json) {
this.count = json[‘count’]
T.listFromJson(json[‘results’])
}
But this of course does not work as T.listFromJson does not exist.
The Plan:
So now what I basically want is to take my propertys out of the class, let the user pick some and then pull a List with ONLY those propertys out of MongoDB.
The Code:
here is where the method starts:
private void DoStuffExecute(object obj)
{
Class class= new Class();
ExtractClass(class);
if (propList != null)
{
var result = classService.DoStuff(propList);
}
}
in "ExtractClass()" the Propertys are being pulled out of the Class.
void ExtractClass(object obj)
{
foreach (var item in obj.GetType().GetProperties())
{
propList.Add(item.Name);
}
}
and finally in "classService.DoStuff()" i try to set the "fields".
public List<class> DoStuff(List<string> Props)
{
try
{
var filter = Builders<class>.Filter.Empty;
var fields = Builders<class>.Projection.Include(x => x.ID);
foreach (var item in Props)
{
string str = "x.";
str += item.ToString();
fields = Builders<class>.Projection.Include(x => str);
fields = Builders<class>.Projection.Include(x => item);
}
var result = MongoConnectionHandler.MongoCollection.Find(filter).Project<class>(fields).ToList();
return result;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
var result = new List<class>();
return result;
}
}
when i run the programm it gives me an "Unable to determine the serialization information for x=> value"... since im giving it a string.
The Question:
Does anyone have an Idea how to repair the code above or even make the plan work in another way?
thank you.
First of all: you are using such code lines as : var filter = Builders<class>.Filter.Empty; It is not possible, because class is a reserved keyword in c# (https://msdn.microsoft.com/en-us/library/x53a06bb.aspx) I assume, it's your Model, and i will speak about it as about Model class.
Include Filter needs Expression as a parameter, not a string, you should construct is as a expression. That's the second thing. Third, you should combine your includes as a chain, So your part of creating Include Filter from string List should look like:
var filter = Builders<Model>.Filter.Empty;
var fields = Builders<Model>.Projection.Include(x => x.Id);
foreach (var item in Props)
{
var par = Expression.Parameter(typeof(Model));
var prop = Expression.Property(par, item);
var cast = Expression.Convert(prop, typeof(object));
var lambda = Expression.Lambda(cast, par);
fields = fields.Include((Expression<Func<Model, object>>)lambda);
}
I have all expresiions separate for better understanding: first you create Parameter (x=>), than you add property (x=>x.Property1), than you should cast it to object, and after all create Lambda Expression from it.
And now the last part: You don't need all of it, Include function could get jsut a string as a parameter. So you could instead of all expression call write this:
fields = fields.Include(item);