Combine freezed lib with model state - flutter

I have a #freezed class defined like below and I need to be able to use its artistId in order to make an API call and get an Artist object. Since that object will be used across multiple screens and filtering functions, I don't want to make requests every single time.
Since the freezed library doesn't allow mutable state in annotated classes, what would be the best workaround?
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../abstract/repository/artist/artist_repository.dart';
import '../artist/artist.dart';
import '../fair/fair.dart';
import '../provenance/provenance.dart';
part 'photo.g.dart';
part 'photo.freezed.dart';
enum OwnershipType { user, artist, collector }
#freezed
class Photo with _$Photo {
factory Photo({
required int id,
required int artistId,
required String title,
required String year,
double? height,
double? length,
double? depth,
required String photo,
required String approved,
required String creationDate,
#JsonKey(name: 'show') Fair? fair,
// required List<Fair> exhibited,
required String description,
required List<Provenance> provenance,
required String submittedDate,
String? submitterName,
bool? pendingUpdate,
String? dimensionUnits,
#Default(<String>[]) List<String> galleryNames,
String? thumb,
}) = _Photo;
factory Photo.fromJson(Map<String, dynamic> json) => _$PhotoFromJson(json);
Photo._();
String get firebasePhoto {
return 'https://storage.googleapis.com/download/storage/v1/b/blob-venture.appspot.com/o/${photo.replaceAll('/', '%2F')}?alt=media';
}
//following is not allowed sadly
// Artist? artist;
// Future<void> fetchArtist(ArtistRepository artistRepository) async {
// artist = await artistRepository.getArtist(id: artistId);
// }
}
I've thought about making another model that will hold the Photo object along with its corresponding Artist object after fetching it, but if there's a better way to go about it, I'd love to know.
Maybe there's another library that allows for such cases? This library was selected by the previous dev of the project but it's not been a great tool for me so far.

You could make a service that handle the API requests.
You could have a state management solution (e.g. flutter_bloc) that handles calling the service. The state management solution could be constructed to provide all the information down a specific widget tree, or the entire app.
Convert the result from your API to a "freezed-model-classes".
freezed is amazing and imo is a great choice by your colleagues. Just make sure to realize that package shouldn't handle everything in your app. In other words, if you are making API calls from your model classes, you could improve the architecture of your app.
To expand further, you could put Artist? as a part of your Photo class by putting it with the rest of your parameters. And you can use the copyWith method to "add" (actually copy with added data as name suggests) new information to an existing Photo object.

Related

Why when using Riverpod with StateNotifier, setting a (state) field to null, the listeners are not notified?

So this is a Flutter application using Dart language with the Riverpod package for state management. The intent is that an application has a user if, they are signed in, and is set to null if they are not (or signed out). I found that setting the user to null did not notify the listeners, so I tried with a basic nullable String field, called name. I received the same result.
Example below is for the simple nullable String field. Another thing is, I use the standard data class generator plugin to generate all the boiler plate code such as equality, copywith, hashcode and so on.
So let's assume I have the following using flutter_riverpod: 2.1.3
class AppSettings {
User? user;
String? name;
AppSettings({
this.user,
this.name,
});
AppSettings copyWith({
User? user,
String? name,
}) {
return AppSettings(
user: user ?? this.user,
name: name ?? this.name,
);
}
// Additional data class generator methods
}
class AppSettingsNotifier extends StateNotifier<AppSettings> {
AppSettingsNotifier() : super(AppSettings());
void updateUser(User? user) {
state = state.copyWith(user: user);
}
void updateName(String? name) {
state = state.copyWith(name: name);
}
}
final appSettingProvider =
StateNotifierProvider<AppSettingsNotifier, AppSettings>(
(ref) => AppSettingsNotifier());
Then when I set the name field as follows:
ref.read(appSettingProvider.notifier).updateName(null);
Then my listeners aren't reacting and the widgets aren't rebuilt.
On the other hand, if I set the name to an actual string:
ref.read(appSettingProvider.notifier).updateName("Bruce Lee");
It would instantly update. Even with empty string it will also notify the listeners. So it seems something special is happening with null specifically.
Why is "null" not causing a notification to listeners?
What am I missing here?
I've tried reading the manual, googling and countless attempts at debugging. Unfortunately, I do not understand enough of the underlying Riverpod/Flutter code to get to the bottom of it.
By looking at the provided code, So far what I can see a potential issue in your copyWith method. Bcz in flutter, when using a copyWith method the common convention is to keep the instances past data if the data given inside the copyWith is null.
Right now, it looks like that can be the issue here. So when you pass a null data, The new instance comes with the name value from your past object. So try to check if it's the case in your AppSettings model class.

