How to handle enum in Hive Flutter? - flutter

I have an enum in my model class:
MyRepresentation { list, tabs, single }
I have already added an adapter and registered it.
I have given it a proper type id and fields.
It gives error:
HiveError: Cannot write, unknown type: MyRepresentation. Did you forget to register an adapter?

Did you register the enum too or just the model? Say your model file myrepresentation.dart looks like this:
import 'package:hive/hive.dart';
part 'myrepresentation.g.dart';
#HiveType(typeId: 1)
class MyRepresentation extends HiveObject {
#HiveType(0)
final String id;
#HiveType(1)
final Foo foo;
MyRepresentation({required this.id, required this.foo});
}
#HiveType(typeId: 2)
enum Foo {
#HiveField(0)
foo,
#HiveField(1)
bar,
}
Then you generate you type adapters and initialize both of them in your main:
void main() async {
await Hive.initFlutter();
...
Hive.registerAdapter(MyRepresentationAdapter());
Hive.registerAdapter(FooAdapter());
runApp(MyApp());
}
If you have done this and it is still giving you problems, you could try to put the enum in its own file and write its own part statement.
If this still isn't working I suggest simply storing the enum as int yourself in the TypeAdapter read() and write() methods like this:
#override
MyRepresentation read(BinaryReader reader) {
return MyRepresentation(
reader.read() as String,
Foo.values[reader.read() as int]
);
}
#override
void write(BinaryWriter writer, MyRepresentation obj) {
writer.write(obj.id);
writer.write(obj.foo.index);
}

Related

Dart Inherit class and Use it in functions of the parent class

I want to make a parent class which use ChangeNotifier. And from this class, I want to create two separate inherited classes that will provide list data to some parts of the app, and each will have its own separate list. But I could not figure out how each class could create its own list and only make operations on that list via using superclass. Can someone explain to me how can I manage this?
import 'package:flutter/material.dart';
class ObjectListProvider<T extends num, K extends Object> with ChangeNotifier {
final Map<T, K> _map = {};
Map<T, K> get map {
return {..._map};
}
K? getSingleObjectWithId(id) {
return _map[id];
}
void add(T id, K obj) {
_map[id] = obj;
notifyListeners();
}
void remove(T id) {
_map.remove(id);
notifyListeners();
}
}
import 'object_list_provider.dart';
import '../person.dart';
class PersonList extends ObjectListProvider {
final Map<dynamic, Person> _people = {};
}
import './object_list_provider.dart';
import '../group.dart';
import '../person.dart';
class GroupList extends ObjectListProvider {
final Map<dynamic, Group> _groups = {};
void addPersonToGroup<T extends num>(Person person, T id) {
super.add(id, person);
notifyListeners();
}
void removePersonFromGroup<T extends num>(Person person, T id) {
_groups[id]?.removePerson(id);
notifyListeners();
}
}
import './person.dart';
import './transaction.dart';
class Group {
final int _id;
String _name;
List<Person> _people = [];
List<Transaction> _transactions = [];
int _totalSpending;
Group({required int id, required String name, int totalSpending = 0})
: _id = id,
_name = name,
_totalSpending = totalSpending;
int get id {
return _id;
}
String get name {
return _name;
}
int get totalSpending {
return _totalSpending;
}
set name(String newName) {
_name = newName;
}
void addPerson(Person person) {
_people.add(person);
}
void removePerson<T extends num>(T id) {
_people = _people.where((Person person) => person.id != id).toList();
}
void addTransaction(Transaction transaction) {
_transactions.add(transaction);
}
}
class Person {
final int _id;
final String _name;
int _balance;
List<int> involvedGroups = [];
Person({required int id, required String name, int balance = 0})
: _id = id,
_name = name,
_balance = balance;
int get id {
return _id;
}
}
For example, I will use this provider in some other dart file as
final groupList = Provider.of<GroupList>(context);
groupList.add(....)
I refactored my code and came up with a solution that worked for me. Let me try to explain future reads as much as I can.
changed map from private to public. I am not sure it is the best way but it worked for this case. I was also able to work it with getter and setters but by doing that provider object did end up having two variables as _map and map.
import 'package:flutter/material.dart';
class ObjectListProvider<T extends num, K extends Object> with ChangeNotifier {
Map<T, K> map = {};
K? getSingleObjectWithId(id) {
return map[id];
}
void add(T id, K obj) {
map[id] = obj;
notifyListeners();
}
void remove(T id) {
map.remove(id);
notifyListeners();
}
}
Add generics after extending. This way I was able to access the map variable which previously I made publicly accessible. did the same thing for the PersonList as well.
import './object_list_provider.dart';
import '../group.dart';
import '../person.dart';
class GroupList extends ObjectListProvider<num, Group> {
void addPersonToGroup<T extends num>(Person person, T id) {
super.map[id]?.addPerson(person);
notifyListeners();
}
void removePersonFromGroup<T extends num>(Person person, T id) {
super.map[id]?.addPerson(person);
notifyListeners();
}
}
Other than these I did not changed anything related. Now I can call and use provider in some other file as
...
#override
Widget build(BuildContext context) {
final groupList = Provider.of<GroupList>(context);
final groups = groupList.map;
return ListView.builder(
itemCount: groups.length,
itemBuilder: (context, int index) {
return ListTile(
onTap: () => index,
title: Text(groups[groups.keys.elementAt(index)]!.name),
trailing: Text(
groups[groups.keys.elementAt(index)]!.totalSpending.toString(),
),
);
},
);
}
...
I'm working on something simmilar(not current version) at the moment. I would like to try and help if and where I can - though with the caveat that I'm still figuring out a lot of the basics myself.
Could you narrow-down or re-phrase the problem?
What I've done in the app I linked to above, as far as I think it might be relevant to you after a quick skim through your code, what I've done is:
To 'hold' the list and as much as possible of the functionality in the parent class.
In my case, each child class extends that parent - I'm calling it a 'listicle', and the type of object is specific to that childTypeListicle (for now).
The child classes hold specification of the types they list - in my case each it type shares an abstract parent Item class - as well as some config details for e.g. remote access and factory constructors. These' fields communicate up to the parent interfacing and its generic-ized functionality around the list through abstract method declarations enforced by the shared parent class. So that crteates a kind of the list as axel while it its reasonably item-type agnostic. Make sense?
Seems to work well so far, basically holds the logic this side of the plane-of-presentation implementation.
I also have tertiary connected interface elements like bottom-alert-bar connecting into fields of the parent 'listicle', and creating a kind of navigation ui that manipulates the list out onto the listview builder. Would like to also build in some partial local repository-ing but that doesn't seem a priority at the moment for this project.
I hope some of that helps somehow.

