๐ŸŽ“ A1Learner
๐Ÿ”
0 of 37 lessons completed

Dart Project Inventory

The inventory project combines classes, maps, and exceptions in one program. It tracks products and stock and prints a final report.

The Product class

The setter clamps a negative price to zero. The label getter builds its text from the stored name and price.

class Product {
  String name;
  double _price;
  Product(this.name, this._price);
  double get price => _price;
  set price(double value) {
    if (value < 0) {
      _price = 0;
    } else {
      _price = value;
    }
  }
  String get label => '$name at $price';
}

void main() {
  var pen = Product('pen', 2.5);
  print(pen.label);
  pen.price = -4;
  print(pen.price);
  print(pen.label);
}

Stock counts in a map

putIfAbsent adds a missing name and leaves an existing count alone. update then lowers the pen count by three.

void main() {
  var stock = <String, int>{};
  stock.putIfAbsent('pen', () => 10);
  stock.putIfAbsent('book', () => 4);
  stock.putIfAbsent('pen', () => 99);
  stock.update('pen', (count) => count - 3);
  print(stock['pen']);
  print(stock['book']);
  print(stock.length);
}

Finding a product

findProduct returns null for a missing name. Promotion makes found safe to use, and the fallback covers the missing name.

class Product {
  String name;
  double _price;
  Product(this.name, this._price);
  double get price => _price;
  set price(double value) {
    if (value < 0) {
      _price = 0;
    } else {
      _price = value;
    }
  }
  String get label => '$name at $price';
}

Product? findProduct(List<Product> items, String name) {
  for (var item in items) {
    if (item.name == name) {
      return item;
    }
  }
  return null;
}

void main() {
  var items = [Product('pen', 2.5), Product('book', 4.5)];
  var found = findProduct(items, 'book');
  if (found != null) {
    print(found.label);
  }
  var missing = findProduct(items, 'ruler');
  var fallback = missing ?? Product('ruler', 1.5);
  print(fallback.label);
}

An order that is too large

placeOrder throws when the order beats the stock. main catches the exception and prints its message text.

class OutOfStockException implements Exception {
  String message;
  OutOfStockException(this.message);
  @override
  String toString() => 'OutOfStockException: $message';
}

void placeOrder(Map<String, int> stock, String name, int amount) {
  var available = stock[name] ?? 0;
  if (amount > available) {
    throw OutOfStockException('only $available left for $name');
  }
  stock.update(name, (count) => count - amount);
}

void main() {
  var stock = {'pen': 5};
  placeOrder(stock, 'pen', 2);
  print(stock['pen']);
  try {
    placeOrder(stock, 'pen', 10);
  } on OutOfStockException catch (e) {
    print(e.toString());
  }
  print(stock['pen']);
}

Example

class Product {
  String name;
  double _price;
  Product(this.name, this._price);
  double get price => _price;
  set price(double value) {
    if (value < 0) {
      _price = 0;
    } else {
      _price = value;
    }
  }
  String get label => '$name at $price';
}

class OutOfStockException implements Exception {
  String message;
  OutOfStockException(this.message);
  @override
  String toString() => 'OutOfStockException: $message';
}

Product? findProduct(List<Product> items, String name) {
  for (var item in items) {
    if (item.name == name) {
      return item;
    }
  }
  return null;
}

void placeOrder(Map<String, int> stock, String name, int amount) {
  var available = stock[name] ?? 0;
  if (amount > available) {
    throw OutOfStockException('only $available left for $name');
  }
  stock.update(name, (count) => count - amount);
}

void main() {
  var items = [Product('pen', 2.5), Product('book', 4.5), Product('mug', 6.0)];
  var stock = <String, int>{};
  stock.putIfAbsent('pen', () => 10);
  stock.putIfAbsent('book', () => 3);
  stock.putIfAbsent('mug', () => 5);
  stock.putIfAbsent('pen', () => 99);
  var found = findProduct(items, 'mug');
  if (found != null) {
    print(found.label);
  }
  var missing = findProduct(items, 'ruler');
  var fallback = missing ?? Product('ruler', 1.5);
  print(fallback.label);
  var pen = findProduct(items, 'pen');
  if (pen != null) {
    pen.price = -1;
    print(pen.price);
  }
  placeOrder(stock, 'pen', 4);
  try {
    placeOrder(stock, 'book', 9);
  } on OutOfStockException catch (e) {
    print(e.toString());
  }
  stock.update('book', (count) => count + 2);
  items.sort((a, b) => a.name.compareTo(b.name));
  for (var item in items) {
    print('${item.label} stock ${stock[item.name]}');
  }
  print('Report complete for ${items.length} products');
}

Key points

  • Product stores a name and a price.
  • The price setter clamps a negative value to zero.
  • A computed getter builds a label from the stored fields.
  • A map stores the stock count for each product name.
  • putIfAbsent adds a count only when the name is missing, and update changes an existing count.
  • A lookup can return null when a product name is absent.
  • placeOrder throws OutOfStockException when an order is larger than the stock.
  • Sorting the products by name gives a fixed order for the report.

In this example, the program builds three products and a stock map with putIfAbsent. The mug lookup prints its label, and the missing ruler falls back to a new product. Setting the pen price to -1 prints the clamped value 0.0. The pen order succeeds, and the book order throws OutOfStockException, which the catch block prints. The report sorts the products by name and prints each label with its stock count, and the last line closes the report.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท DartPad

Exercise

What does the price setter do with a negative value?