I am trying to understand, how can I use the bloc pattern (specifically state) properly. I am facing this issue for more than a month, but not able to figure out a solution.
Let's consider, I have defined state class following way,
#freezed
abstract class ExampleState with _$ExampleState {
const factory ExampleState.initial() = Initial;
const factory ExampleState.getDataFromServer() = GetDataFromServer; //Thsi will return the ServerData Object.
const factory ExampleState.useServerData() = UseServerData;
const factory ExampleState.changeServerDataAndUpload() = ChangeServerDataAndUpload;
}
Let's consider our Server Data Model is the following way
class ServerData {
final String userId;
final String firstName;
final String lastName;
final String fullAddress;
final String fatherName;
ServerData(
this.userId,
this.firstName,
this.lastName,
this.fullAddress,
this.fatherName,
);
}
In this example, we are able to see, GetDataFromServer, UseServerData, and ChangeServerDataAndUpload state is sharing the same ServerData object. How should I design my state such that, the same DataModel object can be shared between different states?
Related
I'm using json_serializable's JSONConverter interface to convert Firestore's DocumentReferences into valid JSON objects.
This is the JSON converter class. It tries to be generic:
class DocumentSerializer<T>
implements JsonConverter<DocumentReference<T>, DocumentReference<T>> {
const DocumentSerializer();
#override
DocumentReference<T> fromJson(DocumentReference<T> docRef) => docRef;
#override
DocumentReference<T> toJson(DocumentReference<T> docRef) => docRef;
}
And this is the usage of the annotation:
#JsonSerializable(explicitToJson: true)
class Recipe {
#DocumentSerializer<Recipe>()
final DocumentReference<Recipe> id;
#DocumentSerializer<User>()
final DocumentReference<User> authorId;
final String title;
final String description;
final String imageUrl;
final List<DocumentReference<User>> likes;
final List<String> ingredients;
final List<String> steps;
.
.
.
}
Unfortunately, this does not work. The build runner fails when it reaches the first usage of the annotation, which is the id field. Is there a way to provide generics to DocumentReferences in JsonConverter which can be serialized properly?
I found a workaround; albeit less elegant than what I'm aiming for. Turns out separate JsonConverters need to be created for each DocumentReference type.
For example, if I have an id field of type DocumentReference<Recipe> for the Recipe class , I would need to create the following JSON converter...
class RecipeSerializer
implements
JsonConverter<DocumentReference<Recipe>, DocumentReference<Recipe>> {
const RecipeSerializer();
#override
DocumentReference<Recipe> fromJson(DocumentReference<Recipe> json) => json;
#override
DocumentReference<Recipe> toJson(DocumentReference<Recipe> object) => object;
}
...and use it as such:
#JsonSerializable(explicitToJson: true)
class Recipe {
#RecipeSerializer()
final DocumentReference<Recipe> id;
#UserSerializer() // <- same goes for other types
final DocumentReference<User> authorId;
final String title;
final String description;
final String imageUrl;
final List<DocumentReference<User>> likes;
final List<String> ingredients;
final List<String> steps;
.
.
.
}
A bit more verbose, but I'll take it unless someone comes up with a more efficient answer.
I have just started learning freezed. I have a GameBase class below:
import 'package:json_annotation/json_annotation.dart';
part 'game_base.g.dart';
#JsonSerializable()
class GameBase {
final int id;
final String facilityName;
final ActivityType activityType;
final Level level;
final DateTime startTime;
final DateTime endTime;
final int participantsNumber;
final String admin;
const GameBase(
{required this.id,
required this.level,
required this.activityType,
required this.admin,
required this.startTime,
required this.facilityName,
required this.endTime,
required this.participantsNumber});
factory GameBase.fromJson(Map<String, dynamic> json) =>
_$GameBaseFromJson(json);
}
Now i have another class called Game, which extends from GameBase. I'm trying to use freezed on this class. I also have a getter in this class. Game class is shown bellow:
part 'game.freezed.dart';
part 'game.g.dart';
#freezed
class Game extends GameBase with _$Game {
Game._();
factory Game({
required List<UserBase> participants,
required String? gameDescription,
required String? activityGroundsName,
required DateTime day,
required double lat,
required double lng,
required int id,
required Level level,
required ActivityType activityType,
required String admin,
required DateTime startTime,
required String facilityName,
required DateTime endTime,
required int participantsNumber,
}) = _Game;
factory Game.fromJson(Map<String, dynamic> json) => _$GameFromJson(json);
get facilityActivityText {
if (activityGroundsName == null) {
return facilityName;
} else {
return facilityName + " - " + activityGroundsName!;
}
}
}
since I have a getter in this class, I must have a private custructor, as mentioned in freezed documentation. However, by doing so I get an error since I extend from GameBase and must call its costructor with its fields.
*Note: I know I can move one field up to GameBase and then have my getter over there without any issue, but since I've just started working with freezed I want to understand it better and find out if there's any way to handle this?
I think your problem's same as this thread. Hope this thread will answer ur questions.
Flutter/Dart: Subclass a freezed data class
This is my Hive model and I already generate the adapter:
#HiveType(typeId: 7)
class GoalModel {
#HiveField(0)
bool progessNotificaitons;
#HiveField(1)
List<DayModel> daysToWork;
#HiveField(2)
int amountGoal;
#HiveField(3)
DateTime initialDate;
GoalModel({
this.amountGoal,
this.daysToWork,
this.initialDate,
this.progessNotificaitons,
});
}
class DayModel {
String label;
int index;
bool gonnaWork;
DayModel({
this.gonnaWork,
this.index,
this.label,
});
}
The issue is that does not allow me save the daysToWork which is a list of the class DayModel which does not have an adapter. the question is: is required generate an adapter or any special config to save a list of a type of object?
Thanks in advance.
The response is YES, is required generate an register a new adapter even if you just are going to issue the object into a list of another object.
https://flutter.dev/docs/cookbook/networking/fetch-data
In the last 'complete example' of the above page,
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
It is an Album class to receive the JSON string received in the request and handle it in the application,
The constructor provides a factory constructor in addition to the normal constructor.
About the factory constructor,
https://dart.dev/guides/language/language-tour#constructors
I have read the Factory constructors section of the above page.
The factory constructor of the Logger class in the sample does not always create a new instance, so
I can understand adding the factory keyword,
Is it necessary to use the factory constructor even in the Album class of this Complete example?
In the case of the Album class, since the normal constructor is used in the factory constructor,
I feel that this factory constructor (Album.fromJson) always creates a new instance.
In fact
Future<Album> fetchAlbum() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/albums/16');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
var temp=json.decode(response.body);
return Album(userId:temp['userId'],id:temp['id'],title:temp['title']);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
As you can see, it seems that it works without any problems even if I try using only the normal constructor.
Is there any advantage to preparing and using a factory constructor?
Or is there something wrong without using the factory constructor in this situation?
I'm not sure when to use the factory constructor in the first place,
Is there a clear definition?
Before diving into factory as keyword in flutter, you might have a look at Factory as design pattern to have the full picture in mind.
The main benefit of using factory design pattern
That is, the Factory Method design pattern defines an interface for a class responsible for creating an object, hence deferring the instantiation to specific classes implementing this interface. This resolves the issue of creating objects directly within the class which uses them.
Also, it enables compile-time flexibility via subclassing. When objects are created within the class, it is very inflexible since you cannot change the instantiation of the object independently from the class — the class is committed to a particular object. By implementing the pattern, subclasses can be written to redefine the way an object is created.
For more info, see here
and as docs refers
Use the factory keyword when implementing a constructor that doesn’t always create a new instance of its class. For example, a factory constructor might return an instance from a cache, or it might return an instance of a subtype. Another use case for factory constructors is initializing a final variable using logic that can’t be handled in the initializer list.
So it's all about hiding the creation logic from outside world.
And of course you can do the following
return Album(userId:temp['userId'],id:temp['id'],title:temp['title']);
But if you did that in many different components or classes let's say, so whenever you change the logic behind the creation of Album object you will be in-need to change it over all places.
On the other hand the classes which use Album class they only care about having an Object of Album, they care not about how it got instantiated, so if you put the logic of having an instance outside the class itself you are going into what's called spaghetti code
You can use factory to test for example if the json data returned by som request is null so you return a null dirctly by the use of factory named constructor for example look at this
//class for Product, Brand, Model
class PBM {
static const String pbmCollectionName = 'productsBrandsModels';
static const String pbmIdField = 'pbmId';
static const String pbmNameField = 'pbmName';
static const String parentIdField = 'parentId';
static const String iconUrlField = 'iconUrl';
//general
final String pbmId;
final String pbmName;
final String parentId;
//icon
final String iconUrl;
PBM({
this.pbmId,
this.pbmName,
this.parentId,
this.iconUrl,
});
Map<String, dynamic> toMap() {
return {
'pbmId': pbmId,
'pbmName': pbmName,
'parentId': parentId,
'iconUrl': iconUrl,
};
} //end of toMap method
factory PBM.fromFirestore(Map<String, dynamic> firestore) {
//here the benefit of factory comes into play: it will return a null
//otherwise it gonna create the object
if (firestore == null) return null;
return PBM(
pbmId: firestore['pbmId'],
pbmName: firestore['pbmName'],
parentId: firestore['parentId'],
iconUrl: firestore['iconUrl'],
);
} //end of PBM.fromFirestore named constructor
} //end of PBM class
I am creating a program that needs to use Provider to get values. I call my provider like below in a stateful widget
final user = Provider.of<Users>(context);
Now I would like to use the provider in a custom class
class UserInformation {
final user = Provider.of<Users>(context):
}
This won't work because context is not defined. kindly assist on how I can do this without using a BuildContext.
This is my class Users that I have on a separate dart file and use as a model for for my data streams.
class Users {
final String uid;
final String name;
final String department;
final String position;
Users({ this.uid, this.department, this.name, this.position });
}
This is the query I use to pull data from firestore
Stream<List<FormQuestions>> get userData{
return userCollection.where('name', isEqualTo: 'First Name').where('department', isEqualTo: 'department').snapshots()
.map(_userDataFromSnapshot);
}
I would like the name to be a variable that I get from say (user.name) from the model class. and the same for the department.
Thanks.
You can only access classes which are ChangeNotifiers in the descendant widgets in the tree below this provided ChangeNotifier because behind the scenes the Provider uses InheritedWidget (which uses context) to provide you with the ChangeNotifier you put up in the tree
So in your case there is no way to access the Users from UserInformation and you have to alter your code to make a workaround
Edit: this is a suggestion to achieve what you want if you are using this code inside a widget:
class UserInformation{
final User user;
UserInformation(this.user);
}
class SomeWidget extends StatefulWidget {
#override
_SomeWidgetState createState() => _SomeWidgetState();
}
class _SomeWidgetState extends State<SomeWidget> {
void someMethod(){
final User user = Provider.of<Users>(context);
UserInformation userInformation = UserInformation(user);
//do something
}
#override
Widget build(BuildContext context) {
return Container();
}
}
¡Hey! In the class you need to add "with ChangeNotifier":
class Users with ChangeNotifier {
final String uid;
final String name;
final String department;
final String position;
Users({ this.uid, this.department, this.name, this.position });
}
Hope help. Sorry for the english, yo hablo español. :D