How to create Hive adapter for XFile class

I'm trying to store custom object that has variable storing list of XFile's; cross platform image representation from package called image_picker version 0.8.4+3. When writing an error occurs saying that im missing Adapter for XFile which is understandable, but I'm having hard time deciding how to declare such Adapter for external source file class.
This is my Receipt class that has some list of XFile's.
Note: I've removed nonmeaningful variables from snippet.
#HiveType(typeId: 0)
class Receipt extends HiveObject with EquatableMixin {
Receipt({
List<XFile>? files,
}) {
this.files = files ?? <XFile>[];
}
#HiveField(6)
late final List<XFile> files;
#override
List<Object?> get props => [
files,
];
}
Now I was thinking about two possible solutions; one - copy whole source code from XFile, add HiveType and HiveField decorators and generate adapter from that or two - create class that will extend from XFile and add decorators something like this:
#HiveType(typeId: 1)
class XFileAdapter extends XFile with HiveObjectMixin {
// access fields and add decorators here
}
but I have no clue how to add decorators to these field without overriding every one of them. And even if I knew how to do that, it turns out that XFile's doesn't have its own variables I've could add Hive decorators to, it simply takes paramethers and passes them down to XFileBase class since it further decides what to do with them.
Very similar question has been asked in this thread but the only aswer suggests creating new class from scratch that imitates source class which is not solution to my problem.
I think creating a new MyXFile class as you suggested might be the way to go. But as you've said you will need to override the properties you want to keep in hive. This code seems to be working as intended:
Code
import 'dart:typed_data';
import 'package:image_picker/image_picker.dart';
import 'package:hive_flutter/adapters.dart';
part 'my_xfile.g.dart';
#HiveType(typeId: 1)
class MyXFile extends XFile {
#override
#HiveField(1)
final String path;
#override
#HiveField(2)
final String? mimeType;
#HiveField(3)
final String? _name;
/// The base implementation of `XFileBase.name` throws an
/// [UnimplementedError] so we are overriding it to return a known
/// [_name] value.
#override
String get name {
if (_name != null) {
return _name!;
}
return super.name;
}
#HiveField(4)
final int? _length;
/// The base implementation of `XFileBase.length()` throws an
/// [UnimplementedError] so we are overriding it to return a known
/// [_length] value.
#override
Future<int> length() {
return _length != null ? Future.value(_length!) : super.length();
}
#HiveField(5)
final Uint8List? bytes;
#HiveField(6)
final DateTime? _lastModified;
/// The base implementation of `XFileBase.lastModified()` throws an
/// [UnimplementedError] so we are overriding it to return a known
/// [_lastModified] value.
#override
Future<DateTime> lastModified() {
return _lastModified != null
? Future.value(_lastModified!)
: super.lastModified();
}
MyXFile(
this.path, {
this.mimeType,
String? name,
int? length,
this.bytes,
DateTime? lastModified,
}) : _name = name,
_length = length,
_lastModified = lastModified,
super(
path,
mimeType: mimeType,
name: name,
length: length,
bytes: bytes,
lastModified: lastModified,
);
}
By using this I've been able to save and retrieve my object MyXFile and as it is extending XFile you should be able to use it the same way.
Then instead of having a List<XFile>? files in your Receipt class you will need a List<MyXFile>? files.

