Flutter bloc: yielding state doesn't triggers widget rebuild

Issue

In my reminder app, when I click the add reminder button, it actually adds the reminder to the database and then updates my reminders, but the list of reminders is not rebuild. For some reason, a similar scenario works great when I delete my reminders.

part of my bloc:


  void _loadReminders() async {
    final reminders = await _repository.getAllReminders();
    reminders.sort((a, b) => a.dateTime.isAfter(b.dateTime) ? 1 : -1);
    if (reminders.isNotEmpty) {
      add(RemindersLoaded(reminders: reminders));
    }
  }

  @override
  Stream<MainState> mapEventToState(MainEvent event) async* {
    if (event is ItemSelected) {
      yield state.copyWith(isSelectedModeActive: true);
    } else if (event is SelectModeDisabled) {
      yield state.copyWith(isSelectedModeActive: false);
    } else if (event is RemindersLoaded) {
      yield state.copyWith(reminders: event.reminders);
    } else if (event is SaveReminderPressed) {
      await _saveReminder(event.reminder);
      _loadReminders();
    } else if (event is DeletePressed) {
      await deleteReminders();
      _loadReminders();
      add(SelectModeDisabled());
    }
  }

widget:


  Widget _remindersList(BuildContext context) =>
      BlocBuilder<MainBloc, MainState>(
        builder: (context, state) {
          return ListView.builder(
            itemCount: state.reminders.length,
            itemBuilder: (context, index) {
              return ReminderItem(reminders: state.reminders, index: index);
            },
          );
        },
      );

state:


class MainState extends Equatable {
  final bool isSelectedModeActive;
  final List<Reminder> reminders;
  final int quantityOfSelectedItems;

  MainState({
    this.isSelectedModeActive = false,
    this.reminders,
  }) : quantityOfSelectedItems =
            reminders.where((reminder) => reminder.isSelected).length;

  MainState copyWith({
    bool isSelectedModeActive,
    List<Reminder> reminders,
  }) =>
      MainState(
        isSelectedModeActive: isSelectedModeActive ?? this.isSelectedModeActive,
        reminders: List<Reminder>()..addAll(reminders ?? this.reminders),
      );

  @override
  List<Object> get props => [
        isSelectedModeActive,
        reminders,
        quantityOfSelectedItems,
      ];
}

wasted several days, trying to solve this. For more detail you can check my project on github: https://github.com/rockstar4095/reminder_flutter_app/tree/main_screen

Solution

The problem is here in reminder_item.dart file, in function _reminderDescription your are using Flexible with non Flex parent. Just remove the Flexible Widget or make it direct child of Row or Column Widget and everything will work fine.

  Widget _reminderDescription(BuildContext context) => Align(
    alignment: Alignment.centerLeft,
    child: RichText(
      overflow: TextOverflow.ellipsis,
      text: TextSpan(
        text: _current.description,
        style: Theme.of(context).textTheme.caption,
      ),
    ),
  );

Also update MainScreen Widget as well

class MainScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final content = Scaffold(
      appBar: AppBar(
        title: Text('Напоминания'),
        actions: [
          _deleteIcon(context),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          EditReminderDialog.open(context);
        },
        child: Icon(Icons.add),
      ),
      body: _remindersList(context),
    );
    return BlocProvider(
      create: (context) {
        return context.read<MainBloc>();
      },
      child: Builder(builder: (context) {
        return content;
      }),
    );
  }

EDIT

You are missing Navigator.of(context).pop()as well in your popup dialog save function.

Widget _save(BuildContext context) => RaisedButton(
        child: Text('Сохранить'),
        onPressed: () {
          final reminder = Reminder(
            id: null,
            title: 'test reminder title',
            description:
                'test reminder description ${DateTime.now().toLocal()}',
            dateTime: DateTime.now(),
          );
          context.read<MainBloc>().add(SaveReminderPressed(reminder: reminder));
          Navigator.of(context).pop();
        },
      );

Answered By – Taha Malik

Answer Checked By – Pedro (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *