Strings and formatting
In Dart, string literals use single or double quotes interchangeably — pick one as a team style (the official lint prefer_single_quotes picks single).
- Interpolation:
'Hello, $name', and'${user.name.toUpperCase()}'for a complex expression. Always prefer interpolation over concatenation ('a' + b) — it reads better and theprefer_interpolation_to_compose_stringslint enforces it. - Raw string:
r'C:\Users\path'— escape sequences are not processed. It is the de-facto standard for regex patterns:RegExp(r'\d{4}-\d{2}'). - Multiline: triple quotes
'''...'''. Good for SQL queries, JSON fixtures and long text. - Adjacent literals: literals written next to each other are concatenated automatically, which is handy for splitting long text across lines.
Strings are immutable. Every +, replaceAll or substring creates a new object. Building a string in a loop is therefore expensive: s += x over 1000 iterations allocates 1000 intermediates and means O(n²) copying.
The right tool is StringBuffer: it keeps an internal buffer, you append with write/writeln/writeAll, and call toString() once at the end.
For simple joins there are alternatives: list.join(', ') (the most readable) and '$a$b' interpolation. The practical rule: loop present, use `StringBuffer` or `join`; no loop, use interpolation.
Emoji break `length`. In Dart a String is a sequence of UTF-16 code units, and length returns the number of code units, not characters.
'a'.lengthis 1'é'.lengthis 1 (precomposed form)'😀'.lengthis 2 — this emoji lives outside the BMP and is encoded as a surrogate pair'👨👩👧'.lengthis 8 — the family emoji is several code points joined by zero-width joiners
Distinguish three levels: code unit (codeUnits, UTF-16), code point / rune (runes), and grapheme cluster (what a user calls "one character"). runes gives you the second level, but it is still not enough for family emoji or flags — that needs the .characters extension from the characters package.
The practical consequence: truncating user text with substring (the classic text.substring(0, 20) + '...') can cut an emoji in half and render a broken glyph.
| Operation | Method | Note |
|---|---|---|
| Parse a number | `int.tryParse(s)` | `parse` throws, `tryParse` returns `null` — use `tryParse` for user input |
| Decimal precision | `x.toStringAsFixed(2)` | Rounds and returns a string |
| Padding | `s.padLeft(2, '0')` | Typical for time formatting |
| Split / join | `s.split(',')`, `list.join(', ')` | `join` reads better than a loop |
| Trimming | `s.trim()` | First step in form validation |
| Emptiness check | `s.isEmpty`, `s.trim().isEmpty` | Do not write `s == ''` |
| Search / replace | `contains`, `replaceAll`, `RegExp` | Write the regex pattern as a raw string |
Interview tip. '😀'.length comes up often and the expected answer is not "1". A strong answer: "2 — because a Dart string is a sequence of UTF-16 code units and this emoji is a surrogate pair. For a real character count you use `runes` (code points) or the `characters` package's `.characters` (grapheme clusters)." Add the practical impact: truncating with substring can split an emoji in half.
A second frequent question: "What is wrong with `s += x` inside a loop?" — combine immutability, O(n²) copying and the StringBuffer alternative in one sentence.
📚 Sources and documentation
- String classofficialapi.dart.dev