Flutter ProviderListener при ответе сервера с замороженной реализацией

В этом коде реализации, который я отправляю и получаю ответ от сервера, у меня есть поле в каждом ответе, и я обработал это с помощью freezed библиотека, и я хотел бы слушать этот класс как success способ показать что-то вроде SnakeBar

      @freezed
class ValidateCodeRequestState<T> with _$ValidateCodeRequestState {
  const factory ValidateCodeRequestState.idle() = Idle<T>;

  const factory ValidateCodeRequestState.loading() = Loading<T>;

  const factory ValidateCodeRequestState.success(T? data) = Success<T>;

  const factory ValidateCodeRequestState.error(Object error, StackTrace stackTrace) = Error<T>;
}

данные ответа сервера:

      {
  "message":"something",
  "statusCode":-1,
  ...
}

и я хочу послушать statusCode с ProviderListen, но я не могу, например:

      ProviderListener<ValidateCodeRequestState>(
 provider: validateCodeRequestState,
 onChange: (_, validateCodeRequestState) {
  if (weatherStateNotifier.state.statusCode  == 1) {   
     ...
 }
}

StateNotifier:

      class ValidateCodeRequestStateNotifier<T> extends StateNotifier<ValidateCodeRequestState<T>> {
  ValidateCodeRequestStateNotifier() : super(const ValidateCodeRequestState.idle());
  
  Future<ValidateCodeRequestState<T>> makeRequest(Future<T> Function() sendDioConnectionRequest)async{
    try{
      state = ValidateCodeRequestState<T>.loading();
      final response = await sendDioConnectionRequest();
      final newState = ValidateCodeRequestState<T>.success(response);
      if(mounted){
        state = newState;
      }
      return newState;
    }on DioError catch(error,stackTrace){
      final newState = ValidateCodeRequestState<T>.error(error, stackTrace);
      if(mounted){
        state = newState;
      }
      return newState;
    }
  }
}

репозиторий:

      final validateCodeRepositoryProvider = Provider((ref) => ValidateCodeRepository(ref.read));
final validateCodeProvider =
    StateNotifierProvider<ValidateCodeRequestNotifier, ValidateCodeRequestState<ValidateCodeResponseStructure>>(
        (ref) => ValidateCodeRequestNotifier(ref.watch(validateCodeRepositoryProvider)));

class ValidateCodeRepository {
  final Reader _reader;

  ValidateCodeRepository(this._reader);

  Future<ValidateCodeResponseStructure> getValidateCodeResponse(String mobileNumber, String validateCode) async {
    try {
      const r = RetryOptions(maxAttempts: 3);
      final response = await r.retry(
          () => _reader(dioProvider)
              .post(
                Server.$verifyCode,
                queryParameters: {'mobile_number': mobileNumber, 'validate_code': validateCode},
                options: Options(headers: {'Content-Type': 'application/json'}),
              )
              .timeout(const Duration(seconds: 3)),
          retryIf: (e) => e is SocketException || e is TimeoutException);

      return ValidateCodeResponseStructure.fromJson(response.data as Map<String, dynamic>);
    } on DioError catch (e) {
      throw e.error as Object;
      //FlutterE
    }
  }
}

class ValidateCodeRequestNotifier extends ValidateCodeRequestStateNotifier<ValidateCodeResponseStructure> {
  final ValidateCodeRepository _validateCodeRepository;

  ValidateCodeRequestNotifier(this._validateCodeRepository);

  Future<ValidateCodeRequestState<ValidateCodeResponseStructure>> checkValidateCode(
          String mobileNumber, String validateCode) =>
      makeRequest(() => _validateCodeRepository.getValidateCodeResponse(mobileNumber, validateCode));
}

и структура ответа:

      @freezed
class ValidateCodeResponseStructure with _$ValidateCodeResponseStructure {
  const factory ValidateCodeResponseStructure({
    required String message,
    required int statusCode,
  }) = _ValidateCodeResponseStructure;

  factory ValidateCodeResponseStructure.fromJson(Map<String, dynamic> json) => _$ValidateCodeResponseStructureFromJson(json);
}

3 ответа

Решение

Я исправил проблемы, increment метод должен быть:

      void increment(int productId, int serviceId, int serviceCost) {
  final newState = OrderStructure(state.title, state.address, state.lat, state.lang, state.pelak, state.vahed, []);
  for (final product in state.products) {
    if (product.id == productId) {
      if (product.services.any((s) => s.id == serviceId)) {
        final service = product.services.firstWhere((s) => s.id == serviceId);
        newState.products.add(SelectedProducts(productId, [
          ...product.services.where((s) => s.id != serviceId),
          SelectedProductServices(service.id, service.count + 1, service.cost)
        ]));
      } else {
        newState.products.add(
            SelectedProducts(productId, [...product.services, SelectedProductServices(serviceId, 1, serviceCost)]));
      }
    } else {
      newState.products.add(product);
    }
  }
  if (!newState.products.any((p) => p.id == productId) ||
      !newState.products.firstWhere((p) => p.id == productId).services.any((s) => s.id == serviceId)) {
    // Add new
    newState.products.add(SelectedProducts(productId, [SelectedProductServices(serviceId, 1, serviceCost)]));
  }
  state = newState;
}

спасибо TimWhiting

Вам нужно сделать что-то подобное.

      void increment(int productId, int serviceId, int serviceCost) {
  // copy the old state
  final newState = OrderStructure(state.title, [...state.products]);
  bool updated = false;
  /// We directly iterate on the newState.
  for(final product in newState.products){
      if( product.id != productId ) {
          continue; // jump to next the product because the current product do not match.
       } 
      updated = true; // We are sure that the product already exist.
      // We search the first index of service that has `id` equal to `serviceId`.
      final serviceIndex = product.services.indexWhere( (s) => s.id == serviceId );
      if( serviceIndex >= 0 ) {
         product.services[serviceIndex].count ++;
         break;
      }
      final newService = SelectedProductServices(serviceId, 1, serviceCost);
      product.services.add(newService);
      break;
  }
  // Now we only check if we have not made an update.
  if(!update) {
    final newService = SelectedProductServices(serviceId, 1, serviceCost);
    final newProduct =  SelectedProduct(productId, [newService]);
    newState.products.add(newProduct); 
  }
}

Вам не нужно обновлять все OrderStructure, только ссылка на внутренний список, содержащий обновляемую службу:

      void increment({required int productId, required int serviceId, required int serviceCost}) {
  final OrderStructure order = state;

  final int productIndex = order.products.indexWhere((product) => product.id == productId);

  final SelectedProducts product;
  // If the product has not been found, add it to the products
  if (productIndex == -1) {
    product = SelectedProducts(productId, []);
    order.products.add(product);

  // Otherwise, just get it
  } else {
    product = order.products[productIndex];
  }

  final int serviceIndex = product.services.indexWhere((service) => service.id == serviceId && service.cost == serviceCost);
  
  // If the service has not been found, add it to the services
  if (serviceIndex == -1) {
    product.services.add(SelectedProductServices(serviceId, 1, serviceCost));
  
  // Otherwise, just update its counter
  } else {
    final SelectedProductServices service = product.services[serviceIndex];
    product.services[serviceIndex] = SelectedProductServices(service.id, service.count + 1, service.cost);
  }

  // Set the state to the updated order structure
  state = order;
}

Для декремента просто сделайте service.count - 1 в последней строке.

Другие вопросы по тегам