Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Problem #530

Closed
muhammednazil opened this issue Dec 31, 2020 · 50 comments
Closed

Update Problem #530

muhammednazil opened this issue Dec 31, 2020 · 50 comments
Labels
question Further information is requested

Comments

@muhammednazil
Copy link

Question
I am using the hive database for making my items as favorite and show them in a list.

While clicking a button " favoriteBox.add(Favorite(id, name,place,featuredImage,rating,));"

if I click that button again then the same data is coming into the list again.

is there any way to overcome this problem?

I need to know how can I update a column in the hive.

I am just showing the item in a list. I just want to update it from my screen.

@muhammednazil muhammednazil added the question Further information is requested label Dec 31, 2020
@muhammednazil
Copy link
Author

            if(!favoriteBox.isEmpty){

                 favoriteBox.put(id, Favorite(id, name,place,featuredImage,rating,));


            }else{
              favoriteBox.add(Favorite(id, name,place,featuredImage,rating,));
            }

@baumths
Copy link

baumths commented Dec 31, 2020

Just make sure that the id property from your model is the same as the key returned by box.add and you should be good to go.

When you call box.put it actually takes in the key of your data.

If you're using a model that extends HiveObject, you can use the key getter instead of id in box.put:

final favorite = Favorite(/* [ ... ] */);
await favoriteBox.put(favorite.key, favorite);

Otherwise, if you're using a list, you can get the key for your data through box.keyAt(index).

@muhammednazil
Copy link
Author

muhammednazil commented Dec 31, 2020 via email

@baumths
Copy link

baumths commented Dec 31, 2020

Could you please post the code for your Favorite model? And if possible, the code where you add favorites to the box.

@muhammednazil
Copy link
Author

muhammednazil commented Jan 1, 2021 via email

@baumths
Copy link

baumths commented Jan 1, 2021

Sure! So.

I would suggest you to read through the documentation on hive, it will help a lot.

As your model doesn't extend HiveObject, you will have to find the key of your Favorite class iterating through box.values:

/// Make sure you test if the favorite exists before running this function
Future<void> updateItemById(Box<Favorite> box, int id) async {
  final boxEntries = box.values.toList();

  // Find your favorite by its id
  final favorites = boxEntries.where((fav) => fav.id == id).toList();

  if (favorites.isEmpty) return; // favorite not in the box

  // Get the index of your favorite in the box
  final favIndex = boxEntries.indexOf(favorites.first);

  // Update the instance
  await box.putAt(favIndex, favorite);
}

An easier way of doing the same thing is using the HiveObject mixin in your model, just add with HiveObject to the definition of Favorite:

// Make sure you run build_runner if you make this change
class Favorite with HiveObject {
  // your code
}

Then you will be able to use the methods delete, save and the key getter:

final favorite = Favorite(id: 0);

favoriteBox.add(favorite);

// Update any field thats not final
favorite.id = 1;

await favorite.save(); // updates the favorite without adding a new entry to the box

// If you dont want to mutate the object, you can use `favorite.key`
final oldFavorite = Favorite(id: 0);
final newFavorite = Favorite(id: 1);

await box.put(oldFavorite.key, newFavorite); // use the old key to override the entry with a new Favorite

Then, before adding a new favorite you can check if it exists, and update it accordingly.

@muhammednazil
Copy link
Author

muhammednazil commented Jan 2, 2021 via email

@baumths
Copy link

baumths commented Jan 2, 2021

Oh!

Make sure your model extends HiveObject. Then use the isInBox getter:

Take a look at HiveObject. It helps a lot.

Another way:

final boxList = box.values.toList();
final isInBox = boxList.contains(favorite);

// OR

final favorite = Favorite(id: 0);
final isInBox = box.values.where((fav) => fav.id == favorite.id);
// You can also use .firstWhere()

Hope this time I understood you. Let me know if it worked 🙂

@muhammednazil
Copy link
Author

