How to create multi select CheckboxListTile perfectly in flutter?

270 views Asked by At

I have a Bottom sheet which shows the list of Cities from which I have to select multiple Cities and if I open the bottomSheet second time previous selected cities should be pre selected but only if user press the save button after selecting Cities and if bottomSheet is dismissed then dont include the new selected cities. I am able to select the multiple cities and show previous selected cities in second time. also I am getting null as returned value if bottomSheet is dismissed. but also I am getting upated data in case of dismiss I dont know how it is gettting initialized with new selected values.

below is my code.

This is my bottom sheet.

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

import '../../../../business_logic/app/app_event_and_state.dart';
import '../bloc/city_bloc.dart';
import '../bloc/city_event.dart';
import '../bloc/city_state.dart';
import '../model/city_model.dart';

class MultiSelectCity extends StatefulWidget {
  const MultiSelectCity({Key? key, this.selectedCitiesList}) : super(key: key);
  final List<City>? selectedCitiesList;
  @override
  State<MultiSelectCity> createState() => _MultiSelectCityState();
}

class _MultiSelectCityState extends State<MultiSelectCity> {
  bool _isInit = true;
  List<City>? selectedCities = [];
  List<City> filteredCities = [];
  @override
  void didChangeDependencies() {
    print('didChangeDependencies');
    if (_isInit) {
      BlocProvider.of<CityBloc>(context)
          .add(const AppEventLoadCitiesWithFilter(isActive: true));
    }
    _isInit = false;
    selectedCities = widget.selectedCitiesList;
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<CityBloc, AppState>(
      builder: (context, state) {
        return DraggableScrollableSheet(
          initialChildSize: 0.6,
          minChildSize: 0.4,
          maxChildSize: 0.75,
          expand: false,
          builder: (context, scrollController) => Container(
            color: Colors.white,
            child: Column(children: [
              Container(
                color: Theme.of(context).colorScheme.background,
                padding: EdgeInsets.symmetric(vertical: 25.h, horizontal: 45.w),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Container(
                      height: 15.h,
                      width: 140.w,
                      decoration: BoxDecoration(
                        shape: BoxShape.rectangle,
                        borderRadius: BorderRadius.circular(25.r),
                        color: Colors.grey.shade400,
                      ),
                    ),
                    Padding(
                      padding:
                      EdgeInsets.only(top: 20.h, left: 10.w, bottom: 2.h),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          SizedBox(height: 25.h),
                          Text('Select City',
                              style: TextStyle(
                                  fontWeight: FontWeight.w900,
                                  fontSize: 65.sp)),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
              Expanded(
                child: state.isLoading
                    ? const Center(
                  child: CircularProgressIndicator(),
                )
                    : filteredCities.isNotEmpty
                    ? ListView.builder(
                  controller: scrollController,
                  itemBuilder: (context, index) {
                    return CheckboxListTile(
                      title: Text(
                        '${filteredCities[index].cityName},'
                            ' ${filteredCities[index].stateCode}, '
                            '${filteredCities[index].countryName}',
                        style:
                        Theme.of(context).textTheme.bodyLarge,
                      ),
                      value: selectedCities?.contains(
                          filteredCities[index]) ,
                      onChanged: (bool? value) {
                        setState(() {
                          if (selectedCities?.contains(
                              filteredCities[index]) ==
                              true) {
                            selectedCities?.removeWhere((element) =>
                            element ==
                                filteredCities[index]);
                          } else {
                            selectedCities
                                ?.add(filteredCities[index]);
                          }
                          print(value);
                        });
                      },
                    );
                  },
                  itemCount: filteredCities.length,
                )
                    : const Center(
                  child: Text("No cities found"),
                ),),
              Container(
                width: double.infinity,
                height: 50,
                margin: EdgeInsets.all(20),
                child: ElevatedButton(
                    onPressed: () {
                      return Navigator.of(context).pop(selectedCities); // if save pressed then pass selected cities else if bottomsheet is dismissed it returns null.
                    },

                    child: Text(
                      'Save',
                      style: TextStyle(
                          fontWeight: FontWeight.w700, fontSize: 45.sp),
                    )),
              )
            ]),
          ),
        );
      },
      listener: (context, state) {
        if (state is AppStateActiveCities) {
          print(state.activeCities.length.toString());
          filteredCities = state.activeCities;
        }
      },
    );
  }
}

This is how I am calling it.

Future<void> _showMultiSelectCityOptions() async {
  returnedSelectedCities = await showModalBottomSheet<List<City>>(
    isScrollControlled: true,
    isDismissible: false,
    context: context,
    builder: (context) => Padding(
      padding:
          EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
      child: MultiSelectCity(selectedCitiesList: selectedCities),
    ),
  );
  // here i am getting null as expected if bottomSheet is dismissed but also seletedCites are getting updated I dont know how. can some explain it.
  if (returnedSelectedCities != null) {
    setState(() {
      selectedCities = returnedSelectedCities;
    });
  }
}
0

There are 0 answers