How to define concrete type from generic in Dart?

I am trying to make multiple Controller Types foo multiple Models for use with Provider in Flutter.
This is my Code:
//generic controller
class ModelController<E extends HiveObject> extends ChangeNotifier {
Box<E> _db;
ModelController(Box<E> db) {
_db = db;
}
// getters
List<E> get all => _db.values;
// create
void add(E item) {
_db.add(item);
notifyListeners();
}
void update(E item) {
item.save();
notifyListeners();
}
// delete
void delete(E item) {
item.delete();
notifyListeners();
}
}
class PacketController extends ModelController<Packet>{
PacketController(Box<Packet> db) : super(db);
}
The code for class PacketController is the only way I found to create a concrete type from a generic one.
Question:Is there a better way for this?
Shortly, I am looking for something like typedef in c++:
typedef IntVector std::vector<int>

How can I use variable of CubitState inside Cubit? Flutter/Bloc

so I don't have any idea how to take argument from mine Cubit state which is AnswerPicked in this case, there is a code from states file.
part of 'answer_cubit.dart';
abstract class AnswerState extends Equatable {
const AnswerState();
#override
List<Object> get props => [];
}
class AnswerInitial extends AnswerState {}
class AnswerPicked extends AnswerState {
final String answer;
AnswerPicked({
this.answer,
});
String toString() => '{AnswerPicked: $answer}';
}
I want to use it in Cubit function right there:
part 'answer_state.dart';
class AnswerCubit extends Cubit<AnswerState> {
final ExamScoreCubit scoreCubit;
AnswerCubit({
#required this.scoreCubit,
}) : super(AnswerInitial());
List<String> userAnswersList = [];
void pickAnswer(String answer) {
emit(AnswerInitial());
emit(AnswerPicked(answer: answer));
}
void takeAnswer(String questionAnswer, int type) {
if(state is AnswerPicked){
userAnswersList.add(state.answer); // state.answer don't work
scoreCubit.checkAnswer(AnswerPicked().answer, questionAnswer, type); // AnswerPicked().answer don't work
}
emit(AnswerInitial());
}
}
In void takeAnswer() I don't want to pass it throw argument inside the widget tree using context. Any ideas how to do it?
userAnswersList.add((state as AnswerPicked) .answer);

How can I create a non-modifiable class Flutter

I need to create a class called GeneralAppAndDeviceDetails which has the following fields:
import 'package:flutter/material.dart';
class GeneralAppAndDeviceDetails {
final bool isDarkModeEnabled;
final double deviceWidth;
final double deviceHeight;
final Color primaryColor;
final Color secondaryColor;
}
It basically stores general device and app information at the start of the program so that I don't need to create a new instance of Theme or MediaQuery class whenever I want to access these details.
The problem I'm facing is that how can I write this class so that after the fields' values are assigned, They will be unmodifiable? (so that nothing can change the field values)
(I tried to create a singleton class but I need to pass the values to the constructor and by using factory and private constructor, A user can create new classes with different parameters passed to the factory.)
The thing I need is to have static fields that can receive a value once and become unmodifiable after that. How can I achieve something similar?
Thank you
Update:
I wrote the class as below:
import 'package:flutter/material.dart';
class GeneralAppAndDeviceDetails {
final bool isDarkModeEnabled;
final double deviceWidth;
final double deviceHeight;
final Color primaryColor;
final Color secondaryColor;
static bool _isAlreadyCreated = false;
static GeneralAppAndDeviceDetails _instance;
factory GeneralAppAndDeviceDetails(bool isDarkModeEnabled, double deviceWidth,
double deviceHeight, Color primaryColor, Color secondaryColor) {
if (_isAlreadyCreated == false) {
_isAlreadyCreated = true;
_instance = GeneralAppAndDeviceDetails._(isDarkModeEnabled, deviceWidth,
deviceHeight, primaryColor, secondaryColor);
}
return _instance;
}
const GeneralAppAndDeviceDetails._(this.isDarkModeEnabled, this.deviceWidth,
this.deviceHeight, this.primaryColor, this.secondaryColor);
}
I use a flag to check if an instance was created before or not in here and with this code, a similar instance will be returned every time but is it the best way to achieve this?
This is your singleton class
class Test{
final String str;
static Test _singleton;
Test._internal({this.str});
factory Test(String str) {
return _singleton ??= Test._internal(
str: str
);
}
}
example code for you to try and test
void main() {
Test obj = Test('ABC');
print(obj.str);
Test obj1 = Test('XYZ');
print(obj1.str);
}
class Test{
final String str;
static Test _singleton;
Test._internal({this.str});
factory Test(String str) {
return _singleton ??= Test._internal(
str: str
);
}
}
try running this in dartpad for better understanding
you can make the class as singleton and then make these fields as private, accessible only through getters and setters, inside the setter you can check and discard the new value if there is already some value assigned to the field.