Arguments of a constant creation must be constant expressions while implement a model using freezed

I'm having a small issue converting this class using freezed since is not possible to have a default value which is not constant, so the line DateTime nocache= DateTime.now() is not possible to be transformed into #Default(DateTime.now()) DateTime nocache
Here the full code
import 'package:equatable/equatable.dart';
abstract class DynamicLinkState extends Equatable {
const DynamicLinkState();
#override
List<Object> get props => [];
}
class DynamicLinkInitial extends DynamicLinkState {
#override
String toString() => 'DynamicLinkInitial';
}
class DynamicLinkToNavigate extends DynamicLinkState {
final String path;
final DateTime nocache = DateTime.now();
DynamicLinkToNavigate({this.path});
#override
List<Object> get props => [path, nocache];
#override
String toString() => 'DynamicLinkToNavigate';
}
How can I eventually do that?
Additional context
I'm using a nocache attribute here because bloc is optimize to not send the same event multiple times, but this is a valid use case in this situation since i might expect the user to receive more then one time the same dynamic link. So the solution we found is simply to invalidate this optimization by passing an always changing nocache parameter.
So a valid solution to this question might also be to simply remove this workaround in favor of a more solid solution.
I ran into the same problem this morning. There's not a direct way that I could see to achieve this. However, a different approach to the problem may be in order.
Hopefully you are using a state management solution in your app. In my case it's RiverPod. So, rather than having the model know directly how to generate a default value for a property I elected to have my RiverPod state provider generate the value when creating the model.
So with a model like this...
#freezed class Pix with _$Pix {
const factory Pix({String? id, String? description, String? uri}) = _Pix;
}
...where I need the id to be generated (in my case it's a uuid), I have a StateNotifier handle that for me
class PixList extends StateNotifier<List<Pix>> {
PixList([List<Pix>? initialPixList]) : super(initialPixList ?? []);
void add(String description, String uri) {
state = [...state,
Pix(
id: _uuid.v4(),
description: description,
uri: uri
)
];
}
}
Since it's my state provider that should be handling creating objects, I'm effectively delegating responsibility for assigning that initial id value to the provider, not the model itself which remains nice and thin

auto converting json to object in dart flutter

I search google and stackoverflow but did not find some auto converting lib like in C#
I need something in dart-flutter alternative to this code in C#
string jsonTokenData = "{my token json data}";
TokenModel getTokenModel = Newtonsoft.Json.JsonConvert.DeserializeObject<GetTokenModel>(jsonTokenData);
Update
I read the Documentation and I know how to map models with json
this question to who know how Newtonsoft lib work on C#
if you do not know Newtonsoft: the Idea is to convert json data to a model automatically without writing map for each model. Also I know there is a tool to create that map with json for each model automatically but still make code ridiculous.
Update2: as I get a suggestion from community for a similar question that not answer my question. so this is a another explanation:
I have a lot of models these models are updated by time as clients requested or adding new features to some models.
so when an update happen I just need to add the extra properties that has been added to these models, I do not need to warry every time about mapping, spell mistaken or using some tools again to regenerate these codes.
so I'm asking for a function that take two parameters
first one is the type of the model
second one the json string that hold the data
[then in return is an object instance of the passed type]
for simple example if I have this class:
class Car {
String name;
String type;
}
Then I could keep it clean this way without getting it dart with other methods:
Car({
this.name,
this.type,
});
factory Car.fromJson(Map<String, dynamic> json) => Car(
name: json["name"],
type: json["type"],
);
Map<String, dynamic> toJson() => {
"name": name,
"type": type,
};
by the way lib in C# is also take care about arrays, lists and nested classes
I hope this time it explained well
I've added a new Dart package to Jsonize custom classes within any data structure.
This is how your Car example would look like:
import 'package:jsonize/jsonize.dart';
class Car implements Jsonizable<Car> {
final String name;
final String type;
Car({required this.name, required this.type});
// Jsonizable implementation
factory Car.empty() => Car(name: "", type: "");
#override
String get jsonClassCode => "car";
#override
Map<String, dynamic> toJson() => {"name": name, "type": type};
#override
Car? fromJson(value) => Car(name: value["name"], type: value["type"]);
}
void main() {
// Register classes
Jsonize.registerClass(Car.empty());
// Now you can jsonize it wherever
Map<String, dynamic> myMap = {
"my_car": Car(name: "Jessie", type: "Tesla"),
"purchase_dt": DateTime.now(),
};
var jsonRep = Jsonize.toJson(myMap);
var hereIsMyMap = Jsonize.fromJson(jsonRep);
print(hereIsMyMap);
List<dynamic> myList = [
Car(name: "Jessie", type: "Tesla"),
Car(name: "Bob", type: "Ford"),
];
jsonRep = Jsonize.toJson(myList);
var hereIsMyList = Jsonize.fromJson(jsonRep);
print(hereIsMyList);
}
just import dart:convert library like this :
import 'dart:convert';
Then use this code :
json.decode(json)
Take a look at this link : dart:convert library
Doing some research would allow you to not ask here on SO.
JSON decoding is well documented on the flutter site. It points to using the jsonDecode function, which parses the String and returns the resulting JSON object.
String jsonTokenData = "{my token json data}";
dynamic json = jsonDecode(jsonTokenData);
You can pass the decoded object to a custom constructor of your object like .fromJson, which accepts a Map/List depending on your JSON data.
If for some reason creating a constructor to accept JSON data would be too much work/typing or if you're having trouble doing it yourself, you can use a JSON to Dart service like this.

How to make and relate complex table with each other in sqfllite flutter?

I am using sqflite package to store data offline as well once it got from API. I had seen a couple of examples and all used the simple Strings and not lists in their models as a type and due to that I am feeling difficulty in the conversion of these classes in respective tables and link them so that data can be fetch easily once synced. Can someone guide me on how to make create and link tables in sqflite with these types of classes so that they work perfectly?
TodayDeliveriesModel class
class TodayDeliveriesModel {
final String Name;
final List<ItemsModel> allItems;
final bool pending;
TodayDeliveriesModel({this.Name, this.allItems, this.pending});
}
Items Model class:
class ItemsModel {
String name;
String quantity;
ItemsModel({this.name, this.quantity});
}
fetch and Parse JSON data from the server.
Create Database Table with just 3 parameters.
class DeliveryModel{
String name;
String items;
bool isPending;
}
then when you save data in the database, just covert List to String
and save this string as the items.
After then when you get this string from database convert this into again List

Flutter / Inject.dart - Is it possible to simplify or generate code for a 'provided' class instantiation?

Been doing some research on Flutter Dependency Injection,and I kinda settled on Inject.dart
However, I am having some trouble using inject.
Is there any way to simplify instantitation of a injected class?
I have this MyClass, which I need to instantiate passing a HomeStore, however, how can I instatiate it without calling global acessors? (such as the ones made on the Injector file).
It seems I could just use the Get_It package otherwise, which would get me the same results, and without code generation, but I don't quite like the whole global access thing.
My sample:
// current code
class MyClass(){
store = HomeStore(AppInjector().globalHudStore, AppInjector().globalErrorStore);
}
// desired code
class MyClass(){
#instance ?
store = HomeStore();
store = HomeStore.instanciate?();
}
class HomeStore {
#provide
HomeStore(this._globalHudStore, this._globalErrorStore);
final GlobalHudStore _globalHudStore;
final ErrorStore _globalErrorStore;
}
If you are willing to work with json, you might want to consider this package json_serializable. Just search for it at pub.dev.
I can give a little example. Let's say you have a User class/model.
part 'user.g.dart';
#JsonSerializable()
class User {
int id;
#JsonKey(name: 'user_type_id')
int userTypeId;
String email;
#JsonKey(name: 'user_detail')
UserDetail userDetail;
#JsonKey(name: 'user_type')
UserType userType;
#JsonKey(name: 'user_location')
UserLocation userLocation;
User();
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
and you fetched a User data via API, and want to create a User variable and fill it with data without having to do that manually.
You can actually do that with just this line of code
User user = User.fromJson(jsonUser);
These even automatically json serializes classes in your User class, as long as those classes are also json serializable, which is defined with #JsonSerializable()
So even with just that single line of code, if the jsonUser which came from the API also has values for UserDetail, UserType, and UserLocation, you can also access them.