Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use ReorderableListView with flutter_bloc?

I'm new to flutter_bloc and trying to implement a ReorderableListView, to allow the user to rerange the order of textfields in a listview.

Unfortunately it isn't working. After dropping the listtile to the new position it changes back to the old one:

Demo

This is the output in the screen:

class _Levels extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<DifficultyLevelsCubit, DifficultyLevelsState>(
      builder: (context, state) {
        print('rebuild');
        return Flexible(
          child: ReorderableListView(
            shrinkWrap: true,
            children: state.levels
                .asMap()
                .map((i, lvl) => MapEntry(
                      i,
                      ListTile(
                        key: ValueKey(i),
                        title: TextField(
                          onChanged: (lvl) => context
                              .read<DifficultyLevelsCubit>()
                              .levelChanged(lvl, i),
                          decoration: InputDecoration(
                            labelText: 'Level',
                            helperText: '',
                            errorText: state.levels[i].invalid
                                ? 'invalid level'
                                : null,
                          ),
                        ),
                        trailing: Icon(Icons.menu),
                      ),
                    ))
                .values
                .toList(),
            onReorder: (int oldIndex, int newIndex) => context
                .read<DifficultyLevelsCubit>()
                .orderChanged(oldIndex, newIndex),
          ),
        );
      },
    );
  }
}

And this my implementation of the reorder function in my cubit class:

void orderChanged(int oldIndex, int newIndex) {
    List<DiffLevel> _list = state.levels;

    if (newIndex > oldIndex) {
      newIndex -= 1;
    }
    final items = _list.removeAt(oldIndex);
    _list.insert(newIndex, items);

    emit(state.copyWith(levels: _list));
}

Does someone know why this behaviour appears and how to solve this? Thanks in advance!

Note: print('rebuild)` is not being triggerd on reorder. I don't really understand why, cause I emit the state in the reorder function and expect the blocbuilder to react on that change.

like image 262
Matthias Avatar asked Jan 22 '26 17:01

Matthias


1 Answers

What's happening here is you are mutating the same list and not overwriting it - bloc expects and entirely new list in order to rebuild.

Also, I'm not entirely sure you can emit state directly like that. I generally follow the pattern suggested here where the emitted state carries the updated list

 const ListState.success(List<Item> items)
      : this._(status: ListStatus.success, items: items);

With that said, here 👇🏼 is the approach I would suggest !

void orderChanged(int oldIndex, int newIndex) {

    if (newIndex > oldIndex) {
      newIndex -= 1;
    }

// First get the items at the old index
    final items = state.levels[oldIndex];

//Then remove it from the old position and insert it at the new one
    final reorderedList = List.of(state.levels)
    ..removeWhere((element) => element.id == items.id)
    ..insert(newIndex, items);

    emit((ListState.success(reorderedList);
}
like image 166
Gene Avatar answered Jan 25 '26 08:01

Gene