4

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;

}
Greg
  • 34,042
  • 79
  • 253
  • 454

0 Answers0