Sparround

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 the prefer_interpolation_to_compose_strings lint 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'.length is 1
  • 'é'.length is 1 (precomposed form)
  • '😀'.length is 2 — this emoji lives outside the BMP and is encoded as a surrogate pair
  • '👨‍👩‍👧'.length is 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.

OperationMethodNote
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

), stored as a class-level static final so it is not recompiled on every call. Close with a principle: the validation rule belongs in a model or validator class rather than the UI, so it can be covered by tests."}}]}