Provide generic arguments to DocumentReferences in JsonConverter - flutter

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.

Related

how to target an element of our class with hive to change it

good morning,
I don't know how to access an element of my boxHive without rebuilding it
I would like to be able to update some information without changing the existing one
Here is how I do it here, I use the constructor of my model class to add notes but it rebuilds all my class at the same time I would like it to rebuild only ListNote
class InformationPatient extends StatefulWidget {
final Patients patients;
final int index;
const InformationPatient(this.patients, this.index, {Key? key})
: super(key: key);
#override
State<InformationPatient> createState() => _InformationPatientState();
}
class _InformationPatientState extends State<InformationPatient> {
late Box<Patients> boxPatient;
#override
void initState() {
super.initState();
boxPatient = Hive.box('Patient');
}
void _addNote(String newtitle, String newnote, String newconclusion) {
final newNOTE = Patients(
listOfNotes: [
ListNote(title: newtitle, note: newnote, conclusion: newconclusion)
],
);
boxPatient.put(widget.index, newNOTE);
Navigator.of(context).pop();
}
And here is my class model
import 'package:hive_flutter/hive_flutter.dart';
part 'listpatient.g.dart';
#HiveType(typeId: 0)
class Patients {
#HiveField(0)
final String? name;
#HiveField(1)
final String? firstname;
#HiveField(3)
final String? dateofbirth;
#HiveField(4)
final String? email;
#HiveField(5)
final String? phonenumber;
#HiveField(6)
final DateTime? date;
#HiveField(7)
final int? id;
#HiveField(8)
final List<ListNote>? listOfNotes;
const Patients({
this.name,
this.firstname,
this.dateofbirth,
this.email,
this.phonenumber,
this.date,
this.id,
this.listOfNotes,
});
}
#HiveType(typeId: 10)
class ListNote {
#HiveField(1)
final String? title;
#HiveField(2)
final String? note;
#HiveField(3)
final String? conclusion;
ListNote({
this.title,
this.note,
this.conclusion,
});
}
Actually Hive offers only Write and Read operations, as you can see from its official documentation for flutter
so in order to update an Object in your boxPatient Box you need to get it with this:
Patients patients = boxPatient.get(widget.index);
then make your edits over it, then save it again on that same key:
boxPatient.put(widget.index, patients);
you can say we just updated it with this process.

Flutter Hive store color

I need to store colour in my Hive database to retrieve in my eCommerce Application,
it gave me the error below saying that I need to make an adapter, can anyone tell me how to make a colour adapter?
part 'items_model.g.dart';
#HiveType(typeId: 0)
class Item {
#HiveField(0)
final String name;
#HiveField(1)
final double price;
#HiveField(2)
final String? description;
#HiveField(3)
var image;
#HiveField(4)
final String id;
#HiveField(5)
final String shopName;
#HiveField(6)
final List<Category> category;
#HiveField(7)
Color? color;
#HiveField(8)
int? quantity;
Item({
required this.category,
required this.image,
required this.name,
required this.price,
this.description,
required this.id,
required this.shopName,
this.color,
required this.quantity,
});
}
does anyone know how to generate or create Color Adapter? as I don't know-how
E/flutter ( 4621): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: HiveError: Cannot write, unknown type: MaterialColor. Did you forget to register an adapter?
I think the easiest thing to do here would be to store the int value of the color.
Here's an example.
final colorValue = Colors.blue.value; // colorValue is an integer here
So your Hive color could be stored like this
#HiveField(7)
int? colorValue;
Then, in your app when you're creating a color from storage it would look like this.
final item = Item(...however you initialize your Item);
final color = Color(item.colorValue);
Base on previous answers by #Cryptozord and #Loren.A one can simply just write specific adapter for color
import 'package:flutter/cupertino.dart';
import 'package:hive/hive.dart';
class ColorAdapter extends TypeAdapter<Color> {
#override
final typeId = 221;
#override
Color read(BinaryReader reader) => Color(reader.readUint32());
#override
void write(BinaryWriter writer, Color obj) => writer.writeUint32(obj.value);
}
and remember to register your adapter with
Hive.registerAdapter(ColorAdapter());
make sure your typeId is not taken by other models
now you can use
#HiveField(7)
Color? colorValue;
I was confused about this for a whole day and I figured out how to do it. You should instead create your adapter manually like so:
class ItemAdapter extends TypeAdapter<Item> {
#override
final typeId = 0;
#override
ThemeState read(BinaryReader reader) {
return Item(
color: Color(reader.readUint32()),
//the rest of the fields here
);
}
#override
void write(BinaryWriter writer, ThemeState obj) {
writer.writeUint32(obj.color.value);
writer.writeString(obj.name);
//the rest of the fields here
}
}
Color needs to saved and written as a Uint32 value because the Int32 value only has a range of -2147483648 to 2147483647 and an unsigned integer is 0 to 4294967295. The integer values of Color() can go past 2147483647. If Uint32 is not used then your values will not be stored correctly and weird colors will be saved instead.

Flutter Riverpod: Filter rebuilds with StateNotifier and .select()

This is my current state management solution
class UserState {
final int id;
final String name;
}
class UserNotifier extends StateNotifier<UserState> {
UserNotifier() : super(User(1, 'Pero Peric'));
}
final userNotifierProvider = StateNotifierProvider((ref) => UserNotifier());
I want to rebuild my UI only when the name changes not the id!
Riverpod provides a way to do this link but I can't get it working with my StateNotifier.
I would write it like this but it isn't working like this.
// inside Consumer widget
final userName = watch(userNotifierProvider.select((value) => value.state.name));
Can you refactor my code to work or propose another solution?
Any help is appreciated!
According to the doc, "this method of listening to an object is currently only supported by useProvider from hooks_riverpod and ProviderContainer.listen".
Try to create another provider, which you can use in UI.
final nameProvider = StateProvider<String>((ref) => ref.watch(userNotifierProvider.select((user) => user.name)));
class UserState {
UserState({
required this.id,
required this.name,
});
final int id;
final String name;
}
class UserNotifier extends StateNotifier<UserState> {
UserNotifier() : super(UserState(id: 1, name: 'Pero Peric'));
}
final userNotifierProvider =
StateNotifierProvider<UserNotifier, UserState>((ref) => UserNotifier());
In consumer,
final userName =
watch(userNotifierProvider.select((value) => value.name)); // value is the state

How to define proper state class in bloc while using freezed plugin

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?

Using a provider in a custom class flutter

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