Home > Enterprise >  StateNotifierProvider inside ModalBottomSheet in Riverpod | Flutter
StateNotifierProvider inside ModalBottomSheet in Riverpod | Flutter

Time:02-05

I am trying to use riverpod inside my modalBottomsheet to update values, but when i try to update the state, it updates but Widget(FilterView) doesnot rebuild. Since it is Inside of showModalBottomSheet, I have enclosed it inside

ProviderScope() 

Below is my code to open ModalBottomSheet

_showFilter(BuildContext context) {
    final container = ProviderScope.containerOf(context);

    showModalBottomSheet(
      context: context,
      builder: (context) => ProviderScope(
        parent: container,
        child: FilterView(
          key: Key("filterview"),
        ),
      ),
    );
  }

Below is FilterView Widget

class FilterView extends ConsumerWidget {
  const FilterView({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Consumer(builder: ((context, ref, _) {
      var reference = ref.read(filterController.notifier);
      var filter = ref.watch(filterController);
      return Card(
        child: Column(
          children: [
            const Text(
              "FILTER",
              style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Checkbox(
                    value: filter.today,
                    onChanged: (bool? value) {
                      reference.today(value ?? false);
                    }),
                const Text("Today only")
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                const Text("From Date:"),
                IconButton(
                  onPressed: filter.today
                      ? null
                      : () async {
                          var date =
                              await _showDatePicker(context, filter.fromDate);
                          reference.newFromDate(date);
                        },
                  icon: const Icon(Icons.date_range),
                ),
                if (filter.fromDate != null)
                  Text(filter.fromDate!.toHumanRedable()),
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                const Text("To Date"),
                IconButton(
                  onPressed: filter.today
                      ? null
                      : () async {
                          var date =
                              await _showDatePicker(context, filter.toDate);
                          reference.newToDate(date);
                        },
                  icon: const Icon(Icons.date_range),
                ),
                if (filter.toDate != null)
                  Text(filter.toDate!.toHumanRedable()),
              ],
            ),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text("Filter with Given Criteria"),
            ),
          ],
        ),
      );
    }));
  }

  Future<DateTime?> _showDatePicker(
      BuildContext context, DateTime? initialDate) async {
    var result = await showDatePicker(
      context: context,
      firstDate: DateTime.now().add(const Duration(days: -100)),
      lastDate: DateTime.now().add(const Duration(days: 100)),
      initialDate: initialDate ?? DateTime.now(),
    );
    return result;
  }
}

State does update I have verified that. But FilterView Widget is not rebuilding.

Below is the provider

final filterController =
    StateNotifierProvider<FilterNotifier, Filter>((ref) => FilterNotifier());

class FilterNotifier extends StateNotifier<Filter> {
  FilterNotifier() : super(Filter.initialize());

  void newFromDate(DateTime? fromDateTime) {
    state = state.registerNewFromDate(fromDateTime);
  }

  void newToDate(DateTime? toDateTime) {
    state = state.registerNewToDate(toDateTime);
  }

  void today(bool today) {
    state = state.registerNewValues(today, state.fromDate, state.toDate);
  }
}

Below is a filter class

class Filter {
  bool today = true;
  DateTime? fromDate;
  DateTime? toDate;

  Filter._(this.today, this.fromDate, this.toDate);

  static Filter initialize() {
    return Filter._(true, null, null);
  }

  Filter registerNewValues(bool today, DateTime? fromDate, DateTime? toDate) {
    this.today = today;
    this.fromDate = fromDate;
    this.toDate = toDate;
    return this;
  }

  Filter registerNewFromDate(DateTime? fromDate) {
    this.fromDate = fromDate;
    return this;
  }

  Filter registerNewToDate(DateTime? toDate) {
    this.toDate = toDate;
    return this;
  }
}

CodePudding user response:

The current model class Filter having same instance, therefore the UI isn't updating. To update the state, you need to pass new instance. I am creating copyWith method wich is handy,

class Filter { // I pefer creating final fileds with copyWith
  bool today = true;
  DateTime? fromDate;
  DateTime? toDate;

  Filter._(this.today, this.fromDate, this.toDate);

  static Filter initialize() {
    return Filter._(true, null, null);
  }

  Filter registerNewFromDate(DateTime? fromDate) {
    this.fromDate = fromDate;
    return this;
  }

  Filter registerNewToDate(DateTime? toDate) {
    this.toDate = toDate;
    return this;
  }

  Filter copyWith(
    bool? today,
    DateTime? fromDate,
    DateTime? toDate,
  ) {
    return Filter._(
      today ?? this.today,
      fromDate ?? this.fromDate,
      toDate ?? this.toDate,
    );
  }
}

  • Related