muhammednazil commented Jan 2, 2021 via email

@baumths
Copy link

baumths commented Jan 2, 2021

@themisir we can close this one 🙂

@muhammednazil
Copy link
Author

muhammednazil commented Jan 2, 2021 via email

@themisir themisir closed this as completed Jan 2, 2021
@muhammednazil
Copy link
Author

muhammednazil commented Jan 3, 2021 via email

@themisir
Copy link
Contributor

themisir commented Jan 3, 2021

Hey @muhammednazil , You have to generate and register adapter for CompletedHistoryRecord class.

@muhammednazil
Copy link
Author

muhammednazil commented Jan 3, 2021 via email

@baumths
Copy link

baumths commented Jan 3, 2021

Could you please share the code for UpComingHistoryRecord and CompletedHistoryRecord?

@muhammednazil
Copy link
Author

muhammednazil commented Jan 3, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 3, 2021 via email

@baumths
Copy link

baumths commented Jan 3, 2021

You cannot have a class inside a @HiveType that doesn't have an adabter. (Hive only supports primitive types, like String, int, Map, List, ..., all other types must be registered)

For your approach to work you would either have to make CompletedHistoryRecord into a @HiveType and use a Hive List to save it inside History or use methods like toMap() and fromMap() to save a Dart Map of your data models inside History.

@themisir
Copy link
Contributor

themisir commented Jan 3, 2021

For simplicity you can add @HiveType annotation to your model to fix the issue.

@HiveType(typeId: 3)
class UpComingHistoryRecord {
...

@muhammednazil
Copy link
Author

muhammednazil commented Jan 3, 2021 via email

@baumths
Copy link

baumths commented Jan 3, 2021

Do the same things you did to Favorite and History with your other models, like @themisir showed above.

@muhammednazil
Copy link
Author

muhammednazil commented Jan 3, 2021 via email

@baumths
Copy link

baumths commented Jan 3, 2021

Yes you can! But you have to register an adapter for it:

@HiveType(typeId: 5 /*any int < 255 here. Look out for duplicates*/);
class CompletedHistoryRecord {
  CompletedHistoryRecord({this.id, this.name});

  // Mark the properties you want to store with @HiveField(). For example:
  @HiveField(0)
  final int id;

  @HiveField(1)
  final String name;

  // [ ... ] Other properties and methods
}

And do the same for the UpComing model.
Then run build_runner again and register the adapters.

@baumths
Copy link

baumths commented Jan 3, 2021

Another way is changing the History model to store maps instead of your models:

@HiveField(0)
final List<Map<String, dynamic>> _upComingHistoryRecord;

// And

@HiveField(1)
final List<Map<String, dynamic>> _completedHistoryRecord;

Then when you instantiate History do it like so:

final history = History(
  [completedHistoryRecordInstance.toJson()],
  [upComingHistoryRecordInstance.toJson()],
);
// Use the instances, or a list with the instances:
final history = History(
  listOfCompletedHistoryRecord.map((value) => value.toJson()).toList(),
  listOfUpComingHistoryRecord.map((value) => value.toJson()).toList(),
);

Then in History, add getters for converting back:

List<CompletedHistoryRecord> get completedHistoryRecord {
  return _completedHistoryRecord.map((value) => CompletedHistoryRecord.fromJson(value)).toList();
}

// Do the same for UpComing

@muhammednazil
Copy link
Author

muhammednazil commented Jan 4, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 4, 2021 via email

@baumths
Copy link

baumths commented Jan 4, 2021

Seems like an error with the package path_provider. I would recommend that you use Hive.initFlutter() from the hive_flutter package as it resolves the paths for you 🙂

import 'package:hive_flutter/hive_flutter.dart';

void main() async {
  await Hive.initFlutter();
  // [ ... ]
}

@muhammednazil
Copy link
Author

muhammednazil commented Jan 4, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 4, 2021 via email

@baumths
Copy link

baumths commented Jan 4, 2021

Hive.initFlutter already calls WidgetsFlutterBinding.ensureInitialized(), so you don't have to.
Oh, and make sure you add hive_flutter: ^0.3.1 as a dependency.

Try the following:

