Flutter/Dart - Cannot modify unmodifiable map error
Asked Answered
D

1

8

Ive created an app with Flutter bloc package.Ive created a screen named learning_dashboard_screen which contains a list fetched from a server. inside that same screen I have a filter button which on press will show a filter screen using the bottom sheet widget.

enter image description here

it has got different categories for the list to be filtered. In the learning_dashboard_screen_state screen Ive got a Map named filteroptions which is of type Map<String, Map<String, dynamic>>.This map is used to populate the checkboxes by mapping the map with the checkbox widget.

part of 'learning_dashboard_screen_bloc.dart';

class LearningDashboardScreenState extends Equatable {
  static ScrollController controller = ScrollController();
  final int courseLimit = 3;
  final List<GPaginatedCoursesData_paginatedCourses_data> courses;
  final DataRetrievalStatus initialCoursesStatus;
  final Map<String, Map<String, dynamic>> filterOption;
 Map<String, Map<String, dynamic>> getFilterOption() {
    return filterOption;
 }

  final int currPage;
  final bool hasNextPage;
  final DataRetrievalStatus moreCoursesStatus;

  LearningDashboardScreenState(
     {this.courses = const [],
     this.initialCoursesStatus = DataRetrievalStatus.NOT_INITIALIZED,
     this.filterOption = const {
      "Certificate Provider": {"NVQ": false, "City and Guilds": false},
      "Course Language": {"Sinhala": false, "English": false, "Tamil": false},
      "Duration": {"6 Months": false, "8 Months": false, "1Year": false},
      "Category": {
        "Baby Care": false,
         "First Aid": false,
          "Adult Care": false,
          "Mental Care": false,
          "Physiotherapy": false,
          "Baby First Aid": false,
          "Light Housekeeping": false,
           "Assist Methods": false
         }
       },
       this.currPage = 0,
       this.hasNextPage = false,
       this.moreCoursesStatus = DataRetrievalStatus.NOT_INITIALIZED});

      @override
      List<Object?> get props => [
       courses,
       filterOption,
      initialCoursesStatus,
      courseLimit,
      currPage,
      hasNextPage,
      moreCoursesStatus
     ];

   LearningDashboardScreenState copyWith(
        {List<GPaginatedCoursesData_paginatedCourses_data>? course,
         DataRetrievalStatus? courseStatus,
         int? currPage,
         bool? hasNextPage,
         Map<String, Map<String, dynamic>>? filterOption,
         DataRetrievalStatus? moreCourseStatus}) {
        return LearningDashboardScreenState(
          courses: course ?? this.courses,
          filterOption: filterOption ?? this.filterOption,
          initialCoursesStatus: courseStatus ?? this.initialCoursesStatus,
          currPage: currPage ?? this.currPage,
          hasNextPage: hasNextPage ?? this.hasNextPage,
           moreCoursesStatus: moreCourseStatus ?? this.moreCoursesStatus);
         }

         ScrollController getScrollController() {
           return controller;
         }
       }

what I want is to change the boolean value of the filteroption map in the state according to the chackbox value. For that what Ive done is ,

1.I created an event in the event class ,

  class CheckBoxValueChangedEvent extends LearningDashboardScreenEvent {
   String filterOption;
   String filterValue;
   bool isChecked;


  CheckBoxValueChangedEvent({required this.filterOption,required this.filterValue,required 
     this.isChecked});

  @override
   List<Object?> get props => [];
  }
  1. I called that event in the on press of the checkbox

                CheckBox(
                   label: entry.key,
                   onChanged: (isChecked) {
                      context
                   .read<LearningDashboardScreenBloc>()
                   .add(CheckBoxValueChangedEvent(filterOption: widgetName, filterValue: 
                    entry.key, isChecked: isChecked));
    
             },
             key: GlobalKey<CheckBoxState>(),
             initValue: entry.value,
           ),
    

3.In the bloc class I wrote the function to change the bool value in the state class and emit the state,

void _onCheckBoxValueChangedEvent(CheckBoxValueChangedEvent event,
  Emitter<LearningDashboardScreenState> emit) {
Map<String, Map<String, dynamic>> filterOption = {};
filterOption=new Map<String, Map<String, dynamic>>.from(state.filterOption);
if (event.isChecked = true) {
  filterOption[event.filterOption]![event.filterValue] = true;
} else {
  filterOption[event.filterOption]![event.filterValue] = false;
}
emit(state.copyWith(filterOption: filterOption));

}

My problem is when I tick a checkbox I get,

Error: Unhandled error Unsupported operation: Cannot modify unmodifiable map occurred in 
 Instance of 'LearningDashboardScreenBloc'.
Devilish answered 16/12, 2021 at 8:7 Comment(0)
B
14

The problem is that when you declare a map as const you cannot modify the map.

In your constructor filterOption has a default value that is const:

LearningDashboardScreenState(
   {this.courses = const [],
   this.initialCoursesStatus = DataRetrievalStatus.NOT_INITIALIZED,
   this.filterOption = const {
     "Certificate Provider": {"NVQ": false, "City and Guilds": false},
     "Course Language": {"Sinhala": false, "English": false, "Tamil": false},
     "Duration": {"6 Months": false, "8 Months": false, "1Year": false},
     "Category": {
       "Baby Care": false,
       "First Aid": false,
       "Adult Care": false,
       "Mental Care": false,
       "Physiotherapy": false,
       "Baby First Aid": false,
       "Light Housekeeping": false,
       "Assist Methods": false
     },
   },
   this.currPage = 0,
   this.hasNextPage = false,
   this.moreCoursesStatus = DataRetrievalStatus.NOT_INITIALIZED}
);

A possible workaround would be to define the map literal in the initializer list, rather than as a default parameter (so that it doesn't need to be const).

LearningDashboardScreenState(
   {this.courses = const [],
   this.initialCoursesStatus = DataRetrievalStatus.NOT_INITIALIZED,
   Map<String, Map<String, dynamic>>? filterOption,
   this.currPage = 0,
   this.hasNextPage = false,
   this.moreCoursesStatus = DataRetrievalStatus.NOT_INITIALIZED}
): this.filterOption = filterOption ??
            {
              "Certificate Provider": {"NVQ": false, "City and Guilds": false},
              "Course Language": {
                "Sinhala": false,
                "English": false,
                "Tamil": false
              },
              "Duration": {
                "6 Months": false,
                "8 Months": false,
                "1Year": false
              },
              "Category": {
                "Baby Care": false,
                "First Aid": false,
                "Adult Care": false,
                "Mental Care": false,
                "Physiotherapy": false,
                "Baby First Aid": false,
                "Light Housekeeping": false,
                "Assist Methods": false
              }
            };
Bergmann answered 16/12, 2021 at 8:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.