In Flutter, what is the difference between print() and debugPrint(), and why does debugPrint exist at all?
answer
- Android drops lines under load
- a queue drained in chunks
- about 12 KB, then a one-second pause
- still prints in release
- a replaceable function variable
basics
~20 sprint() writes each message straight to the console. debugPrint() is Flutter's throttled wrapper: it queues lines and releases about 12 KB of text, then pauses a second, so Android does not drop log lines. Both still print in release builds.
solid answer
~40 s`print()` comes from `dart:core` and writes immediately. `debugPrint` is a Flutter function variable in `package:flutter/foundation.dart` whose default, `debugPrintThrottled`, splits the message into lines, queues them, prints roughly **12 * 1024 characters**, then pauses **one second** before continuing. It exists because platforms that rate-limit logging, Android in particular, silently drop lines when an app floods the log, which is exactly what large dumps like `debugDumpApp()` do. It also takes an optional `wrapWidth` for word wrapping. Two things people get wrong: `debugPrint` is **not** removed in release builds, so real logging should sit behind `kDebugMode` or an assert; and because it is buffered, mixing it with `print` can reorder messages. Tests swap in `debugPrintSynchronously`, and the `avoid_print` lint in `flutter_lints` nudges you away from raw `print`.
code
dart · 9 linesimport 'package:flutter/foundation.dart';
void logCart(List<String> items) {
// kDebugMode is a compile-time constant, so release builds drop this block.
if (kDebugMode) {
// Throttled: a long list is released in chunks instead of flooding the log.
debugPrint('cart (${items.length}): ${items.join(', ')}', wrapWidth: 100);
}
}go deeper
Remember that debugPrint throttles output so Android does not drop lines, and that it still prints in release unless you guard it with kDebugMode.
Explain the mechanism: a queue of lines, about 12 * 1024 characters per burst, a one-second pause, optional wrapWidth, and why that can reorder output relative to print.
Show you treat logs as a release concern: guard or silence debugPrint in release, keep personal data out of logs, and replace the debugPrint variable when you need to redirect framework output.
Set the team's logging policy: which API for which purpose, what may reach release logs, and lints like avoid_print enforced in CI.
## Two ways to write a line | | `print()` | `debugPrint()` | |---|---|---| | Defined in | `dart:core` | `package:flutter/foundation.dart` | | What it is | a top-level function | a **variable** of type `DebugPrintCallback` | | Default behaviour | writes the message immediately | `debugPrintThrottled`: queue, print in chunks, pause | | Line wrapping | none | optional `wrapWidth` word-wraps each line | | In release builds | prints | **prints** too | | Ordering with the other | immediate | buffered, so it can interleave out of order with `print` | ## Why debugPrint exists Some platforms **rate-limit** their system log. On Android, an app that writes too much too fast loses lines: they are silently dropped. Flutter's own diagnostics produce exactly that kind of burst, since `debugDumpApp()` or `debugDumpRenderTree()` can print thousands of lines in one call. `debugPrintThrottled`, the default implementation, protects against that: 1. It splits the message on newlines (wrapping each line first if `wrapWidth` is given) and appends the lines to a queue. 2. It prints lines until about **12 * 1024 characters** have gone out. 3. If lines remain, it waits **one second** on a `Timer` and continues. 4. When the queue is empty, the `debugPrintDone` future completes. The result is slower but complete output. The source comments admit it is a crude throttle, measured in characters rather than bytes. ## Consequences to know - **Out-of-order logs.** A `print` issued while `debugPrint` lines are still queued can appear before them. - **Release builds still print.** The doc comment says so explicitly and recommends wrapping calls in `if (kDebugMode)` or an `assert`, both of which the compiler removes from release builds. - **It is replaceable.** Because `debugPrint` is a variable, you can assign another `DebugPrintCallback`, for example to redirect framework output in a test or to add a prefix. The widget test binding uses `debugPrintSynchronously`, which prints without throttling so test output is deterministic. - **The lint.** `flutter_lints` enables `avoid_print`, which flags `print` in production code, pushing you towards `debugPrint`, `dart:developer`'s `log()` or a logging package. ## Choosing between them - A quick one-off trace while developing: either works; `debugPrint` is the habit that survives large output. - Large dumps or anything that may burst: `debugPrint`, so Android keeps every line. - Structured logs with a name, severity and attached error: `log()` from `dart:developer`, viewed in the DevTools Logging view. - Anything that must not reach release logs: guard it with `kDebugMode` or `assert`, whichever function you use. ## A common interview trap Candidates often say `debugPrint` "only prints in debug mode". The name suggests it, but the implementation has no mode check at all: in a release build it still queues and prints. That matters for privacy (tokens, emails, payment details in logs) and for noise in a user's device log. The fix is the guard, not the function name.
- Why can a Flutter debugPrint message appear after a print() call that ran later?`debugPrintThrottled` queues lines and drains them in chunks, pausing for a second when about 12 KB of text has gone out. A `print` bypasses that queue and writes at once, so while earlier `debugPrint` lines are still waiting, a later `print` can reach the console first. The source comments warn about exactly this interleaving.
- How do you stop Flutter's debugPrint from writing anything in a release build?Guard the call with `if (kDebugMode)` or put it inside an `assert`, both of which the compiler removes from release builds. Alternatively, assign a no-op `DebugPrintCallback` to the `debugPrint` variable at startup when not in debug mode, which also silences framework calls that use it.
saying these in an interview costs you the question
- debugPrint is automatically stripped from release builds.
- print() and debugPrint() are aliases that behave identically.
- debugPrint exists to add colours and timestamps to console output.
- debugPrint always preserves ordering relative to print().
- debugPrint is a final function that cannot be replaced.