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;
}