DMarlon Studio

Lesson 20 · 20 of 20

Project: shopping cart · Dart

A Dart project that brings the course together: a shopping cart with maps, functions, exceptions, null safety, fold and a switch expression for the discount.

Dart · Project: shopping cart in DMarlon Studio: the example's code and its output in the terminal

This program puts the whole course together. The shop's prices live in a `const` map outside main, because they never change. The cart is a list of product,units lines, just as they might come from a form or a file, and three of them contain mistakes on purpose.

The lineCost function works out the cost of one line. split(',') separates the product from the units; prices[parts[0]] gives a double?, because the product may not exist, and int.tryParse gives an int?, because the text may not be a number. If either is missing, or the quantity is not positive, `throw` raises an ArgumentError with a clear message; otherwise it returns price times units. After those if checks, Dart knows neither is null.

In main, the loop adds each line's cost into the totals map with (totals[product] ?? 0) + ..., so the milk, which appears twice, builds up. The `try` with `on ArgumentError catch (e)` handles the bad lines: they are printed with Skipped: and the cart carries on.

For the receipt, forEach prints each product with the arrow function row, which lines up the columns with padRight and padLeft. `fold` adds up the map's values and a `switch` expression with >= 20 and >= 10 patterns picks the discount: 17.60 euros earns 5%, and the total comes to 16.72.

A good next step is to give it a screen: in DMarlon Studio you can design a Flutter app on the phone canvas with the product list and a pay button, and reuse these same Dart functions.

Code · main.dart

const prices = {'bread': 1.20, 'milk': 0.95, 'cheese': 4.50, 'apples': 2.30};// Gives the cost of one "product,units" line, or throws an error.double lineCost(String line) {  var parts = line.split(',');  double? price = prices[parts[0]];  int? units = int.tryParse(parts[1]);  if (price == null) throw ArgumentError('${parts[0]} is not sold here');  if (units == null || units <= 0) throw ArgumentError('bad quantity in "$line"');  return price * units;}String row(String label, double euros) =>    label.padRight(14) + euros.toStringAsFixed(2).padLeft(7);void main() {  var cart = ['bread,2', 'cheese,1', 'milk,x', 'apples,3', 'ham,1', 'milk,4', 'bread,0'];  var totals = <String, double>{};  for (var line in cart) {    var product = line.split(',')[0];    try {      totals[product] = (totals[product] ?? 0) + lineCost(line);    } on ArgumentError catch (e) {      print('Skipped: ${e.message}');    }  }  totals.forEach((product, cost) => print(row(product, cost)));  double subtotal = totals.values.fold(0, (a, b) => a + b);  double rate = switch (subtotal) { >= 20 => 0.10, >= 10 => 0.05, _ => 0.0 };  print('-' * 21);  print(row('Subtotal', subtotal));  print(row('Discount (${(rate * 100).round()}%)', -subtotal * rate));  print(row('TOTAL', subtotal * (1 - rate)));}

Output · dart run main.dart

Skipped: bad quantity in "milk,x"
Skipped: ham is not sold here
Skipped: bad quantity in "bread,0"
bread            2.40
cheese           4.50
apples           6.90
milk             3.80
---------------------
Subtotal        17.60
Discount (5%)   -0.88
TOTAL           16.72

Key points

  • Fixed data goes in a const outside main.
  • double? and int? make you think about what may be missing.
  • throw in the function and try in the loop keep the error apart from its handling.
  • fold adds up the values of a map.
  • A switch expression turns the subtotal into a discount.

Exercise

Add a map with each product's VAT rate (4 for bread and milk, 10 for the rest) and print a VAT line on the receipt with the tax included in the total.