  1. Stop your app
  2. Run flutter clean
  3. Run flutter pub cache repair
  4. Run flutter pub get
  5. Start your app again

Let me know if this works!

@muhammednazil
Copy link
Author

muhammednazil commented Jan 4, 2021 via email

@baumths
Copy link

baumths commented Jan 4, 2021

Did you await the initFlutter?

Can I see the contents of your pubspec.yaml?

I ran out of ideas, perhaps @themisir knows whats going on.

@muhammednazil
Copy link
Author

muhammednazil commented Jan 4, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 4, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 4, 2021 via email

@themisir
Copy link
Contributor

themisir commented Jan 4, 2021

Actually the error tells "NoSuchMethodError: The getter 'upComingHistoryRecord' was called on null." which means History object that you're trying to retrieve upComingHistoryRecord property from is null. I'm not sure but it might worth checking if history object is null or not before getting properties.

@muhammednazil
Copy link
Author

muhammednazil commented Jan 5, 2021 via email

@baumths
Copy link

baumths commented Jan 5, 2021

History history; does not instantiate nor have any reference to an object, you just created a null variable, try like so:

final historyList = historyBox.values.toList();
final history = historyList.first; // same as calling list[0]
final a = history.completedHistoryRecord;

@muhammednazil
Copy link
Author

muhammednazil commented Jan 6, 2021 via email

@themisir
Copy link
Contributor

themisir commented Jan 6, 2021

Unfortunately dart is not good at handling generic types like List<Map<String, dynamic>>. It's not related to hive itself but dart.

You can store List<dynamic> then cast it to Map<dynamic, dynamic> then cast items into <String, dynamic>.

@muhammednazil
Copy link
Author

muhammednazil commented Jan 6, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 6, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 6, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 6, 2021 via email

@themisir
Copy link
Contributor

themisir commented Jan 6, 2021

Could you try to modify your class like that:

@HiveType(typeId: 2)
class History extends HiveObject{
  @HiveField(0)
  final List<dynamic> _upComingHistoryRecord;
  @HiveField(1)
  final List<dynamic> _completedHistoryRecord;

  History(
      this._upComingHistoryRecord,
      this._completedHistoryRecord,
      ) : assert(_upComingHistoryRecord != null,
'_upComingHistoryRecord cannot be null.'),
        assert(_completedHistoryRecord != null,
'_completedHistoryRecord cannot be null.');


  List<UpComingHistoryRecord> get upComingHistoryRecord {
    return _upComingHistoryRecord.map((value) =>
UpComingHistoryRecord.fromJson(value as Map<String, dynamic>)).toList();
  }

  List<CompletedHistoryRecord> get completedHistoryRecord {
    return _completedHistoryRecord.map((value) =>
CompletedHistoryRecord.fromJson(value as Map<String, dynamic>)).toList();
  }
}

@muhammednazil
Copy link
Author

muhammednazil commented Jan 6, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 6, 2021 via email

@muhammednazil
Copy link
Author

muhammednazil commented Jan 6, 2021 via email

@baumths
Copy link

baumths commented Jan 6, 2021

Try this for the getters:

List<CompletedHistoryRecord> get completedHistoryRecord {
  final output = Map<String, dynamic>[];
  _completedHistoryRecord.forEach((value) {
    final historyRecord = value as Map<dynamic, dynamic>;
    output.add(historyRecord as Map<String, dynamic>);
  });
  return output;
}

Do the same for UpComing.

Btw, your problem is not with Hive anymore, the errors you're getting are from dart...

@muhammednazil
Copy link
Author

muhammednazil commented Jan 6, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants