0

After search and learn more about how can i add or update some field of data structure, we have below implementation

here we want to have a simple order structure with data which user can add service to order or update selected service count, for example in first time user doesn't selected any service then we have OrderStructure:[] which we initial that in constructor:

OrderNotifier() : super(_initial);
static OrderStructure _initial = OrderStructure('', []);

when user selected any service we pass three argument to increment method:

void increment({required int productId, required int serviceId, required int serviceCost})

if service doesn't exist it should be add which that belongs to product. each service belongs to product and we store that with productId in increment method we pass productId, serviceId and serviceCost, which can search into class data structure as OrderStructure.

if productId exist then we try to search serviceId, if serviceId exists. productId = true && serviceId = true then we should increment count value, if it doesn't exist it should be save as new item notice we should't have multiple the same productId or serviceId into OrderStructure.

each of them must be unique. each product can be have multiple service such as List, OrderStructure shouldn't have multiple productId

for example OrderStructure data structure can be:

OrderStructure:
   title : 'sample',
   products:[
       productId: 1
       services : [
         [serviceId:1, count:1, cost: 100],
         [serviceId:2, count:9, cost: 500],
         [serviceId:3, count:5, cost: 2000],
       ],
       productId: 2
       services : [
         [serviceId:3, count:10, cost: 100],
         [serviceId:5, count:6, cost: 500]
       ],
       productId: 3
       services : [
         [serviceId:3, count:10, cost: 100],
         [serviceId:5, count:6, cost: 500],
         [serviceId:6, count:6, cost: 500],
         [serviceId:7, count:6, cost: 500],
         [serviceId:8, count:6, cost: 500],
       ],
   ]

when user selected seriveId id 1 and productId id is 1, we search in this collection with productId and then serviceId, if its exists then count should be increment, otherwise it should be added into OrderStructure collection as a new one

for example:

OrderStructure:
   title : 'sample',
   products:[
       productId: 1
       services : [
         [serviceId:1, count:2, cost: 100],
         ...

we have this code, but it doesn't correct:

void increment({required int productId, required int serviceId, required int serviceCost}) {
  final newState = OrderStructure(state.title, [
    ...state.products,
    for (final product in state.products)
      if (product.id == productId)
        for (final service in product.services)
          if (service.id == serviceId)
            SelectedProducts(productId,
                [...product.services, SelectedProductServices(service.id, service.count + 1, service.cost)])
  ]);
  // ignore: iterable_contains_unrelated_type
  if (!newState.products.contains(productId) ||
      // ignore: iterable_contains_unrelated_type
      !newState.products.firstWhere((p) => p.id == productId).services.contains(serviceId)) {
    newState.products.add(
        SelectedProducts(serviceId, <SelectedProductServices>[SelectedProductServices(serviceId, 1, serviceCost)]));
  }
  state = newState;
}

FULL CODE:

void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context,ScopedReader watch) {
    final _orderProvider = watch(orderStateNotifierProvider.notifier);
    final _product = SelectedProducts(1, []);
    final _service = SelectedProductServices(1, 1, 111);
    return Scaffold(
      appBar: AppBar(
        title: Text('test'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            TextButton(
              child: Text('add new service'),
              onPressed: ()=>context.read(orderStateNotifierProvider.notifier)
                  .increment(productId:1, serviceId:1, serviceCost:100),
            ),

            // here we want to add new service to product, the same as above code, if productId doesn't exists
            // into `OrderStructure` it should be save with
            TextButton(
              child: Text('append service to product`s services'),
              onPressed: ()=>context.read(orderStateNotifierProvider.notifier)
                  .increment(productId:1, serviceId:2, serviceCost:500),
            ),

            Text('product`s service count: ${_orderProvider.state.products[0].services.length}')
          ],
        ),
      )
    );
  }
}


//------------------------


final orderStateNotifierProvider = StateNotifierProvider<OrderNotifier, OrderStructure>((ref) => OrderNotifier());

class OrderNotifier extends StateNotifier<OrderStructure> {
  OrderNotifier() : super(_initial);
  static OrderStructure _initial = OrderStructure('', []);

  void resetOrder() {
    _initial = const OrderStructure('', []);
  }

  void updateAddress({
    required String title,
  }) {
    state = OrderStructure(title,state.products);
  }


  void increment({required int productId, required int serviceId, required int serviceCost}) {
    final newState = OrderStructure(state.title, [
      ...state.products,
      for (final product in state.products)
        if (product.id == productId)
          for (final service in product.services)
            if (service.id == serviceId)
              SelectedProducts(productId,
                  [...product.services, SelectedProductServices(service.id, service.count + 1, service.cost)])
    ]);
    // ignore: iterable_contains_unrelated_type
    if (!newState.products.contains(productId) ||
        // ignore: iterable_contains_unrelated_type
        !newState.products.firstWhere((p) => p.id == productId).services.contains(serviceId)) {
      newState.products.add(
          SelectedProducts(serviceId, <SelectedProductServices>[SelectedProductServices(serviceId, 1, serviceCost)]));
    }
    state = newState;
  }
}

class OrderStructure {
  final String title;
  final List<SelectedProducts> products;

  const OrderStructure(this.title, this.products);
}

class SelectedProducts {
  final int id;
  final List<SelectedProductServices> services;

  SelectedProducts(this.id, this.services);
}

class SelectedProductServices {
  final int id;
  final int count;
  final int cost;

  const SelectedProductServices(this.id, this.count, this.cost);
}
DolDurma
  • 15,753
  • 51
  • 198
  • 377

3 Answers3

1

You need to do something like this.

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); 
  }
}
Taur
  • 514
  • 3
  • 8
  • thanks so much, i think you answered in my another issues, thanks. but i have a issue with your code. i invited you in chat to resolve this problem. https://chat.stackexchange.com/rooms/128804/riverpod – DolDurma Aug 21 '21 at 17:44
0

I fixed issues, increment method should be:

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;
}

thanks to TimWhiting

DolDurma
  • 15,753
  • 51
  • 198
  • 377
-1

You don't need to update the whole OrderStructure, only the reference to the inner list that contains the service to update:

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;
}

For the decrement part, just do service.count - 1 on the last line.

enzo
  • 9,861
  • 3
  • 15
  • 38