# Dart Cheat Sheet

Course: [Dart](https://www.devtutorials.ai/learn/dart) · Section: Reference
Source: https://www.devtutorials.ai/learn/dart/reference/dart-cheat-sheet

> A lookup page for the whole course: one table per topic, from data types and null safety to classes, patterns, async code, files and JSON.

## What Is This Page?

**Reference page** = a page you look things up in, not one you read from top to bottom.

Nobody remembers every method name.
Programmers remember that a tool exists, then look up how to spell it.

This page gives you:

- one table per topic, in course order
- a Run box wherever seeing beats reading
- the traps worth keeping in your head

**Simple lesson:** remember that a tool exists, and look up the details here.

### A Real-World Example

A developer building a school app forgets whether it is `trim()` or `strip()`.
They do not reread a whole tutorial.
They open a reference, find the strings table, and get the answer in seconds.
This page is that reference for everything in the course.

<figure>
  <svg viewBox="0 0 560 190" role="img" aria-label="The topics of the course around the word Dart." width="100%">
    <rect x="0" y="0" width="560" height="190" rx="12" fill="#f8fafc" stroke="#e2e8f0" />
    <line x1="190" y1="35" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="20" y="18" width="170" height="34" rx="17" fill="#dbeafe" stroke="#1d4ed8"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="0s" repeatCount="indefinite" /></rect>
    <text x="105" y="40" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#1e3a8a">types and null safety</text>
    <line x1="190" y1="95" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="20" y="78" width="170" height="34" rx="17" fill="#dcfce7" stroke="#16a34a"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="1s" repeatCount="indefinite" /></rect>
    <text x="105" y="100" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#14532d">if, switch, loops</text>
    <line x1="190" y1="155" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="20" y="138" width="170" height="34" rx="17" fill="#fef3c7" stroke="#d97706"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="2s" repeatCount="indefinite" /></rect>
    <text x="105" y="160" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#78350f">functions, libraries</text>
    <line x1="370" y1="35" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="370" y="18" width="170" height="34" rx="17" fill="#ede9fe" stroke="#7c3aed"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="3s" repeatCount="indefinite" /></rect>
    <text x="455" y="40" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#4c1d95">lists, sets, maps</text>
    <line x1="370" y1="95" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="370" y="78" width="170" height="34" rx="17" fill="#fee2e2" stroke="#b91c1c"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="4s" repeatCount="indefinite" /></rect>
    <text x="455" y="100" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#7f1d1d">classes and patterns</text>
    <line x1="370" y1="155" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="370" y="138" width="170" height="34" rx="17" fill="#ffffff" stroke="#94a3b8"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="5s" repeatCount="indefinite" /></rect>
    <text x="455" y="160" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#0f172a">async, files, JSON</text>
    <circle cx="280" cy="95" r="48" fill="#0175C2" />
    <text x="280" y="101" textAnchor="middle" fontSize="17" fontWeight="bold" fill="#ffffff">Dart</text>
  </svg>
  <figcaption>Every topic of the course, on one page.</figcaption>
</figure>

Here is most of the course working together in one program:

```dart runnable title="The whole course in one program"
class Result {
  final String name;
  final String subject;
  final int mark;

  Result(this.name, this.subject, this.mark);

  String get grade => mark >= 35 ? 'pass' : 'fail';
}

void main() {
  var results = [
    Result('Asha', 'Maths', 78),
    Result('Ravi', 'Science', 85),
    Result('Meera', 'English', 30),
  ];

  results.sort((a, b) => b.mark.compareTo(a.mark));
  for (var r in results) {
    print('${r.name.padRight(8)}${r.subject.padRight(10)}${r.mark.toString().padLeft(4)}  ${r.grade}');
  }

  var passed = results.where((r) => r.grade == 'pass').length;
  var average = results.map((r) => r.mark).reduce((a, b) => a + b) / results.length;
  print('$passed of ${results.length} passed, average ${average.toStringAsFixed(1)}');
}
```

Open this page at the section you need. Each heading matches a lesson you can go back to.

## What You Will Learn

On this page, you will find:

1. The syntax for every topic in the course.
2. Run boxes for the trickiest parts.
3. The traps worth remembering, in one table.

## Data Types

Every basic type from the course, side by side:

| Type | Example | Notes |
|---|---|---|
| `int` | `78` | Whole numbers |
| `double` | `78.5` | Not exact: `0.1 + 0.2 != 0.3` |
| `num` | `78` or `78.5` | Either `int` or `double` |
| `String` | `'Asha'` | Cannot change. Every method returns a new string |
| `bool` | `true` | Only `true` and `false`. `if (1)` does not compile |
| `List<int>` | `[78, 85]` | Ordered, duplicates allowed |
| `Set<int>` | `{78, 85}` | No duplicates |
| `Map<String, int>` | `{'Asha': 78}` | Keys are unique |
| `int?` | `null` | The `?` allows `null` |
| `dynamic` | anything | No type checks. Avoid it |

The ways to make a variable:

| Form | Example | Can change? |
|---|---|---|
| `var` | `var marks = 78;` | Yes. Type fixed from the value |
| Type name | `int marks = 78;` | Yes |
| `final` | `final name = 'Asha';` | No. Set once, at run time |
| `const` | `const passMark = 35;` | No. Known before the program runs |
| `late` | `late String title;` | Set later, before first use |

```dart runnable title="Checking and converting"
void main() {
  var text = '78';
  print(text.runtimeType);
  print(int.parse(text) + 2);
  print(int.tryParse('abc'));
  print(double.parse('78.5').round());
  print(78.toString().length);
  print((2 / 3).toStringAsFixed(2));
}
```

`int.parse` crashes on bad text. `int.tryParse` gives `null` instead.

## Keywords

**Keyword** = a word Dart keeps for itself. You cannot use most of them as names.

| Group | Keywords |
|---|---|
| Variables | `var` `final` `const` `late` |
| Decide | `if` `else` `switch` `case` `default` `when` |
| Repeat | `for` `in` `while` `do` `break` `continue` |
| Functions | `return` `void` `async` `await` `yield` `sync` |
| Classes | `class` `extends` `implements` `with` `mixin` `abstract` `enum` `extension` |
| Modifiers | `sealed` `base` `interface` `final` `static` `factory` `covariant` |
| Errors | `try` `catch` `on` `finally` `throw` `rethrow` |
| Libraries | `import` `export` `library` `part` `show` `hide` `as` `deferred` |

**Contextual keyword** = a word like `on`, `show` or `async` that is special only in one place. You can still use it as a variable name.

## Operators

The operator groups:

| Group | Operators |
|---|---|
| Arithmetic | `+` `-` `*` `/` `~/` `%` |
| Increase / decrease | `++` `--` |
| Comparison | `==` `!=` `>` `<` `>=` `<=` |
| Assignment | `=` `+=` `-=` `*=` `/=` `~/=` `??=` |
| Logical | `&&` `\|\|` `!` |
| Type test | `is` `is!` `as` |
| Null-aware | `?.` `??` `??=` `!` `?[]` |
| Choose | `cond ? a : b` |
| Cascade | `..` `?..` |
| Spread | `...` `...?` |

```dart runnable title="The ones that surprise people"
void main() {
  print(7 / 2);
  print(7 ~/ 2);
  print(-7 ~/ 2);
  print(7 % 2);
  int? bonus;
  print(88 + (bonus ?? 5));
  var list = [3, 1, 2]..sort();
  print(list);
}
```

`/` always gives a `double`. `~/` gives an `int` and drops the part after the point.

## Null Safety

| Syntax | Meaning | Example |
|---|---|---|
| `T?` | This variable may be `null` | `String? name;` |
| `a ?? b` | `b` if `a` is `null` | `name ?? 'Guest'` |
| `a ??= b` | Set `a` only if it is `null` | `name ??= 'Guest';` |
| `a?.b` | Use `b` only if `a` is not `null` | `name?.length` |
| `a!` | "I promise this is not `null`" | `name!.length` |
| `late` | Will be set before first use | `late final int id;` |

`!` does not fix a `null`. It only moves the crash to run time. Check for `null` first, or use `??`.

## Conditional Statements

| Form | Syntax | Notes |
|---|---|---|
| if / else | `if (a) {} else if (b) {} else {}` | Brackets around the condition |
| Ternary | `var r = m >= 35 ? 'pass' : 'fail';` | A short `if` that gives a value |
| switch statement | `switch (v) { case 1: ...; default: ... }` | No `break` needed |
| switch expression | `var x = switch (v) { 1 => 'a', _ => 'b' };` | `_` matches anything else |
| Guard | `case int m when m >= 90:` | An extra check on a case |

## Loops

| Loop | Syntax | Use it when |
|---|---|---|
| Counting `for` | `for (var i = 0; i < n; i++) { }` | You need the position |
| `for-in` | `for (var x in xs) { }` | You need every item |
| `forEach` | `xs.forEach((x) => print(x));` | A short action per item |
| `while` | `while (x < 100) { }` | You do not know how many times |
| `do-while` | `do { } while (x < 100);` | It must run at least once |
| Control | `break`, `continue`, `outer: for ...` then `break outer;` | Leave or skip |

## Functions

| Topic | Syntax | Notes |
|---|---|---|
| Declare | `int add(int a, int b) { return a + b; }` | Return type first |
| Arrow | `int add(int a, int b) => a + b;` | For one expression |
| Optional | `void greet(String n, [String? title])` | Square brackets |
| Named | `void greet({required String name, int age = 0})` | Called with `greet(name: 'Asha')` |
| Anonymous | `(x) => x * 2` | A function with no name |
| Closure | `var add5 = makeAdder(5);` | Remembers values from where it was made |
| Function type | `int Function(int) f` | A function as a value |

```dart runnable title="Control flow and functions"
String grade(int m) => switch (m) {
  >= 90 => 'A',
  >= 75 => 'B',
  _ => 'C',
};

void greet({required String name, String title = 'Student'}) {
  print('Hello, $title $name');
}

void main() {
  for (var m in [95, 80, 60]) {
    print('$m: ${grade(m)}');
  }
  greet(name: 'Asha');
}
```

## Libraries and Packages

| Task | Syntax |
|---|---|
| Core library | `import 'dart:math';` |
| Package | `import 'package:http/http.dart' as http;` |
| Your own file | `import 'marks.dart';` |
| Only some names | `import 'dart:math' show max, min;` |
| All but some | `import 'dart:math' hide Random;` |
| Add a package | `dart pub add http` |
| Private name | Start it with `_`: `_total` |

The core libraries you will use most: `dart:core` (automatic), `dart:math`, `dart:convert`, `dart:io`, `dart:async`, `dart:isolate`.

## Collections

| Collection | Make | Add / change | Notes |
|---|---|---|---|
| List | `[1, 2]`, `List.filled(3, 0)` | `add`, `insert`, `removeAt`, `sort` | Index from `0` to `length - 1` |
| Set | `{1, 2}`, `<int>{}` | `add`, `remove`, `union`, `intersection` | `{}` alone is an empty **Map** |
| Map | `{'a': 1}`, `<String, int>{}` | `m['b'] = 2`, `remove`, `putIfAbsent` | `m['x']` is `null` when missing |
| Spread | `[...a, ...b]` | — | Joins collections |
| Collection if / for | `[if (ok) 'a', for (var x in xs) x * 2]` | — | Build lists with logic |

The iterable methods:

| Method | Gives | Example |
|---|---|---|
| `map` | Each item changed | `xs.map((x) => x * 2)` |
| `where` | Items that pass | `xs.where((x) => x >= 80)` |
| `fold` / `reduce` | One value | `xs.fold(0, (a, b) => a + b)` |
| `any` / `every` | `true` or `false` | `xs.any((x) => x < 35)` |
| `firstWhere` | First match | `xs.firstWhere((x) => x > 90, orElse: () => 0)` |
| `toList()` / `toSet()` | A real collection | `xs.map(...).toList()` |

```dart runnable title="Collections in one go"
void main() {
  var marks = {'Asha': 92, 'Ravi': 75, 'Priya': 85};
  var good = marks.entries.where((e) => e.value >= 80).map((e) => e.key).toList();
  print(good);
  print(marks.values.fold(0, (a, b) => a + b));
  var unique = {...[1, 2, 2, 3]};
  print(unique);
}
```

## String Methods

| Method | Example | Result |
|---|---|---|
| `length` | `'Asha'.length` | `4` |
| `toUpperCase` / `toLowerCase` | `'asha'.toUpperCase()` | `'ASHA'` |
| `trim` | `'  hi '.trim()` | `'hi'` |
| `contains` | `'Green Valley'.contains('Val')` | `true` |
| `startsWith` / `endsWith` | `'a.txt'.endsWith('.txt')` | `true` |
| `indexOf` | `'Asha'.indexOf('h')` | `2` |
| `substring` | `'Valley'.substring(0, 3)` | `'Val'` |
| `split` / `join` | `'a,b'.split(',')` | `['a', 'b']` |
| `replaceAll` | `'8-A'.replaceAll('-', '')` | `'8A'` |
| `padLeft` / `padRight` | `'7'.padLeft(3, '0')` | `'007'` |

Also remember:

- `'$name'` puts a variable in text. `'${a + b}'` puts an expression.
- `'''...'''` makes text over many lines.
- For many joins, use `StringBuffer`: `sb.write('x')`, then `sb.toString()`.

## Date and Time

| Task | Syntax | Notes |
|---|---|---|
| Now | `DateTime.now()` | |
| Make one | `DateTime(2026, 11, 14, 9, 30)` | Months start at **1** |
| Parse | `DateTime.parse('2026-11-14')` | ISO format |
| Duration | `Duration(days: 1, hours: 3)` | |
| Add / subtract | `d.add(Duration(days: 2))`, `d2.difference(d1)` | `difference` gives a `Duration` |
| Compare | `d1.isBefore(d2)`, `d1.isAfter(d2)` | |
| Parts | `d.year`, `d.month`, `d.day`, `d.weekday` | `weekday`: Monday is `1` |

```dart runnable title="Strings and dates"
void main() {
  var parts = 'Asha,8A,91'.split(',');
  print('${parts[0].toUpperCase()} ${parts.length}');
  var exam = DateTime(2026, 11, 14, 9, 30);
  var end = exam.add(Duration(hours: 3));
  print('${end.hour}:${end.minute.toString().padLeft(2, '0')}');
  print(DateTime(2026, 12, 25).difference(exam).inDays);
}
```

## Classes and Objects

| Topic | Syntax | Notes |
|---|---|---|
| Class | `class Student { String name; int marks; ... }` | A blueprint |
| Constructor | `Student(this.name, this.marks);` | `this.` fills fields |
| Named constructor | `Student.guest() : name = 'Guest', marks = 0;` | Another way to make one |
| Factory | `factory Student.fromJson(Map m) => ...` | Can return a checked or existing object |
| Private field | `int _marks;` | Private to the **file** |
| Getter / setter | `int get marks => _marks;` `set marks(int v) {...}` | Controlled access |
| Static | `static int count = 0;` | Belongs to the class, not each object |
| Cascade | `Student('Asha', 0)..marks = 90` | Many calls on one object |

## Inheritance, Polymorphism and Mixins

| Keyword | Means | Example |
|---|---|---|
| `extends` | "is a", gets the parent's code | `class Teacher extends Person` |
| `@override` | Replaces a parent method | `@override String toString()` |
| `super` | The parent's version | `super.greet()`, `Teacher(super.name)` |
| `abstract` | Cannot be made, only extended | `abstract class Shape { double area(); }` |
| `implements` | Promises the same methods | `class Dog implements Pet` |
| `with` | Adds a mixin's methods | `class Robot with Walker` |
| `mixin` | Reusable methods without a parent | `mixin Walker { void walk() {} }` |
| `extension` | Adds methods to an existing type | `extension on String { ... }` |
| `call()` | Makes an object usable like a function | `adder(5)` |

The class modifiers:

| Modifier | Outside the library, others can... |
|---|---|
| (none) | extend and implement |
| `base` | extend only |
| `interface` | implement only |
| `final` | neither |
| `sealed` | neither, and `switch` knows every subtype |
| `abstract` | extend and implement, but not make one |

```dart runnable title="A small class family"
abstract class Shape {
  double area();
}

class Square extends Shape {
  final double side;
  Square(this.side);
  @override
  double area() => side * side;
}

mixin Named {
  String get label => 'Shape #1';
}

class LabelledSquare extends Square with Named {
  LabelledSquare(super.side);
}

void main() {
  var s = LabelledSquare(2.5);
  print('${s.label}: ${s.area()}');
}
```

## Enums and Generics

| Topic | Syntax | Notes |
|---|---|---|
| Simple enum | `enum House { red, blue, green }` | `House.red.name` is `'red'` |
| Enhanced enum | `enum Grade { a(90), b(75); final int min; const Grade(this.min); }` | Fields and methods |
| All values | `House.values` | A list |
| Generic class | `class Box<T> { T value; Box(this.value); }` | `T` is filled in later |
| Generic function | `T first<T>(List<T> xs) => xs[0];` | |
| Limit the type | `<T extends num>` | Only numbers allowed |

## Records and Patterns

| Topic | Syntax | Notes |
|---|---|---|
| Record | `var r = ('Asha', age: 13);` | `r.$1`, `r.age` |
| Return two values | `(int, int) minMax(...)` | No class needed |
| Destructure | `var (a, b) = pair;` | |
| List pattern | `var [first, ...rest] = list;` | |
| Map pattern | `if (json case {'name': String n}) {...}` | Checks shape and type |
| Object pattern | `case Student(:name, :marks):` | Pulls fields out |
| Guard | `case (var x, _) when x > 0:` | Extra check |

```dart runnable title="Records and patterns"
(int, int) minMax(List<int> xs) {
  var sorted = [...xs]..sort();
  return (sorted.first, sorted.last);
}

void main() {
  var (low, high) = minMax([72, 95, 64]);
  print('$low to $high');
  var data = {'name': 'Ravi', 'marks': 75};
  if (data case {'name': String n, 'marks': int m} when m >= 35) {
    print('$n passed');
  }
}
```

## Exception Handling

| Part | Syntax | Notes |
|---|---|---|
| Try | `try { ... }` | Code that may fail |
| Catch a type | `on FormatException { ... }` | Specific first |
| Catch and see | `catch (e, s) { ... }` | `e` = error, `s` = stack trace |
| Always run | `finally { ... }` | Runs on both paths |
| Throw | `throw FormatException('bad marks');` | |
| Throw again | `rethrow;` | Inside a `catch` |
| Your own | `class MarksException implements Exception { ... }` | |

**Exception** = something that can go wrong in normal use, like bad input. **Error** = a bug in the program, like a wrong index. Catch exceptions, fix errors.

## Async: Futures, Streams and Isolates

| Piece | Syntax | For |
|---|---|---|
| Future | `Future<int> load() async { ... }` | One value, later |
| Wait | `var x = await load();` | Only inside `async` |
| Many at once | `await Future.wait([a(), b()])` | Run together, wait for all |
| Time limit | `await load().timeout(Duration(seconds: 2))` | |
| Stream | `Stream<int> count() async* { yield 1; }` | Many values, over time |
| Read a stream | `await for (var x in s) { }`, `s.listen(...)` | |
| Controller | `StreamController<int>()` | Make your own stream |
| Isolate | `await Isolate.run(() => heavyWork())` | Heavy maths without freezing |

## File Handling and JSON

| Task | Syntax | Notes |
|---|---|---|
| Read all | `File('a.txt').readAsStringSync()` | Or `await readAsString()` |
| Read lines | `File('a.txt').readAsLinesSync()` | A list of lines |
| Write | `File('a.txt').writeAsStringSync(text)` | Replaces the file |
| Append | `writeAsStringSync(t, mode: FileMode.append)` | Adds to the end |
| Exists? | `File('a.txt').existsSync()` | |
| Folder | `Directory('reports').createSync(recursive: true)` | |
| To JSON | `jsonEncode(map)` | Needs `import 'dart:convert';` |
| From JSON | `jsonDecode(text)` | Gives `Map` or `List` |
| In a class | `toJson()` and `factory X.fromJson(Map m)` | |

```dart runnable title="Async, exceptions and JSON together"
import 'dart:convert';

Future<String> fetch() => Future.delayed(Duration(seconds: 1), () => '{"name": "Asha", "marks": [92, 88]}');

Future<void> main() async {
  try {
    var data = jsonDecode(await fetch());
    print('${data['name']}: ${data['marks']}');
    jsonDecode('oops');
  } on FormatException {
    print('Bad JSON skipped');
  } finally {
    print('done');
  }
}
```

Forgot a method? Ask your editor. Type a dot after a value, like `name.`, and it lists everything that value can do. Hover over a method to read what it does.

**Easy Way to Remember:**

- Forgot a name? Type `.` and let the editor list it.
- Not sure of the type? Print `x.runtimeType`.
- Got an error? Read the first line: file, line number, then the problem.

## TL;DR

1. **Types** = `int`, `double`, `String`, `bool`, `List`, `Set`, `Map`, and `?` for null.
2. **Variables** = `var`, `final`, `const`, `late`.
3. **Control flow** = `if`, `switch` expressions, `for`, `for-in`, `while`.
4. **Functions** = arrow `=>`, optional `[]`, named `{}`, closures.
5. **OOP** = classes, `extends`, `implements`, `with`, modifiers, generics, enums.
6. **Records and patterns** = `(a, b)`, `var (x, y) =`, `case`, `when`.
7. **Safety** = `try` / `on` / `catch` / `finally`.
8. **Async and data** = `async` / `await`, Streams, isolates, `dart:io`, `dart:convert`.

## Common mistakes

The most common mistakes from the whole course, in one place.

<div className="tscroll">

| Mistake | What you see | Why it happens | Fix |
|---|---|---|---|
| `7 / 2` expected to be `3` | `3.5` | `/` always gives a `double` | `7 ~/ 2` |
| `=` in a condition | A compile error | `=` sets, `==` compares | `if (a == b)` |
| Using `!` on a `null` value | `Null check operator used on a null value` | `!` only promises | Check first, or use `??` |
| Forgetting `await` | `Instance of 'Future<String>'` | The call gives a Future | `await load()` |
| `list[list.length]` | `RangeError` | The last index is `length - 1` | `list.last` |
| `{}` meant as an empty set | It is a `Map` | `{}` means an empty map | `<int>{}` |
| `int.parse('abc')` | `FormatException` | The text is not a number | `int.tryParse` and `??` |
| A missing import | `Undefined name 'jsonEncode'` | Not in `dart:core` | `import 'dart:convert';` |
| `DateTime(2026, 0, 1)` | December of the year before | Months start at 1 | Use `1` for January |
| `catch (e)` above `on FormatException` | The specific block never runs | First match wins | Specific first |
| Writing a file without `FileMode.append` | Old text is gone | Write replaces | `mode: FileMode.append` |
| Setting a `final` variable again | A compile error | `final` locks the variable | Change the items, or use `var` |

</div>

## Check Yourself

## Exercises

## What to Remember

- Nobody remembers everything. Know a tool exists, and look up the spelling.
- Type `.` after a value and your editor lists what it can do.
- `/` gives a `double`. `~/` gives an `int`.
- `T?` allows `null`. Handle it with `??`, `?.` or a check, not with `!`.
- `final` is set once at run time. `const` is fixed before the program runs.
- `{}` is an empty map. Write `<int>{}` for an empty set.
- Months in `DateTime` start at 1.
- `async` functions give a Future. `await` it before you use the value.
- The first matching `on` block wins, so go specific to general.
- Writing a file replaces it. Use `FileMode.append` to add.

You have finished the Dart course. Next, try building a small app of your own, like a marks tracker that saves to a JSON file.
