Not able to change the postion of items in hive database - flutter

Not able to swap the position of the record in hive database
getting error like Unhandled Exception: HiveError: The same instance of an HiveObject cannot be stored with two different keys ("5" and "10").
code:
Box<CounterDetails> box = await Hive.openBox<CounterDetails>(kHiveBoxName);
if (oldIndex < newIndex) {
newIndex -= 1;
}
// this is required, before you modified your box;
final oldItem = box.getAt(oldIndex);
final newItem = box.getAt(newIndex);
// here you just swap this box item, oldIndex <> newIndex
box.putAt(oldIndex, newItem!);
box.putAt(newIndex, oldItem!);

As for the error, you can create copyWith method on CounterDetails that will return new instance. Or create new instance duplicating fields. Then put this new object.
The copyWith method will be like
class CounterDetails {
final String a;
CounterDetails({
required this.a,
});
CounterDetails copyWith({
String? a,
}) {
return CounterDetails(
a: a ?? this.a,
);
}
}
And putting those will be like
box.putAt(oldIndex, newItem!.copyWith()); //better do a null check 1st , same for next one

Related

Existing state editing in Riverpod / Flutter

How I can manipulate the existing state in Riverpod. 'm a beginner about Flutter and Riverpod. When I try add one to Order error pops up and says:
Error: A value of type 'int' can't be assigned to a variable of type
'List'.
final OrderPaperProvider = StateNotifierProvider<OrderPaper, List<Order>>((ref) {
return OrderPaper();
});
#immutable
class Order {
final String product_id;
final String product_name;
final int product_price;
final int product_count;
Order({required this.product_id, required this.product_name, required this.product_price, required this.product_count});
Order copyWith({product_id, product_name, product_price, product_count}) {
return Order(
product_id: product_id,
product_name: product_name,
product_price: product_price,
product_count: product_count,
);
}
}
class OrderPaper extends StateNotifier<List<Order>> {
OrderPaper() : super([]);
void addOrder(Order order) {
for (var x = 0; x < state.length; x++) {
if (state[x].product_id == order.product_id) {
addOneToExistingOrder(x, order);
return;
}
}
state = [...state, order];
}
void removeOrder(String product_id) {
state = [
for (final order in state)
if (order.product_id != order) order,
];
}
void addOneToExistingOrder(index, Order order) {
state = state[index].product_count + 1; // <--------- Error in this line
}
void clearOrderPaper() {
state = [];
}
}
What is happening in the code you posted is basically this:
You are telling it to update the state which is a List<Order> with an integer.
This is because state[index].product_count + 1 actually equals a number, for example 1+2 = 3.
So you are telling it, state = 3, and this causes your error.
What you need to do, is create a new state with the list of items and the edited item, like this (you don't need to pass the index, you can get it in the function):
void addOrder(Order order) {
//find if the product is in the list. The index will not be -1.
final index = state.indexWhere((entry) => entry.product_count == order. product_count);
if (index != -1) {
//if the item is in the list, we just edit the count
state = [
...state.sublist(0, index),
order.copyWith(product_count: state[index].product_count + 1),
...state.sublist(index + 1),
];
}else {
//otherwise, just add it as a new entry.
state = [...state, order],
}
}

Is this flutter (riverpod with sqfite) code optimal/correct for manual reordering a list? (code attached)

Is this flutter (riverpod/StateNotifier) code optimal/correct for the use case of manually re-ordering a list, and for which order is persisted in SQLite (using sqfite)? Being new to riverpod aspects I'm seeking clarity on:
manually calculating database updates for "orderNum" field needed for the rows impacted
separately/manually then calculated updates to Riverpod state (?)
need to iterate through Riverpod state and create new records (using copyWith) when required as you can't "update" Riverpod state objects? (I guess this is the concept of riverpod)
overall am I on track for simplest way to reorder manually a list in flutter when using Riverpod/StateNotifer for internal state, and SQLite for persisting? (my code just seems complex)
ReOrder Function in StateNotifier
void reorderLists({required int oldIndex, required int newIndex}) async {
// Step 1 - Validation & prep (check state isn't loading or error)
if ((state is! AsyncData)) {
developer.log("WARRNING: Current state: ${state.toString()}");
return;
}
List<Todolist> _currTodolists = state.asData!.value.data;
// Index Ajustment for Flutter Reorderable List
bool moveDown = newIndex > oldIndex;
int startIndex = oldIndex;
int destIndex = moveDown ? (newIndex - 1) : newIndex;
// List to store database updates required
List<TodolistEntity> databaseUpdates = [];
// Determine Order Updates - First Item
databaseUpdates.add({
'id': _currTodolists[startIndex].id,
'orderNum': _currTodolists[destIndex].orderNum,
});
// Determine Order Updates - Remaining Items
int loopStart = moveDown ? startIndex + 1 : destIndex;
int loopEnd = moveDown ? destIndex : startIndex - 1;
int loopInc = moveDown ? -1 : 1;
int loopCurr = loopStart;
while (loopCurr <= loopEnd) {
databaseUpdates.add({
'id': _currTodolists[loopCurr].id,
'orderNum': _currTodolists[loopCurr].orderNum + loopInc,
});
loopCurr += 1;
}
// Update database
await _todoRepos.updateOrder(databaseUpdates);
// Update State (Riverpod) via changes to existing state Objects
_logStateData();
for (var dbUpdate in databaseUpdates) {
_currTodolists.asMap().forEach((index, value) {
if (value.id == dbUpdate['id']) {
_currTodolists[index] = _currTodolists[index].copyWith(orderNum: dbUpdate['orderNum']);
}
});
}
_currTodolists.sort( (a,b) => a.orderNum.compareTo(b.orderNum));
state = AsyncData(state.asData!.value.copyWith()); // TODO: This was required to trigger UI refresh - why?
}
UpdateOrder function for DB update (uses sqfite)
#override
Future<void> updateOrder(List<TodolistEntity> entityList) async {
Database _db = await DatabaseHelper.instance.database;
await _db.transaction((txn) async {
var batch = txn.batch();
for (final entity in entityList) {
int tableId = entity['id'];
batch.update(
"todo_list",
entity,
where: 'id = ?',
whereArgs: [tableId],
);
}
await batch.commit();
});
}
State riverpod / freezed classes
#freezed
abstract class Tododataset with _$Tododataset{
const factory Tododataset({
#Default([]) List<Todolist> data,
}) = _Tododataset;
}
#freezed
abstract class Todolist with _$Todolist{
const factory Todolist({
required int id,
required int orderNum,
required String listTitle,
#Default([]) List<Todo> items,
}) = _Todolist;
}

How to store a List in Hive (Flutter)?

I am trying to store and fetch a list using Hive in Flutter, however I get a range error.
int steps = 0;
List<int> stepCountList = List(7);
var time = DateTime.now();
// This is my method for a listener that updates when it detects a change,
void _onData(int newValue) async {
fetchSteps();
steps = stepCountList[time.weekday] ?? 0;
stepDivider += newValue;
stepCountList.insert(time.weekday - 1, steps);
moneyLottoBox.put('stepCountList', stepCountList);
}
void fetchSteps() {
stepCountList = moneyLottoBox.get('stepCountList');
if (stepCountList == null) {
moneyLottoBox.put('stepCountList', <int>[7]);
stepCountList = moneyLottoBox.get('stepCountList');
}
}
// I create my MoneyLotto box here,
var moneyLottoBox = Hive.box('moneyLottoBox');
Future<void> main async {
moneyLottoBox = await Hive.openBox('box');
}
Today being Saturday for me, the value of time.weekday for me is 6, however it shows me the error when I try to print(stepCountList[6])
RangeError (index): Invalid value: Only valid value is 0: 6
You can't use insert() method on fixed length List, by fixed length I mean when you declare it this way List<int> stepCountList = List(7);
Edited the code, now it should work
void _onData(int newValue) async {
fetchSteps();
steps = stepCountList[time.weekday] ?? 0;
stepDivider += newValue;
//stepCountList.insert(time.weekday - 1, steps);this is not correct
stepCountList[time.weekday -1] = steps; // this should be the approach
moneyLottoBox.put('stepCountList', stepCountList);
}
void fetchSteps() {
stepCountList = moneyLottoBox.get('stepCountList');
if (stepCountList == null) {
/* moneyLottoBox.put('stepCountList', <int>[7]); this statement is the
reason for your error because your are storing a growable list instead of fixed list in the hive */
moneyLottoBox.put('stepCountList', List<int>(7));// try this instead
// or u can use this moneyLottoBox.put('stepCountList', stepCountList);
stepCountList = moneyLottoBox.get('stepCountList');
}
}
Hive box has methods like: put(dynamic key, E value), putAll(Map<dynamic, E> entries) and putAt(int index, E value). For example, I used putAt() to update entry at the specific key.
For adding with auto-increment key, you want to use add(E value) or to add a list - addAll(Iterable<E> values).
And when I wanted to delete all data in the box, I used clear(), like so:
await _booksBox.clear();
And this method is for getting all the entries from the database:
List<Book> getAllBooks() {
return _booksBox.values.map(_bookFromDb).toList();
}
A complete Hive db class is from the tutorial.

Flutter Map to a list of custom objects

I am getting back information from a websocket that might contain one or more items on each response.
I want to convert these responses to a list of custom objects.
The issue I have is where I want to use the first index (key) and second index (value) of the returned data and use them to pass into the custom object initialization to instantiate them and add them to the list.
I also want to add to the list, but if the 'name' field in an object already exists then just update that objects amount value instead of adding another copy. I will just do this with a loop, unless there is a shorter way.
Below you can see some of the attempts I have tried.
EDIT:
var listAmounts = valueMap.values.toList();
var listNames = valueMap.keys.toList();
print(listAmounts[0]);
print(listNames[0]);
for (int i = 0; i < listAmounts.length; i++) {
Person newPerson =
new Person(name: listNames[i], amount: double.parse(listAmounts[i]));
coins.add(newPerson);
print('=========');
print(newPerson.name);
print(newPerson.amount.toString());
print('=========');
}
I am not sure this is a good way as would there be a possibility that the keys/values will not be in order? From testing they are but I am not 100% sure?
Here are two examples of the message received from the websocket:
example 1
{jacob: 123.456789, steven: 47.3476543}
example 2
{henry: 54.3768574}
I have a custom class:
class Person {
double amount;
String name;
Person({
this.name = '',
this.amount = 0.0,
});
}
Here is the main code:
IOWebSocketChannel channel;
List<Person> people = [];
void openChannel() async {
channel = IOWebSocketChannel.connect(
"wss://ws.example.com/example?example=exp1,exp2,exp3");
channel.stream.listen((message) {
//print(message);
Map valueMap = json.decode(message);
print(valueMap);
//people = List<Person>.from(valueMap.map((i) => Person.fromJson(i)));
//message.forEach((name, amount) => people.add(Person(name: name, amount: amount)));
});
}
var listAmounts = valueMap.values.toList();
var listNames = valueMap.keys.toList();
print(listAmounts[0]);
print(listNames[0]);
for (int i = 0; i < listAmounts.length; i++) {
Person newPerson =
new Person(name: listNames[i], amount: double.parse(listAmounts[i]));
coins.add(newPerson);
print('=========');
print(newPerson.name);
print(newPerson.amount.toString());
print('=========');
}

How to convert List<Object> flutter

i'm new in flutter and need to help:
I have already got
final List<Genres> genres = [{1,"comedy"}, {2,"drama"},{3,"horror"}]
from api.
class Genres {
final int id;
final String value;
Genres({this.id,this.value});
}
In another method I get genres.id.(2) How can I convert it to genres.value ("drama")?
Getting a Genre from an id is inconvenient when your data structure is a List. You have no choice but to iterate over the list and compare the id value to the id of each element in the list:
final id = 2;
final genre = genres.firstWhere((g) => g.id == id, orElse: () => null);
The problem with this code is that it's slow and there could be multiple matches (where the duplicates after the first found would be ignored).
A better approach would be to convert your list to a Map when you first create it. Afterwards, you can simply use an indexer to get a Genre for an ID quickly and safely.
final genresMap = Map.fromIterable(genres, (item) => item.id, (item) => item);
// later...
final id = 2;
final genre = genresMap[id];
This way, there is guaranteed to not be any duplicates, and if an ID doesn't exist then the indexer will simply return null.
you could iterate over the json result of the api and map them to the Gener class like so,
void fn(id) {
final gener = geners.firstWhere((gener) => gener['id'] == id);
// now you have access to your gener
}
You can find the item inside the List<Genres> like this
Genres element = list.firstWhere((element) => element.id == 2); // 2 being the id you give in the question as an exaple. You should make it dynamic
print(element.value);