flutter Getx and SfCalendar combination throw improper use of a GetX error

389 Views Asked by At

I am developing a flutter project using getx library.

I used binding to link getXView and controller, and I want to link it with SfCalendar's dataSource.

The code is as follows:

// CalendarView
class ICalendarView extends BaseView<ICalendarController> {
  @override
  PreferredSizeWidget? appBar(BuildContext context) {
    return null;
  }

  @override
  Widget body(BuildContext context) {
    return Obx(
        () => SfCalendar(
          view: CalendarView.month,
          dataSource: MeetingDataSource(controller.rxMeetingList),
          monthViewSettings: const MonthViewSettings(
            appointmentDisplayMode: MonthAppointmentDisplayMode.appointment,
            showAgenda: true,
          ),
        ),
    );
  }
}

// calendar_controller
class ICalendarController extends BaseController {
  final RxList<Meeting> rxMeetingList = RxList.empty();

  @override
  void onInit() {
    rxMeetingList.addAll(_getDataSource());
  }

  List<Meeting> _getDataSource() {
    final List<Meeting> meetings = <Meeting>[];
    final DateTime today = DateTime.now();
    final DateTime startTime = DateTime(today.year, today.month, today.day, 9);
    final DateTime endTime = startTime.add(const Duration(hours: 2));
    meetings.add(
        Meeting('예시1', startTime, endTime, const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시2', startTime,
        endTime.add(const Duration(hours: 2)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    return meetings;
  }
}
// MeetingDataSource
class MeetingDataSource extends CalendarDataSource {
  /// Creates a meeting data source, which used to set the appointment
  /// collection to the calendar
  MeetingDataSource(List<Meeting> source) {
    appointments = source;
  }

  @override
  DateTime getStartTime(int index) {
    return _getMeetingData(index).from;
  }

  @override
  DateTime getEndTime(int index) {
    return _getMeetingData(index).to;
  }

  @override
  String getSubject(int index) {
    return _getMeetingData(index).eventName;
  }

  @override
  Color getColor(int index) {
    return _getMeetingData(index).background;
  }

  @override
  bool isAllDay(int index) {
    return _getMeetingData(index).isAllDay;
  }

  Meeting _getMeetingData(int index) {
    final dynamic meeting = appointments![index];
    late final Meeting meetingData;
    if (meeting is Meeting) {
      meetingData = meeting;
    }

    return meetingData;
  }
}

It works fine when I use the Navigation tab to display the Calendar view after the initial build, but when I go to the Home view and return to the Calendar view, the following error occurs:

======== Exception caught by widgets library =======================================================
The following message was thrown building Obx(has builder, dirty, state: _ObxState#ed38f):
      [Get] the improper use of a GetX has been detected. 
      You should only use GetX or Obx for the specific widget that will be updated.
      If you are seeing this error, you probably did not insert any observable variables into GetX/Obx 
      or insert them outside the scope that GetX considers suitable for an update 
      (example: GetX => HeavyWidget => variableObservable).
      If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.

Can you tell me what's wrong?

2

There are 2 best solutions below

0
On

I found the answer!

In previous code, I observed the calendar_controller's field "rxMeetingList" directly.

My solution was accessing calendar_controller's field "rxMeetingList" by getter/setter.

And, I changed super.onInit() code behind the initialization code.

The code is like below one.

class ICalendarController extends BaseController {
  final RxList<Meeting> _rxMeetingList = RxList.empty();

  @override
  void onInit() {
    _rxMeetingList.addAll(_getDataSource());
    super.onInit();
  }

  List<Meeting> get meetingList => _rxMeetingList.toList();

  List<Meeting> _getDataSource() {
    final List<Meeting> meetings = <Meeting>[];
    final DateTime today = DateTime.now();
    final DateTime startTime = DateTime(today.year, today.month, today.day, 9);
    final DateTime endTime = startTime.add(const Duration(hours: 2));
    meetings.add(
        Meeting('예시1', startTime, endTime, const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시2', startTime,
        endTime.add(const Duration(hours: 2)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    meetings.add(Meeting('예시3', startTime,
        endTime.add(const Duration(hours: 5)), const Color(0xFF0F8644), false));
    return meetings;
  }
}
class ICalendarView extends BaseView<ICalendarController> {
  @override
  PreferredSizeWidget? appBar(BuildContext context) {
    return null;
  }

  @override
  Widget body(BuildContext context) {
    return Obx(
      () => SfCalendar(
        view: CalendarView.month,
        dataSource: MeetingDataSource(controller.meetingList),
        monthViewSettings: const MonthViewSettings(
          appointmentDisplayMode: MonthAppointmentDisplayMode.appointment,
          showAgenda: true,
        ),
      ),
    );
  }
}

2
On

You should create your list like:

final RxList<Meeting> rxMeetingList = [].obs;

Adding the .obs makes your list observable for the widget: If you are seeing this error, you probably did not insert any observable variables into GetX/Obx or insert them outside the scope that GetX considers suitable for an update