[flutter_tool] Refactor Logger and Terminal to (mostly) no longer depend directly on context (#47269)

diff --git a/packages/flutter_tools/lib/runner.dart b/packages/flutter_tools/lib/runner.dart
index 2285c2a..c08a004 100644
--- a/packages/flutter_tools/lib/runner.dart
+++ b/packages/flutter_tools/lib/runner.dart
@@ -15,6 +15,7 @@
 import 'src/base/io.dart';
 import 'src/base/logger.dart';
 import 'src/base/process.dart';
+import 'src/base/terminal.dart';
 import 'src/base/utils.dart';
 import 'src/context_runner.dart';
 import 'src/doctor.dart';
@@ -223,7 +224,10 @@
 
 Future<String> _doctorText() async {
   try {
-    final BufferLogger logger = BufferLogger();
+    final BufferLogger logger = BufferLogger(
+      terminal: terminal,
+      outputPreferences: outputPreferences,
+    );
 
     await context.run<bool>(
       body: () => doctor.diagnose(verbose: true, showColor: false),
diff --git a/packages/flutter_tools/lib/src/base/io.dart b/packages/flutter_tools/lib/src/base/io.dart
index 4748512..40ca323 100644
--- a/packages/flutter_tools/lib/src/base/io.dart
+++ b/packages/flutter_tools/lib/src/base/io.dart
@@ -247,11 +247,10 @@
   bool get supportsAnsiEscapes => hasTerminal && io.stdout.supportsAnsiEscapes;
 }
 
-Stdio get stdio => context.get<Stdio>() ?? const Stdio();
-io.Stdout get stdout => stdio.stdout;
-Stream<List<int>> get stdin => stdio.stdin;
-io.IOSink get stderr => stdio.stderr;
-bool get stdinHasTerminal => stdio.stdinHasTerminal;
+io.Stdout get stdout => globals.stdio.stdout;
+Stream<List<int>> get stdin => globals.stdio.stdin;
+io.IOSink get stderr => globals.stdio.stderr;
+bool get stdinHasTerminal => globals.stdio.stdinHasTerminal;
 
 /// An overridable version of io.ProcessInfo.
 abstract class ProcessInfo {
diff --git a/packages/flutter_tools/lib/src/base/logger.dart b/packages/flutter_tools/lib/src/base/logger.dart
index 0291f5a..6dbebee 100644
--- a/packages/flutter_tools/lib/src/base/logger.dart
+++ b/packages/flutter_tools/lib/src/base/logger.dart
@@ -8,8 +8,8 @@
 
 import '../base/context.dart';
 import '../globals.dart' as globals;
-import 'io.dart';
-import 'terminal.dart';
+import 'io.dart' hide stderr, stdin, stdout;
+import 'terminal.dart' show AnsiTerminal, TerminalColor, OutputPreferences;
 import 'utils.dart';
 
 const int kDefaultStatusPadding = 59;
@@ -44,9 +44,15 @@
 
   bool quiet = false;
 
-  bool get supportsColor => globals.terminal.supportsColor;
+  bool get supportsColor;
 
-  bool get hasTerminal => stdio.hasTerminal;
+  bool get hasTerminal;
+
+  AnsiTerminal get _terminal;
+
+  OutputPreferences get _outputPreferences;
+
+  TimeoutConfiguration get _timeoutConfiguration;
 
   /// Display an error `message` to the user. Commands should use this if they
   /// fail in some way.
@@ -155,12 +161,37 @@
 }
 
 class StdoutLogger extends Logger {
+  StdoutLogger({
+    @required AnsiTerminal terminal,
+    @required Stdio stdio,
+    @required OutputPreferences outputPreferences,
+    @required TimeoutConfiguration timeoutConfiguration,
+  })
+    : _stdio = stdio,
+      _terminal = terminal,
+      _timeoutConfiguration = timeoutConfiguration,
+      _outputPreferences = outputPreferences;
+
+  @override
+  final AnsiTerminal _terminal;
+  @override
+  final OutputPreferences _outputPreferences;
+  @override
+  final TimeoutConfiguration _timeoutConfiguration;
+  final Stdio _stdio;
+
   Status _status;
 
   @override
   bool get isVerbose => false;
 
   @override
+  bool get supportsColor => _terminal.supportsColor;
+
+  @override
+  bool get hasTerminal => _stdio.stdinHasTerminal;
+
+  @override
   void printError(
     String message, {
     StackTrace stackTrace,
@@ -172,14 +203,19 @@
   }) {
     _status?.pause();
     message ??= '';
-    message = wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap);
+    message = wrapText(message,
+      indent: indent,
+      hangingIndent: hangingIndent,
+      shouldWrap: wrap ?? _outputPreferences.wrapText,
+      columnWidth: _outputPreferences.wrapColumn,
+    );
     if (emphasis == true) {
-      message = globals.terminal.bolden(message);
+      message = _terminal.bolden(message);
     }
-    message = globals.terminal.color(message, color ?? TerminalColor.red);
-    stderr.writeln(message);
+    message = _terminal.color(message, color ?? TerminalColor.red);
+    _stdio.stderr.writeln(message);
     if (stackTrace != null) {
-      stderr.writeln(stackTrace.toString());
+      _stdio.stderr.writeln(stackTrace.toString());
     }
     _status?.resume();
   }
@@ -196,12 +232,17 @@
   }) {
     _status?.pause();
     message ??= '';
-    message = wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap);
+    message = wrapText(message,
+      indent: indent,
+      hangingIndent: hangingIndent,
+      shouldWrap: wrap ?? _outputPreferences.wrapText,
+      columnWidth: _outputPreferences.wrapColumn,
+    );
     if (emphasis == true) {
-      message = globals.terminal.bolden(message);
+      message = _terminal.bolden(message);
     }
     if (color != null) {
-      message = globals.terminal.color(message, color);
+      message = _terminal.color(message, color);
     }
     if (newline != false) {
       message = '$message\n';
@@ -212,7 +253,7 @@
 
   @protected
   void writeToStdOut(String message) {
-    stdout.write(message);
+    _stdio.stdout.write(message);
   }
 
   @override
@@ -232,15 +273,18 @@
       return SilentStatus(
         timeout: timeout,
         onFinish: _clearStatus,
+        timeoutConfiguration: _timeoutConfiguration
       )..start();
     }
-    if (globals.terminal.supportsColor) {
+    if (supportsColor) {
       _status = AnsiStatus(
         message: message,
         timeout: timeout,
         multilineOutput: multilineOutput,
         padding: progressIndicatorPadding,
         onFinish: _clearStatus,
+        stdio: _stdio,
+        timeoutConfiguration: _timeoutConfiguration,
       )..start();
     } else {
       _status = SummaryStatus(
@@ -248,6 +292,8 @@
         timeout: timeout,
         padding: progressIndicatorPadding,
         onFinish: _clearStatus,
+        stdio: _stdio,
+        timeoutConfiguration: _timeoutConfiguration,
       )..start();
     }
     return _status;
@@ -270,10 +316,22 @@
 /// fonts, should be replaced by this class with printable symbols. Otherwise,
 /// they will show up as the unrepresentable character symbol '�'.
 class WindowsStdoutLogger extends StdoutLogger {
+  WindowsStdoutLogger({
+    @required AnsiTerminal terminal,
+    @required Stdio stdio,
+    @required OutputPreferences outputPreferences,
+    @required TimeoutConfiguration timeoutConfiguration,
+  }) : super(
+      terminal: terminal,
+      stdio: stdio,
+      outputPreferences: outputPreferences,
+      timeoutConfiguration: timeoutConfiguration,
+    );
+
   @override
   void writeToStdOut(String message) {
     // TODO(jcollins-g): wrong abstraction layer for this, move to [Stdio].
-    stdout.write(message
+    _stdio.stdout.write(message
         .replaceAll('✗', 'X')
         .replaceAll('✓', '√')
     );
@@ -281,9 +339,29 @@
 }
 
 class BufferLogger extends Logger {
+  BufferLogger({
+    @required AnsiTerminal terminal,
+    @required OutputPreferences outputPreferences,
+    TimeoutConfiguration timeoutConfiguration = const TimeoutConfiguration()
+  }) : _outputPreferences = outputPreferences,
+       _terminal = terminal,
+       _timeoutConfiguration = timeoutConfiguration;
+
+  @override
+  final OutputPreferences _outputPreferences;
+
+  @override
+  final AnsiTerminal _terminal;
+
+  @override
+  final TimeoutConfiguration _timeoutConfiguration;
+
   @override
   bool get isVerbose => false;
 
+  @override
+  bool get supportsColor => _terminal.supportsColor;
+
   final StringBuffer _error = StringBuffer();
   final StringBuffer _status = StringBuffer();
   final StringBuffer _trace = StringBuffer();
@@ -305,8 +383,13 @@
     int hangingIndent,
     bool wrap,
   }) {
-    _error.writeln(globals.terminal.color(
-      wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap),
+    _error.writeln(_terminal.color(
+      wrapText(message,
+        indent: indent,
+        hangingIndent: hangingIndent,
+        shouldWrap: wrap ?? _outputPreferences.wrapText,
+        columnWidth: _outputPreferences.wrapColumn,
+      ),
       color ?? TerminalColor.red,
     ));
   }
@@ -322,9 +405,19 @@
     bool wrap,
   }) {
     if (newline != false) {
-      _status.writeln(wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap));
+      _status.writeln(wrapText(message,
+        indent: indent,
+        hangingIndent: hangingIndent,
+        shouldWrap: wrap ?? _outputPreferences.wrapText,
+        columnWidth: _outputPreferences.wrapColumn,
+      ));
     } else {
-      _status.write(wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap));
+      _status.write(wrapText(message,
+        indent: indent,
+        hangingIndent: hangingIndent,
+        shouldWrap: wrap ?? _outputPreferences.wrapText,
+        columnWidth: _outputPreferences.wrapColumn,
+      ));
     }
   }
 
@@ -341,7 +434,11 @@
   }) {
     assert(progressIndicatorPadding != null);
     printStatus(message);
-    return SilentStatus(timeout: timeout)..start();
+    return SilentStatus(
+      timeout: timeout,
+      timeoutConfiguration: _timeoutConfiguration,
+
+    )..start();
   }
 
   /// Clears all buffers.
@@ -356,13 +453,23 @@
 }
 
 class VerboseLogger extends Logger {
-  VerboseLogger(this.parent) : assert(globals.terminal != null) {
+  VerboseLogger(this.parent,  { @required Stopwatch stopwatch }) :
+    _stopwatch = stopwatch ?? context.get<Stopwatch>() ??  Stopwatch() {
     _stopwatch.start();
   }
 
   final Logger parent;
 
-  final Stopwatch _stopwatch = context.get<Stopwatch>() ?? Stopwatch();
+  final Stopwatch _stopwatch;
+
+  @override
+  AnsiTerminal get _terminal => parent._terminal;
+
+  @override
+  OutputPreferences get _outputPreferences => parent._outputPreferences;
+
+  @override
+  TimeoutConfiguration get _timeoutConfiguration => parent._timeoutConfiguration;
 
   @override
   bool get isVerbose => true;
@@ -379,7 +486,12 @@
   }) {
     _emit(
       _LogType.error,
-      wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap),
+      wrapText(message,
+        indent: indent,
+        hangingIndent: hangingIndent,
+        shouldWrap: wrap ?? _outputPreferences.wrapText,
+        columnWidth: _outputPreferences.wrapColumn,
+      ),
       stackTrace,
     );
   }
@@ -394,7 +506,12 @@
     int hangingIndent,
     bool wrap,
   }) {
-    _emit(_LogType.status, wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap));
+    _emit(_LogType.status, wrapText(message,
+      indent: indent,
+      hangingIndent: hangingIndent,
+      shouldWrap: wrap ?? _outputPreferences.wrapText,
+      columnWidth: _outputPreferences.wrapColumn,
+    ));
   }
 
   @override
@@ -415,9 +532,10 @@
     final Stopwatch timer = Stopwatch()..start();
     return SilentStatus(
       timeout: timeout,
+      timeoutConfiguration: _timeoutConfiguration,
       onFinish: () {
         String time;
-        if (timeout == null || timeout > timeoutConfiguration.fastOperation) {
+        if (timeout == null || timeout > _timeoutConfiguration.fastOperation) {
           time = getElapsedAsSeconds(timer.elapsed);
         } else {
           time = getElapsedAsMilliseconds(timer.elapsed);
@@ -446,7 +564,7 @@
     } else {
       prefix = '+$millis ms'.padLeft(prefixWidth);
       if (millis >= 100) {
-        prefix = globals.terminal.bolden(prefix);
+        prefix = _terminal.bolden(prefix);
       }
     }
     prefix = '[$prefix] ';
@@ -455,12 +573,12 @@
     final String indentMessage = message.replaceAll('\n', '\n$indent');
 
     if (type == _LogType.error) {
-      parent.printError(prefix + globals.terminal.bolden(indentMessage));
+      parent.printError(prefix + _terminal.bolden(indentMessage));
       if (stackTrace != null) {
         parent.printError(indent + stackTrace.toString().replaceAll('\n', '\n$indent'));
       }
     } else if (type == _LogType.status) {
-      parent.printStatus(prefix + globals.terminal.bolden(indentMessage));
+      parent.printStatus(prefix + _terminal.bolden(indentMessage));
     } else {
       parent.printStatus(prefix + indentMessage);
     }
@@ -468,6 +586,12 @@
 
   @override
   void sendEvent(String name, [Map<String, dynamic> args]) { }
+
+  @override
+  bool get supportsColor => parent.supportsColor;
+
+  @override
+  bool get hasTerminal => parent.hasTerminal;
 }
 
 enum _LogType { error, status, trace }
@@ -495,12 +619,17 @@
 /// Generally, consider `logger.startProgress` instead of directly creating
 /// a [Status] or one of its subclasses.
 abstract class Status {
-  Status({ @required this.timeout, this.onFinish });
+  Status({
+    @required this.timeout,
+    @required TimeoutConfiguration timeoutConfiguration,
+    this.onFinish,
+  }) : _timeoutConfiguration = timeoutConfiguration;
 
   /// A [SilentStatus] or an [AnsiSpinner] (depending on whether the
   /// terminal is fancy enough), already started.
   factory Status.withSpinner({
     @required Duration timeout,
+    @required TimeoutConfiguration timeoutConfiguration,
     VoidCallback onFinish,
     SlowWarningCallback slowWarningCallback,
   }) {
@@ -509,13 +638,19 @@
         timeout: timeout,
         onFinish: onFinish,
         slowWarningCallback: slowWarningCallback,
+        timeoutConfiguration: timeoutConfiguration,
       )..start();
     }
-    return SilentStatus(timeout: timeout, onFinish: onFinish)..start();
+    return SilentStatus(
+      timeout: timeout,
+      onFinish: onFinish,
+      timeoutConfiguration: timeoutConfiguration,
+    )..start();
   }
 
   final Duration timeout;
   final VoidCallback onFinish;
+  final TimeoutConfiguration _timeoutConfiguration;
 
   @protected
   final Stopwatch _stopwatch = context.get<Stopwatch>() ?? Stopwatch();
@@ -526,7 +661,7 @@
 
   @protected
   String get elapsedTime {
-    if (timeout == null || timeout > timeoutConfiguration.fastOperation) {
+    if (timeout == null || timeout > _timeoutConfiguration.fastOperation) {
       return getElapsedAsSeconds(_stopwatch.elapsed);
     }
     return getElapsedAsMilliseconds(_stopwatch.elapsed);
@@ -568,8 +703,13 @@
 class SilentStatus extends Status {
   SilentStatus({
     @required Duration timeout,
+    @required TimeoutConfiguration timeoutConfiguration,
     VoidCallback onFinish,
-  }) : super(timeout: timeout, onFinish: onFinish);
+  }) : super(
+    timeout: timeout,
+    onFinish: onFinish,
+    timeoutConfiguration: timeoutConfiguration,
+  );
 }
 
 /// Constructor writes [message] to [stdout].  On [cancel] or [stop], will call
@@ -580,12 +720,16 @@
     @required Duration timeout,
     this.padding = kDefaultStatusPadding,
     VoidCallback onFinish,
+    Stdio stdio,
+    @required TimeoutConfiguration timeoutConfiguration,
   }) : assert(message != null),
        assert(padding != null),
-       super(timeout: timeout, onFinish: onFinish);
+       _stdio = stdio ?? globals.stdio,
+       super(timeout: timeout, onFinish: onFinish, timeoutConfiguration: timeoutConfiguration);
 
   final String message;
   final int padding;
+  final Stdio _stdio;
 
   bool _messageShowingOnCurrentLine = false;
 
@@ -597,7 +741,7 @@
 
   void _printMessage() {
     assert(!_messageShowingOnCurrentLine);
-    stdout.write('${message.padRight(padding)}     ');
+    _stdio.stdout.write('${message.padRight(padding)}     ');
     _messageShowingOnCurrentLine = true;
   }
 
@@ -608,14 +752,14 @@
     }
     super.stop();
     writeSummaryInformation();
-    stdout.write('\n');
+    _stdio.stdout.write('\n');
   }
 
   @override
   void cancel() {
     super.cancel();
     if (_messageShowingOnCurrentLine) {
-      stdout.write('\n');
+      _stdio.stdout.write('\n');
     }
   }
 
@@ -628,16 +772,16 @@
   /// Examples: `    0.5s`, `   150ms`, ` 1,600ms`, `    3.1s (!)`
   void writeSummaryInformation() {
     assert(_messageShowingOnCurrentLine);
-    stdout.write(elapsedTime.padLeft(_kTimePadding));
+    _stdio.stdout.write(elapsedTime.padLeft(_kTimePadding));
     if (seemsSlow) {
-      stdout.write(' (!)');
+      _stdio.stdout.write(' (!)');
     }
   }
 
   @override
   void pause() {
     super.pause();
-    stdout.write('\n');
+    _stdio.stdout.write('\n');
     _messageShowingOnCurrentLine = false;
   }
 }
@@ -652,10 +796,14 @@
     @required Duration timeout,
     VoidCallback onFinish,
     this.slowWarningCallback,
-  }) : super(timeout: timeout, onFinish: onFinish);
+    Stdio stdio,
+    @required TimeoutConfiguration timeoutConfiguration,
+  }) : _stdio = stdio ?? globals.stdio,
+       super(timeout: timeout, onFinish: onFinish, timeoutConfiguration: timeoutConfiguration);
 
   final String _backspaceChar = '\b';
   final String _clearChar = ' ';
+  final Stdio _stdio;
 
   bool timedOut = false;
 
@@ -688,7 +836,7 @@
   }
 
   void _startSpinner() {
-    stdout.write(_clear); // for _callback to backspace over
+    _stdio.stdout.write(_clear); // for _callback to backspace over
     timer = Timer.periodic(const Duration(milliseconds: 100), _callback);
     _callback(timer);
   }
@@ -697,21 +845,21 @@
     assert(this.timer == timer);
     assert(timer != null);
     assert(timer.isActive);
-    stdout.write(_backspace);
+    _stdio.stdout.write(_backspace);
     ticks += 1;
     if (seemsSlow) {
       if (!timedOut) {
         timedOut = true;
-        stdout.write('$_clear\n');
+        _stdio.stdout.write('$_clear\n');
       }
       if (slowWarningCallback != null) {
         _slowWarning = slowWarningCallback();
       } else {
         _slowWarning = _defaultSlowWarning;
       }
-      stdout.write(_slowWarning);
+      _stdio.stdout.write(_slowWarning);
     }
-    stdout.write('${_clearChar * spinnerIndent}$_currentAnimationFrame');
+    _stdio.stdout.write('${_clearChar * spinnerIndent}$_currentAnimationFrame');
   }
 
   @override
@@ -725,7 +873,7 @@
   }
 
   void _clearSpinner() {
-    stdout.write('$_backspace$_clear$_backspace');
+    _stdio.stdout.write('$_backspace$_clear$_backspace');
   }
 
   @override
@@ -759,10 +907,12 @@
     this.multilineOutput = false,
     this.padding = kDefaultStatusPadding,
     VoidCallback onFinish,
+    Stdio stdio,
+    TimeoutConfiguration timeoutConfiguration,
   }) : assert(message != null),
        assert(multilineOutput != null),
        assert(padding != null),
-       super(timeout: timeout, onFinish: onFinish);
+       super(timeout: timeout, onFinish: onFinish, stdio: stdio, timeoutConfiguration: timeoutConfiguration);
 
   final String message;
   final bool multilineOutput;
@@ -784,20 +934,20 @@
   void _startStatus() {
     final String line = '${message.padRight(padding)}$_margin';
     _totalMessageLength = line.length;
-    stdout.write(line);
+    _stdio.stdout.write(line);
   }
 
   @override
   void stop() {
     super.stop();
     writeSummaryInformation();
-    stdout.write('\n');
+    _stdio.stdout.write('\n');
   }
 
   @override
   void cancel() {
     super.cancel();
-    stdout.write('\n');
+    _stdio.stdout.write('\n');
   }
 
   /// Print summary information when a task is done.
@@ -808,16 +958,16 @@
   /// line before writing the elapsed time.
   void writeSummaryInformation() {
     if (multilineOutput) {
-      stdout.write('\n${'$message Done'.padRight(padding)}$_margin');
+      _stdio.stdout.write('\n${'$message Done'.padRight(padding)}$_margin');
     }
-    stdout.write(elapsedTime.padLeft(_kTimePadding));
+    _stdio.stdout.write(elapsedTime.padLeft(_kTimePadding));
     if (seemsSlow) {
-      stdout.write(' (!)');
+      _stdio.stdout.write(' (!)');
     }
   }
 
   void _clearStatus() {
-    stdout.write('${_backspaceChar * _totalMessageLength}${_clearChar * _totalMessageLength}${_backspaceChar * _totalMessageLength}');
+    _stdio.stdout.write('${_backspaceChar * _totalMessageLength}${_clearChar * _totalMessageLength}${_backspaceChar * _totalMessageLength}');
   }
 
   @override
diff --git a/packages/flutter_tools/lib/src/base/terminal.dart b/packages/flutter_tools/lib/src/base/terminal.dart
index e017556..5a0db58 100644
--- a/packages/flutter_tools/lib/src/base/terminal.dart
+++ b/packages/flutter_tools/lib/src/base/terminal.dart
@@ -4,10 +4,14 @@
 
 import 'dart:async';
 
+import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
+
 import '../convert.dart';
 import '../globals.dart' as globals;
 import 'context.dart';
 import 'io.dart' as io;
+import 'logger.dart';
 
 enum TerminalColor {
   red,
@@ -41,12 +45,13 @@
     bool wrapText,
     int wrapColumn,
     bool showColor,
-  }) : wrapText = wrapText ?? io.stdio.hasTerminal,
+  }) : wrapText = wrapText ?? globals.stdio.hasTerminal,
        _overrideWrapColumn = wrapColumn,
        showColor = showColor ?? globals.platform.stdoutSupportsAnsi ?? false;
 
   /// A version of this class for use in tests.
-  OutputPreferences.test() : wrapText = false, _overrideWrapColumn = null, showColor = false;
+  OutputPreferences.test({this.wrapText = false, int wrapColumn = kDefaultTerminalColumns, this.showColor = false})
+    : _overrideWrapColumn = wrapColumn;
 
   /// If [wrapText] is true, then any text sent to the context's [Logger]
   /// instance (e.g. from the [printError] or [printStatus] functions) will be
@@ -66,7 +71,7 @@
   /// terminal, or to [kDefaultTerminalColumns] if not writing to a terminal.
   final int _overrideWrapColumn;
   int get wrapColumn {
-    return _overrideWrapColumn ?? io.stdio.terminalColumns ?? kDefaultTerminalColumns;
+    return _overrideWrapColumn ?? globals.stdio.terminalColumns ?? kDefaultTerminalColumns;
   }
 
   /// Whether or not to output ANSI color codes when writing to the output
@@ -81,6 +86,13 @@
 }
 
 class AnsiTerminal {
+  AnsiTerminal({@required io.Stdio stdio, @required Platform platform})
+    : _stdio = stdio,
+      _platform = platform;
+
+  final io.Stdio _stdio;
+  final Platform _platform;
+
   static const String bold = '\u001B[1m';
   static const String resetAll = '\u001B[0m';
   static const String resetColor = '\u001B[39m';
@@ -107,7 +119,8 @@
 
   static String colorCode(TerminalColor color) => _colorMap[color];
 
-  bool get supportsColor => globals.platform.stdoutSupportsAnsi ?? false;
+  bool get supportsColor => _platform.stdoutSupportsAnsi ?? false;
+
   final RegExp _boldControls = RegExp('(${RegExp.escape(resetBold)}|${RegExp.escape(bold)})');
 
   /// Whether we are interacting with the flutter tool via the terminal.
@@ -159,10 +172,10 @@
   String clearScreen() => supportsColor ? clear : '\n\n';
 
   set singleCharMode(bool value) {
-    if (!io.stdinHasTerminal) {
+    if (!_stdio.stdinHasTerminal) {
       return;
     }
-    final io.Stdin stdin = io.stdin as io.Stdin;
+    final io.Stdin stdin = _stdio.stdin as io.Stdin;
     // The order of setting lineMode and echoMode is important on Windows.
     if (value) {
       stdin.echoMode = false;
@@ -179,7 +192,7 @@
   ///
   /// Useful when the console is in [singleCharMode].
   Stream<String> get keystrokes {
-    _broadcastStdInString ??= io.stdin.transform<String>(const AsciiDecoder(allowInvalid: true)).asBroadcastStream();
+    _broadcastStdInString ??= _stdio.stdin.transform<String>(const AsciiDecoder(allowInvalid: true)).asBroadcastStream();
     return _broadcastStdInString;
   }
 
@@ -198,6 +211,7 @@
   /// If [usesTerminalUi] is false, throws a [StateError].
   Future<String> promptForCharInput(
     List<String> acceptedCharacters, {
+    @required Logger logger,
     String prompt,
     int defaultChoiceIndex,
     bool displayAcceptedCharacters = true,
@@ -220,14 +234,14 @@
     singleCharMode = true;
     while (choice == null || choice.length > 1 || !acceptedCharacters.contains(choice)) {
       if (prompt != null) {
-        globals.printStatus(prompt, emphasis: true, newline: false);
+        logger.printStatus(prompt, emphasis: true, newline: false);
         if (displayAcceptedCharacters) {
-          globals.printStatus(' [${charactersToDisplay.join("|")}]', newline: false);
+          logger.printStatus(' [${charactersToDisplay.join("|")}]', newline: false);
         }
-        globals.printStatus(': ', emphasis: true, newline: false);
+        logger.printStatus(': ', emphasis: true, newline: false);
       }
       choice = await keystrokes.first;
-      globals.printStatus(choice);
+      logger.printStatus(choice);
     }
     singleCharMode = false;
     if (defaultChoiceIndex != null && choice == '\n') {
diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart
index b246d10..7bb93c8 100644
--- a/packages/flutter_tools/lib/src/base/utils.dart
+++ b/packages/flutter_tools/lib/src/base/utils.dart
@@ -6,6 +6,7 @@
 import 'dart:math' show Random, max;
 
 import 'package:intl/intl.dart';
+import 'package:meta/meta.dart';
 
 import '../convert.dart';
 import '../globals.dart' as globals;
@@ -395,7 +396,7 @@
 /// If [outputPreferences.wrapText] is false, then the text will be returned
 /// simply split at the newlines, but not wrapped. If [shouldWrap] is specified,
 /// then it overrides the [outputPreferences.wrapText] setting.
-List<String> _wrapTextAsLines(String text, { int start = 0, int columnWidth, bool shouldWrap }) {
+List<String> _wrapTextAsLines(String text, { int start = 0, int columnWidth, @required bool shouldWrap }) {
   if (text == null || text.isEmpty) {
     return <String>[''];
   }
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index 1b7cf43..b33a963 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -974,7 +974,7 @@
   }) {
     assert(timeout != null);
     printStatus(message);
-    return SilentStatus(timeout: timeout);
+    return SilentStatus(timeout: timeout, timeoutConfiguration: timeoutConfiguration);
   }
 
   void dispose() {
@@ -983,6 +983,12 @@
 
   @override
   void sendEvent(String name, [Map<String, dynamic> args]) { }
+
+  @override
+  bool get supportsColor => throw UnimplementedError();
+
+  @override
+  bool get hasTerminal => false;
 }
 
 /// A running application, started by this daemon.
@@ -1167,6 +1173,7 @@
 
     _status = SilentStatus(
       timeout: timeout,
+      timeoutConfiguration: timeoutConfiguration,
       onFinish: () {
         _status = null;
         _sendProgressEvent(<String, dynamic>{
@@ -1206,6 +1213,12 @@
       domain.sendEvent(name, args);
     }
   }
+
+  @override
+  bool get supportsColor => throw UnimplementedError();
+
+  @override
+  bool get hasTerminal => false;
 }
 
 class LogMessage {
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart
index e90cc72..a24d88d 100644
--- a/packages/flutter_tools/lib/src/context_runner.dart
+++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -19,6 +19,7 @@
 import 'base/os.dart';
 import 'base/process.dart';
 import 'base/signals.dart';
+import 'base/terminal.dart';
 import 'base/time.dart';
 import 'base/user_messages.dart';
 import 'base/utils.dart';
@@ -101,7 +102,19 @@
       IOSWorkflow: () => const IOSWorkflow(),
       KernelCompilerFactory: () => const KernelCompilerFactory(),
       LinuxWorkflow: () => const LinuxWorkflow(),
-      Logger: () => globals.platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(),
+      Logger: () => globals.platform.isWindows
+        ? WindowsStdoutLogger(
+            terminal: globals.terminal,
+            stdio: globals.stdio,
+            outputPreferences: outputPreferences,
+            timeoutConfiguration: timeoutConfiguration,
+          )
+        : StdoutLogger(
+            terminal: globals.terminal,
+            stdio: globals.stdio,
+            outputPreferences: outputPreferences,
+            timeoutConfiguration: timeoutConfiguration,
+          ),
       MacOSWorkflow: () => const MacOSWorkflow(),
       MDnsObservatoryDiscovery: () => MDnsObservatoryDiscovery(),
       OperatingSystemUtils: () => OperatingSystemUtils(),
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index 366e2f1..a7cd3ec 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -211,7 +211,12 @@
         lineBuffer.write(' (${result.statusInfo})');
       }
 
-      buffer.write(wrapText(lineBuffer.toString(), hangingIndent: result.leadingBox.length + 1));
+      buffer.write(wrapText(
+        lineBuffer.toString(),
+        hangingIndent: result.leadingBox.length + 1,
+        columnWidth: outputPreferences.wrapColumn,
+        shouldWrap: outputPreferences.wrapText,
+      ));
       buffer.writeln();
 
       if (result.type != ValidationType.installed) {
@@ -253,6 +258,7 @@
       final Status status = Status.withSpinner(
         timeout: timeoutConfiguration.fastOperation,
         slowWarningCallback: () => validator.slowWarning,
+        timeoutConfiguration: timeoutConfiguration,
       );
       ValidationResult result;
       try {
diff --git a/packages/flutter_tools/lib/src/globals.dart b/packages/flutter_tools/lib/src/globals.dart
index 777b844..f619882 100644
--- a/packages/flutter_tools/lib/src/globals.dart
+++ b/packages/flutter_tools/lib/src/globals.dart
@@ -10,6 +10,7 @@
 import 'base/context.dart';
 import 'base/error_handling_file_system.dart';
 import 'base/file_system.dart';
+import 'base/io.dart';
 import 'base/logger.dart';
 import 'base/terminal.dart';
 import 'cache.dart';
@@ -103,4 +104,10 @@
   return context?.get<AnsiTerminal>() ?? _defaultAnsiTerminal;
 }
 
-final AnsiTerminal _defaultAnsiTerminal = AnsiTerminal();
+final AnsiTerminal _defaultAnsiTerminal = AnsiTerminal(
+  stdio: stdio,
+  platform: platform,
+);
+
+/// The global Stdio wrapper.
+Stdio get stdio => context.get<Stdio>() ?? const Stdio();
diff --git a/packages/flutter_tools/lib/src/ios/code_signing.dart b/packages/flutter_tools/lib/src/ios/code_signing.dart
index fe375d4..992a0b7 100644
--- a/packages/flutter_tools/lib/src/ios/code_signing.dart
+++ b/packages/flutter_tools/lib/src/ios/code_signing.dart
@@ -243,6 +243,7 @@
       prompt: 'Please select a certificate for code signing',
       displayAcceptedCharacters: true,
       defaultChoiceIndex: 0, // Just pressing enter chooses the first one.
+      logger: globals.logger,
     );
 
     if (choice == 'a') {
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
index 09b50ca..7b4203d 100644
--- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -284,6 +284,7 @@
   }) async {
     final Status status = Status.withSpinner(
       timeout: timeoutConfiguration.fastOperation,
+      timeoutConfiguration: timeoutConfiguration,
     );
     final List<String> showBuildSettingsCommand = <String>[
       _executable,
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
index 7731bd0..ee6d56a 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -13,7 +13,6 @@
 import '../base/common.dart';
 import '../base/context.dart';
 import '../base/file_system.dart';
-import '../base/io.dart' as io;
 import '../base/logger.dart';
 import '../base/terminal.dart';
 import '../base/user_messages.dart';
@@ -146,13 +145,20 @@
 
   @override
   String get usageFooter {
-    return wrapText('Run "flutter help -v" for verbose help output, including less commonly used options.');
+    return wrapText('Run "flutter help -v" for verbose help output, including less commonly used options.',
+      columnWidth: outputPreferences.wrapColumn,
+      shouldWrap: outputPreferences.wrapText,
+    );
   }
 
   @override
   String get usage {
     final String usageWithoutDescription = super.usage.substring(description.length + 2);
-    return  '${wrapText(description)}\n\n$usageWithoutDescription';
+    final String prefix = wrapText(description,
+      shouldWrap: outputPreferences.wrapText,
+      columnWidth: outputPreferences.wrapColumn,
+    );
+    return '$prefix\n\n$usageWithoutDescription';
   }
 
   static String get defaultFlutterRoot {
@@ -233,7 +239,7 @@
     // Check for verbose.
     if (topLevelResults['verbose'] as bool) {
       // Override the logger.
-      contextOverrides[Logger] = VerboseLogger(globals.logger);
+      contextOverrides[Logger] = VerboseLogger(globals.logger, stopwatch: Stopwatch());
     }
 
     // Don't set wrapColumns unless the user said to: if it's set, then all
@@ -255,7 +261,7 @@
     // anything, unless the user explicitly said to.
     final bool useWrapping = topLevelResults.wasParsed('wrap')
         ? topLevelResults['wrap'] as bool
-        : io.stdio.terminalColumns != null && topLevelResults['wrap'] as bool;
+        : globals.stdio.terminalColumns != null && topLevelResults['wrap'] as bool;
     contextOverrides[OutputPreferences] = OutputPreferences(
       wrapText: useWrapping,
       showColor: topLevelResults['color'] as bool,
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
index 7c1dec7..3ce4cd6 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
@@ -699,7 +699,7 @@
     int progressIndicatorPadding = kDefaultStatusPadding,
   }) {
     _log('[progress] $message');
-    return SilentStatus(timeout: timeout)..start();
+    return SilentStatus(timeout: timeout, timeoutConfiguration: timeoutConfiguration)..start();
   }
 
   bool _interrupt = false;
@@ -722,6 +722,12 @@
 
   @override
   void sendEvent(String name, [Map<String, dynamic> args]) { }
+
+  @override
+  bool get supportsColor => throw UnimplementedError();
+
+  @override
+  bool get hasTerminal => false;
 }
 
 class LoggerInterrupted implements Exception {
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart
index 7145de8..eb7df8d 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart
@@ -7,7 +7,6 @@
 
 import 'package:flutter_tools/src/base/context.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/commands/clean.dart';
 import 'package:flutter_tools/src/ios/xcodeproj.dart';
 import 'package:flutter_tools/src/macos/xcode.dart';
@@ -77,7 +76,6 @@
       verify(mockFile.deleteSync(recursive: true)).called(1);
     }, overrides: <Type, Generator>{
       Platform: () => windowsPlatform,
-      Logger: () => BufferLogger(),
       Xcode: () => mockXcode,
     });
 
@@ -94,7 +92,6 @@
       verifyNever(mockFile.deleteSync(recursive: true));
     }, overrides: <Type, Generator>{
       Platform: () => windowsPlatform,
-      Logger: () => BufferLogger(),
       Xcode: () => mockXcode,
     });
   }
diff --git a/packages/flutter_tools/test/general.shard/android/gradle_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_test.dart
index 83f69c7..347e626 100644
--- a/packages/flutter_tools/test/general.shard/android/gradle_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/gradle_test.dart
@@ -15,7 +15,6 @@
 import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/io.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/cache.dart';
@@ -451,11 +450,9 @@
   });
 
   group('Config files', () {
-    BufferLogger mockLogger;
     Directory tempDir;
 
     setUp(() {
-      mockLogger = BufferLogger();
       tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_settings_aar_test.');
     });
 
@@ -498,13 +495,12 @@
 
       createSettingsAarGradle(tempDir);
 
-      expect(mockLogger.statusText, contains('created successfully'));
+      expect(testLogger.statusText, contains('created successfully'));
       expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue);
 
     }, overrides: <Type, Generator>{
       FileSystem: () => MemoryFileSystem(),
       ProcessManager: () => FakeProcessManager.any(),
-      Logger: () => mockLogger,
     });
 
     testUsingContext('create settings_aar.gradle when current settings.gradle doesn\'t load plugins', () {
@@ -532,13 +528,12 @@
 
       createSettingsAarGradle(tempDir);
 
-      expect(mockLogger.statusText, contains('created successfully'));
+      expect(testLogger.statusText, contains('created successfully'));
       expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue);
 
     }, overrides: <Type, Generator>{
       FileSystem: () => MemoryFileSystem(),
       ProcessManager: () => FakeProcessManager.any(),
-      Logger: () => mockLogger,
     });
   });
 
diff --git a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart
index 39c8b02..dd616d2 100644
--- a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart
+++ b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart
@@ -9,7 +9,6 @@
 
 import 'package:flutter_tools/src/asset.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/bundle.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/devfs.dart';
@@ -183,16 +182,15 @@
     });
   });
 
-  test('Failed directory delete shows message', () async {
+  testUsingContext('Failed directory delete shows message', () async {
     final MockDirectory mockDirectory = MockDirectory();
-    final BufferLogger bufferLogger = BufferLogger();
     when(mockDirectory.existsSync()).thenReturn(true);
     when(mockDirectory.deleteSync(recursive: true)).thenThrow(const FileSystemException('ABCD'));
 
-    await writeBundle(mockDirectory, <String, DevFSContent>{}, loggerOverride: bufferLogger);
+    await writeBundle(mockDirectory, <String, DevFSContent>{}, loggerOverride: testLogger);
 
     verify(mockDirectory.createSync(recursive: true)).called(1);
-    expect(bufferLogger.errorText, contains('ABCD'));
+    expect(testLogger.errorText, contains('ABCD'));
   });
 }
 
diff --git a/packages/flutter_tools/test/general.shard/base/build_test.dart b/packages/flutter_tools/test/general.shard/base/build_test.dart
index 450e818..7a70c4d 100644
--- a/packages/flutter_tools/test/general.shard/base/build_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/build_test.dart
@@ -13,7 +13,6 @@
 import 'package:flutter_tools/src/base/context.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/io.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/process.dart';
 import 'package:flutter_tools/src/macos/xcode.dart';
 import 'package:flutter_tools/src/version.dart';
@@ -225,7 +224,6 @@
     MockAndroidSdk mockAndroidSdk;
     MockArtifacts mockArtifacts;
     MockXcode mockXcode;
-    BufferLogger bufferLogger;
 
     setUp(() async {
       fs = MemoryFileSystem();
@@ -247,7 +245,6 @@
       mockXcode = MockXcode();
       when(mockXcode.sdkLocation(any)).thenAnswer((_) => Future<String>.value(kSDKPath));
 
-      bufferLogger = BufferLogger();
       for (final BuildMode mode in BuildMode.values) {
         when(mockArtifacts.getArtifactPath(Artifact.snapshotDart,
             platform: anyNamed('platform'), mode: mode)).thenReturn(kSnapshotDart);
@@ -261,7 +258,6 @@
       ProcessManager: () => FakeProcessManager.any(),
       GenSnapshot: () => genSnapshot,
       Xcode: () => mockXcode,
-      Logger: () => bufferLogger,
     };
 
     testUsingContext('iOS debug AOT snapshot is invalid', () async {
@@ -658,7 +654,7 @@
 
       expect(genSnapshotExitCode, 0);
       expect(genSnapshot.callCount, 1);
-      expect(bufferLogger.statusText, matches(RegExp(r'snapshot\(CompileTime\): \d+ ms.')));
+      expect(testLogger.statusText, matches(RegExp(r'snapshot\(CompileTime\): \d+ ms.')));
     }, overrides: contextOverrides);
   });
 }
diff --git a/packages/flutter_tools/test/general.shard/base/logger_test.dart b/packages/flutter_tools/test/general.shard/base/logger_test.dart
index 189464c..7095381 100644
--- a/packages/flutter_tools/test/general.shard/base/logger_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/logger_test.dart
@@ -6,7 +6,6 @@
 
 import 'package:platform/platform.dart';
 import 'package:flutter_tools/src/base/context.dart';
-import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
@@ -16,7 +15,7 @@
 import '../../src/context.dart';
 import '../../src/mocks.dart';
 
-final Generator _kNoAnsiPlatform = () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
+final Platform _kNoAnsiPlatform = FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
 
 void main() {
   final String red = RegExp.escape(AnsiTerminal.red);
@@ -30,9 +29,15 @@
     setUp(() {
       fakeStopWatch = FakeStopwatch();
     });
-    testUsingContext('error', () async {
-      final BufferLogger mockLogger = BufferLogger();
-      final VerboseLogger verboseLogger = VerboseLogger(mockLogger);
+    testWithoutContext('error', () async {
+      final BufferLogger mockLogger = BufferLogger(
+        terminal: AnsiTerminal(
+          stdio: MockStdio(),
+          platform: _kNoAnsiPlatform,
+        ),
+        outputPreferences: OutputPreferences.test(showColor: false),
+      );
+      final VerboseLogger verboseLogger = VerboseLogger(mockLogger, stopwatch: fakeStopWatch);
 
       verboseLogger.printStatus('Hey Hey Hey Hey');
       verboseLogger.printTrace('Oooh, I do I do I do');
@@ -42,15 +47,17 @@
                                              r'\[ (?: {0,2}\+[0-9]{1,4} ms|       )\] Oooh, I do I do I do\n$'));
       expect(mockLogger.traceText, '');
       expect(mockLogger.errorText, matches( r'^\[ (?: {0,2}\+[0-9]{1,4} ms|       )\] Helpless!\n$'));
-    }, overrides: <Type, Generator>{
-      OutputPreferences: () => OutputPreferences(showColor: false),
-      Platform: _kNoAnsiPlatform,
-      Stopwatch: () => fakeStopWatch,
     });
 
-    testUsingContext('ANSI colored errors', () async {
-      final BufferLogger mockLogger = BufferLogger();
-      final VerboseLogger verboseLogger = VerboseLogger(mockLogger);
+    testWithoutContext('ANSI colored errors', () async {
+      final BufferLogger mockLogger = BufferLogger(
+        terminal: AnsiTerminal(
+          stdio:  MockStdio(),
+          platform: FakePlatform()..stdoutSupportsAnsi = true,
+        ),
+        outputPreferences: OutputPreferences.test(showColor: true),
+      );
+      final VerboseLogger verboseLogger = VerboseLogger(mockLogger, stopwatch: fakeStopWatch);
 
       verboseLogger.printStatus('Hey Hey Hey Hey');
       verboseLogger.printTrace('Oooh, I do I do I do');
@@ -64,10 +71,6 @@
       expect(
           mockLogger.errorText,
           matches('^$red' r'\[ (?: {0,2}\+[0-9]{1,4} ms|       )\] ' '${bold}Helpless!$resetBold$resetColor' r'\n$'));
-    }, overrides: <Type, Generator>{
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
-      Stopwatch: () => fakeStopWatch,
     });
   });
 
@@ -84,6 +87,8 @@
         timeout: const Duration(seconds: 2),
         padding: 20,
         onFinish: () => called += 1,
+        stdio: mockStdio,
+        timeoutConfiguration: const TimeoutConfiguration(),
       );
     }
 
@@ -110,6 +115,8 @@
         FakeAsync().run((FakeAsync time) {
           final AnsiSpinner ansiSpinner = AnsiSpinner(
             timeout: const Duration(hours: 10),
+            stdio: mockStdio,
+            timeoutConfiguration: const TimeoutConfiguration(),
           )..start();
           doWhileAsync(time, () => ansiSpinner.ticks < 10);
           List<String> lines = outputStdout();
@@ -138,7 +145,6 @@
         expect(done, isTrue);
       }, overrides: <Type, Generator>{
         Platform: () => FakePlatform(operatingSystem: testOs),
-        Stdio: () => mockStdio,
         Stopwatch: () => mockStopwatch,
       });
 
@@ -148,6 +154,8 @@
         FakeAsync().run((FakeAsync time) {
           final AnsiSpinner ansiSpinner = AnsiSpinner(
             timeout: const Duration(seconds: 2),
+            stdio: mockStdio,
+            timeoutConfiguration: const TimeoutConfiguration(),
           )..start();
           mockStopwatch.elapsed = const Duration(seconds: 1);
           doWhileAsync(time, () => ansiSpinner.ticks < 10); // one second
@@ -165,14 +173,22 @@
         expect(done, isTrue);
       }, overrides: <Type, Generator>{
         Platform: () => FakePlatform(operatingSystem: testOs),
-        Stdio: () => mockStdio,
         Stopwatch: () => mockStopwatch,
       });
 
+      // Uses Stopwatch from context.
       testUsingContext('Stdout startProgress on colored terminal for $testOs', () async {
         bool done = false;
         FakeAsync().run((FakeAsync time) {
-          final Logger logger = context.get<Logger>();
+          final Logger logger = StdoutLogger(
+            terminal: AnsiTerminal(
+              stdio: mockStdio,
+              platform: FakePlatform(operatingSystem: testOs)..stdoutSupportsAnsi = true,
+            ),
+            stdio: mockStdio,
+            outputPreferences: OutputPreferences.test(showColor: true),
+            timeoutConfiguration: const TimeoutConfiguration(),
+          );
           final Status status = logger.startProgress(
             'Hello',
             progressId: null,
@@ -190,18 +206,21 @@
           done = true;
         });
         expect(done, isTrue);
-      }, overrides: <Type, Generator>{
-        Logger: () => StdoutLogger(),
-        OutputPreferences: () => OutputPreferences(showColor: true),
-        Platform: () => FakePlatform(operatingSystem: testOs)..stdoutSupportsAnsi = true,
-        Stdio: () => mockStdio,
       });
 
       testUsingContext('Stdout startProgress on colored terminal pauses on $testOs', () async {
         bool done = false;
         FakeAsync().run((FakeAsync time) {
           mockStopwatch.elapsed = const Duration(seconds: 5);
-          final Logger logger = context.get<Logger>();
+          final Logger logger = StdoutLogger(
+            terminal: AnsiTerminal(
+              stdio: mockStdio,
+              platform: FakePlatform(operatingSystem: testOs)..stdoutSupportsAnsi = true,
+            ),
+            stdio: mockStdio,
+            outputPreferences: OutputPreferences.test(showColor: true),
+            timeoutConfiguration: const TimeoutConfiguration(),
+          );
           final Status status = logger.startProgress(
             'Knock Knock, Who\'s There',
             timeout: const Duration(days: 10),
@@ -232,10 +251,6 @@
         });
         expect(done, isTrue);
       }, overrides: <Type, Generator>{
-        Logger: () => StdoutLogger(),
-        OutputPreferences: () => OutputPreferences(showColor: true),
-        Platform: () => FakePlatform(operatingSystem: testOs)..stdoutSupportsAnsi = true,
-        Stdio: () => mockStdio,
         Stopwatch: () => mockStopwatch,
       });
 
@@ -270,7 +285,6 @@
         expect(done, isTrue);
       }, overrides: <Type, Generator>{
         Platform: () => FakePlatform(operatingSystem: testOs),
-        Stdio: () => mockStdio,
         Stopwatch: () => mockStopwatch,
       });
 
@@ -307,7 +321,6 @@
         expect(done, isTrue);
       }, overrides: <Type, Generator>{
         Platform: () => FakePlatform(operatingSystem: testOs),
-        Stdio: () => mockStdio,
         Stopwatch: () => mockStopwatch,
       });
 
@@ -353,7 +366,6 @@
         expect(done, isTrue);
       }, overrides: <Type, Generator>{
         Platform: () => FakePlatform(operatingSystem: testOs),
-        Stdio: () => mockStdio,
         Stopwatch: () => mockStopwatch,
       });
     }
@@ -372,33 +384,48 @@
         timeout: timeoutConfiguration.slowOperation,
         padding: 20,
         onFinish: () => called++,
+        stdio: mockStdio,
+        timeoutConfiguration: const TimeoutConfiguration(),
       );
     });
 
     List<String> outputStdout() => mockStdio.writtenToStdout.join('').split('\n');
     List<String> outputStderr() => mockStdio.writtenToStderr.join('').split('\n');
 
-    testUsingContext('Error logs are wrapped', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Error logs are wrapped', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printError('0123456789' * 15);
       final List<String> lines = outputStderr();
+
       expect(outputStdout().length, equals(1));
       expect(outputStdout().first, isEmpty);
       expect(lines[0], equals('0123456789' * 4));
       expect(lines[1], equals('0123456789' * 4));
       expect(lines[2], equals('0123456789' * 4));
       expect(lines[3], equals('0123456789' * 3));
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
-    testUsingContext('Error logs are wrapped and can be indented.', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Error logs are wrapped and can be indented.', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printError('0123456789' * 15, indent: 5);
       final List<String> lines = outputStderr();
+
       expect(outputStdout().length, equals(1));
       expect(outputStdout().first, isEmpty);
       expect(lines.length, equals(6));
@@ -408,17 +435,21 @@
       expect(lines[3], equals('     56789012345678901234567890123456789'));
       expect(lines[4], equals('     0123456789'));
       expect(lines[5], isEmpty);
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
-    testUsingContext('Error logs are wrapped and can have hanging indent.', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Error logs are wrapped and can have hanging indent.', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printError('0123456789' * 15, hangingIndent: 5);
       final List<String> lines = outputStderr();
+
       expect(outputStdout().length, equals(1));
       expect(outputStdout().first, isEmpty);
       expect(lines.length, equals(6));
@@ -428,17 +459,21 @@
       expect(lines[3], equals('     01234567890123456789012345678901234'));
       expect(lines[4], equals('     56789'));
       expect(lines[5], isEmpty);
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
-    testUsingContext('Error logs are wrapped, indented, and can have hanging indent.', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Error logs are wrapped, indented, and can have hanging indent.', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printError('0123456789' * 15, indent: 4, hangingIndent: 5);
       final List<String> lines = outputStderr();
+
       expect(outputStdout().length, equals(1));
       expect(outputStdout().first, isEmpty);
       expect(lines.length, equals(6));
@@ -448,34 +483,42 @@
       expect(lines[3], equals('         8901234567890123456789012345678'));
       expect(lines[4], equals('         901234567890123456789'));
       expect(lines[5], isEmpty);
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
-    testUsingContext('Stdout logs are wrapped', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Stdout logs are wrapped', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printStatus('0123456789' * 15);
       final List<String> lines = outputStdout();
+
       expect(outputStderr().length, equals(1));
       expect(outputStderr().first, isEmpty);
       expect(lines[0], equals('0123456789' * 4));
       expect(lines[1], equals('0123456789' * 4));
       expect(lines[2], equals('0123456789' * 4));
       expect(lines[3], equals('0123456789' * 3));
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
-    testUsingContext('Stdout logs are wrapped and can be indented.', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Stdout logs are wrapped and can be indented.', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printStatus('0123456789' * 15, indent: 5);
       final List<String> lines = outputStdout();
+
       expect(outputStderr().length, equals(1));
       expect(outputStderr().first, isEmpty);
       expect(lines.length, equals(6));
@@ -485,17 +528,21 @@
       expect(lines[3], equals('     56789012345678901234567890123456789'));
       expect(lines[4], equals('     0123456789'));
       expect(lines[5], isEmpty);
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
-    testUsingContext('Stdout logs are wrapped and can have hanging indent.', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Stdout logs are wrapped and can have hanging indent.', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printStatus('0123456789' * 15, hangingIndent: 5);
       final List<String> lines = outputStdout();
+
       expect(outputStderr().length, equals(1));
       expect(outputStderr().first, isEmpty);
       expect(lines.length, equals(6));
@@ -505,17 +552,21 @@
       expect(lines[3], equals('     01234567890123456789012345678901234'));
       expect(lines[4], equals('     56789'));
       expect(lines[5], isEmpty);
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
     testUsingContext('Stdout logs are wrapped, indented, and can have hanging indent.', () async {
-      final Logger logger = context.get<Logger>();
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printStatus('0123456789' * 15, indent: 4, hangingIndent: 5);
       final List<String> lines = outputStdout();
+
       expect(outputStderr().length, equals(1));
       expect(outputStderr().first, isEmpty);
       expect(lines.length, equals(6));
@@ -525,42 +576,78 @@
       expect(lines[3], equals('         8901234567890123456789012345678'));
       expect(lines[4], equals('         901234567890123456789'));
       expect(lines[5], isEmpty);
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
-    testUsingContext('Error logs are red', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Error logs are red', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: FakePlatform()..stdoutSupportsAnsi = true,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(showColor: true),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printError('Pants on fire!');
       final List<String> lines = outputStderr();
+
       expect(outputStdout().length, equals(1));
       expect(outputStdout().first, isEmpty);
       expect(lines[0], equals('${AnsiTerminal.red}Pants on fire!${AnsiTerminal.resetColor}'));
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
-      Stdio: () => mockStdio,
     });
 
-    testUsingContext('Stdout logs are not colored', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Stdout logs are not colored', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: FakePlatform(),
+        ),
+        stdio: mockStdio,
+        outputPreferences:  OutputPreferences.test(showColor: true),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printStatus('All good.');
+
       final List<String> lines = outputStdout();
       expect(outputStderr().length, equals(1));
       expect(outputStderr().first, isEmpty);
       expect(lines[0], equals('All good.'));
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Stdio: () => mockStdio,
     });
 
-    testUsingContext('Stdout printStatus handle null inputs on colored terminal', () async {
-      final Logger logger = context.get<Logger>();
+    testWithoutContext('Stdout printStatus handle null inputs on colored terminal', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: FakePlatform(),
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(showColor: true),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
+      logger.printStatus(
+        null,
+        emphasis: null,
+        color: null,
+        newline: null,
+        indent: null,
+      );
+      final List<String> lines = outputStdout();
+
+      expect(outputStderr().length, equals(1));
+      expect(outputStderr().first, isEmpty);
+      expect(lines[0], equals(''));
+    });
+
+    testWithoutContext('Stdout printStatus handle null inputs on non-color terminal', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
       logger.printStatus(
         null,
         emphasis: null,
@@ -572,40 +659,25 @@
       expect(outputStderr().length, equals(1));
       expect(outputStderr().first, isEmpty);
       expect(lines[0], equals(''));
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Stdio: () => mockStdio,
     });
 
-    testUsingContext('Stdout printStatus handle null inputs on non-color terminal', () async {
-      final Logger logger = context.get<Logger>();
-      logger.printStatus(
-        null,
-        emphasis: null,
-        color: null,
-        newline: null,
-        indent: null,
-      );
-      final List<String> lines = outputStdout();
-      expect(outputStderr().length, equals(1));
-      expect(outputStderr().first, isEmpty);
-      expect(lines[0], equals(''));
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
-    });
-
-    testUsingContext('Stdout startProgress on non-color terminal', () async {
+    // Status uses Stopwatch from context.
+    test('Stdout startProgress on non-color terminal', () async {
       bool done = false;
       FakeAsync().run((FakeAsync time) {
-        final Logger logger = context.get<Logger>();
+        final Logger logger = StdoutLogger(
+          terminal: AnsiTerminal(
+            stdio: mockStdio,
+            platform: _kNoAnsiPlatform,
+          ),
+          stdio: mockStdio,
+          outputPreferences: OutputPreferences.test(showColor: false),
+          timeoutConfiguration: const TimeoutConfiguration(),
+        );
         final Status status = logger.startProgress(
           'Hello',
           progressId: null,
-          timeout: timeoutConfiguration.slowOperation,
+          timeout: const TimeoutConfiguration().slowOperation,
           progressIndicatorPadding: 20, // this minus the "Hello" equals the 15 below.
         );
         expect(outputStderr().length, equals(1));
@@ -619,11 +691,6 @@
         done = true;
       });
       expect(done, isTrue);
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
     testUsingContext('SummaryStatus works when canceled', () async {
@@ -646,7 +713,9 @@
       // Verify that stopping or canceling multiple times throws.
       expect(() { summaryStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
       expect(() { summaryStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
-    }, overrides: <Type, Generator>{Stdio: () => mockStdio, Platform: _kNoAnsiPlatform});
+    }, overrides: <Type, Generator>{
+      Platform: () => _kNoAnsiPlatform,
+    });
 
     testUsingContext('SummaryStatus works when stopped', () async {
       summaryStatus.start();
@@ -669,30 +738,53 @@
       // Verify that stopping or canceling multiple times throws.
       expect(() { summaryStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
       expect(() { summaryStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
-    }, overrides: <Type, Generator>{Stdio: () => mockStdio, Platform: _kNoAnsiPlatform});
+    }, overrides: <Type, Generator>{
+      Platform: () => _kNoAnsiPlatform,
+    });
 
-    testUsingContext('sequential startProgress calls with StdoutLogger', () async {
-      final Logger logger = context.get<Logger>();
-      logger.startProgress('AAA', timeout: timeoutConfiguration.fastOperation)..stop();
-      logger.startProgress('BBB', timeout: timeoutConfiguration.fastOperation)..stop();
+    // Status still uses Stopwatch from context.
+    // TODO(jonahwilliams): switch to testWithoutContext once logger uses a
+    // TimerFactory.
+    test('sequential startProgress calls with StdoutLogger', () async {
+      final Logger logger = StdoutLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        stdio: mockStdio,
+        outputPreferences: OutputPreferences.test(showColor: false),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      );
+      logger.startProgress('AAA', timeout: const TimeoutConfiguration().fastOperation)..stop();
+      logger.startProgress('BBB', timeout: const TimeoutConfiguration().fastOperation)..stop();
       final List<String> output = outputStdout();
+
       expect(output.length, equals(3));
+
       // There's 61 spaces at the start: 59 (padding default) - 3 (length of AAA) + 5 (margin).
       // Then there's a left-padded "0ms" 8 characters wide, so 5 spaces then "0ms"
       // (except sometimes it's randomly slow so we handle up to "99,999ms").
       expect(output[0], matches(RegExp(r'AAA[ ]{61}[\d, ]{5}[\d]ms')));
       expect(output[1], matches(RegExp(r'BBB[ ]{61}[\d, ]{5}[\d]ms')));
-    }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      OutputPreferences: () => OutputPreferences(showColor: false),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
-    testUsingContext('sequential startProgress calls with VerboseLogger and StdoutLogger', () async {
-      final Logger logger = context.get<Logger>();
-      logger.startProgress('AAA', timeout: timeoutConfiguration.fastOperation)..stop();
-      logger.startProgress('BBB', timeout: timeoutConfiguration.fastOperation)..stop();
+    // Status still uses Stopwatch from context.
+    test('sequential startProgress calls with VerboseLogger and StdoutLogger', () async {
+      final Logger logger = VerboseLogger(
+        StdoutLogger(
+          terminal: AnsiTerminal(
+            stdio: mockStdio,
+            platform: _kNoAnsiPlatform,
+          ),
+          stdio: mockStdio,
+          outputPreferences: OutputPreferences.test(),
+          timeoutConfiguration: const TimeoutConfiguration(),
+        ),
+        stopwatch: FakeStopwatch(),
+      );
+      logger.startProgress('AAA', timeout: const TimeoutConfiguration().fastOperation)..stop();
+      logger.startProgress('BBB', timeout: const TimeoutConfiguration().fastOperation)..stop();
+
       expect(outputStdout(), <Matcher>[
         matches(r'^\[ (?: {0,2}\+[0-9]{1,4} ms|       )\] AAA$'),
         matches(r'^\[ (?: {0,2}\+[0-9]{1,4} ms|       )\] AAA \(completed.*\)$'),
@@ -700,19 +792,21 @@
         matches(r'^\[ (?: {0,2}\+[0-9]{1,4} ms|       )\] BBB \(completed.*\)$'),
         matches(r'^$'),
       ]);
-    }, overrides: <Type, Generator>{
-      Logger: () => VerboseLogger(StdoutLogger()),
-      Stdio: () => mockStdio,
-      Platform: _kNoAnsiPlatform,
     });
 
-    testUsingContext('sequential startProgress calls with BufferLogger', () async {
-      testLogger.startProgress('AAA', timeout: timeoutConfiguration.fastOperation)..stop();
-      testLogger.startProgress('BBB', timeout: timeoutConfiguration.fastOperation)..stop();
-      expect(testLogger.statusText, 'AAA\nBBB\n');
-    }, overrides: <Type, Generator>{
-      Logger: () => BufferLogger(),
-      Platform: _kNoAnsiPlatform,
+    // Status still uses Stopwatch from context.
+    test('sequential startProgress calls with BufferLogger', () async {
+      final BufferLogger logger = BufferLogger(
+        terminal: AnsiTerminal(
+          stdio: mockStdio,
+          platform: _kNoAnsiPlatform,
+        ),
+        outputPreferences: OutputPreferences.test(),
+      );
+      logger.startProgress('AAA', timeout: const TimeoutConfiguration().fastOperation)..stop();
+      logger.startProgress('BBB', timeout: const TimeoutConfiguration().fastOperation)..stop();
+
+      expect(logger.statusText, 'AAA\nBBB\n');
     });
   });
 }
diff --git a/packages/flutter_tools/test/general.shard/base/process_test.dart b/packages/flutter_tools/test/general.shard/base/process_test.dart
index bc4383c..668310e 100644
--- a/packages/flutter_tools/test/general.shard/base/process_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/process_test.dart
@@ -6,7 +6,6 @@
 
 import 'package:platform/platform.dart';
 import 'package:flutter_tools/src/base/io.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/process.dart';
 import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:mockito/mockito.dart';
@@ -69,11 +68,9 @@
 
   group('output formatting', () {
     MockProcessManager mockProcessManager;
-    BufferLogger mockLogger;
 
     setUp(() {
       mockProcessManager = MockProcessManager();
-      mockLogger = BufferLogger();
     });
 
     MockProcess Function(List<String>) processMetaFactory(List<String> stdout, { List<String> stderr = const <String>[] }) {
@@ -88,10 +85,10 @@
       final List<String> testString = <String>['0123456789' * 10];
       mockProcessManager.processFactory = processMetaFactory(testString, stderr: testString);
       await processUtils.stream(<String>['command']);
-      expect(mockLogger.statusText, equals('${testString[0]}\n'));
-      expect(mockLogger.errorText, equals('${testString[0]}\n'));
+
+      expect(testLogger.statusText, equals('${testString[0]}\n'));
+      expect(testLogger.errorText, equals('${testString[0]}\n'));
     }, overrides: <Type, Generator>{
-      Logger: () => mockLogger,
       ProcessManager: () => mockProcessManager,
       OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40),
       Platform: () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false,
diff --git a/packages/flutter_tools/test/general.shard/base/terminal_test.dart b/packages/flutter_tools/test/general.shard/base/terminal_test.dart
index ff62a6f..78bb1d4 100644
--- a/packages/flutter_tools/test/general.shard/base/terminal_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/terminal_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:flutter_tools/src/base/logger.dart';
 import 'package:platform/platform.dart';
 import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/terminal.dart';
@@ -11,24 +12,28 @@
 import 'package:mockito/mockito.dart';
 
 import '../../src/common.dart';
-import '../../src/context.dart';
 
 void main() {
   group('output preferences', () {
-    testUsingContext('can wrap output', () async {
-      globals.printStatus('0123456789' * 8);
-      expect(testLogger.statusText, equals(('0123456789' * 4 + '\n') * 2));
-    }, overrides: <Type, Generator>{
-      OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40),
+    testWithoutContext('can wrap output', () async {
+      final BufferLogger bufferLogger = BufferLogger(
+        outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40),
+        terminal: TestTerminal(platform: FakePlatform()..stdoutSupportsAnsi = true),
+      );
+      bufferLogger.printStatus('0123456789' * 8);
+
+      expect(bufferLogger.statusText, equals(('0123456789' * 4 + '\n') * 2));
     });
 
-    testUsingContext('can turn off wrapping', () async {
+    testWithoutContext('can turn off wrapping', () async {
+      final BufferLogger bufferLogger = BufferLogger(
+        outputPreferences: OutputPreferences.test(wrapText: false),
+        terminal: TestTerminal(platform: FakePlatform()..stdoutSupportsAnsi = true),
+      );
       final String testString = '0123456789' * 20;
-      globals.printStatus(testString);
-      expect(testLogger.statusText, equals('$testString\n'));
-    }, overrides: <Type, Generator>{
-      Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
-      OutputPreferences: () => OutputPreferences(wrapText: false),
+      bufferLogger.printStatus(testString);
+
+      expect(bufferLogger.statusText, equals('$testString\n'));
     });
   });
 
@@ -36,32 +41,29 @@
     AnsiTerminal terminal;
 
     setUp(() {
-      terminal = AnsiTerminal();
+      terminal = AnsiTerminal(
+        stdio: globals.stdio, // Danger, using real stdio.
+        platform: FakePlatform()..stdoutSupportsAnsi = true,
+      );
     });
 
-    testUsingContext('adding colors works', () {
+    testWithoutContext('adding colors works', () {
       for (final TerminalColor color in TerminalColor.values) {
         expect(
           terminal.color('output', color),
           equals('${AnsiTerminal.colorCode(color)}output${AnsiTerminal.resetColor}'),
         );
       }
-    }, overrides: <Type, Generator>{
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
     });
 
-    testUsingContext('adding bold works', () {
+    testWithoutContext('adding bold works', () {
       expect(
         terminal.bolden('output'),
         equals('${AnsiTerminal.bold}output${AnsiTerminal.resetBold}'),
       );
-    }, overrides: <Type, Generator>{
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
     });
 
-    testUsingContext('nesting bold within color works', () {
+    testWithoutContext('nesting bold within color works', () {
       expect(
         terminal.color(terminal.bolden('output'), TerminalColor.blue),
         equals('${AnsiTerminal.blue}${AnsiTerminal.bold}output${AnsiTerminal.resetBold}${AnsiTerminal.resetColor}'),
@@ -70,12 +72,9 @@
         terminal.color('non-bold ${terminal.bolden('output')} also non-bold', TerminalColor.blue),
         equals('${AnsiTerminal.blue}non-bold ${AnsiTerminal.bold}output${AnsiTerminal.resetBold} also non-bold${AnsiTerminal.resetColor}'),
       );
-    }, overrides: <Type, Generator>{
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
     });
 
-    testUsingContext('nesting color within bold works', () {
+    testWithoutContext('nesting color within bold works', () {
       expect(
         terminal.bolden(terminal.color('output', TerminalColor.blue)),
         equals('${AnsiTerminal.bold}${AnsiTerminal.blue}output${AnsiTerminal.resetColor}${AnsiTerminal.resetBold}'),
@@ -84,12 +83,9 @@
         terminal.bolden('non-color ${terminal.color('output', TerminalColor.blue)} also non-color'),
         equals('${AnsiTerminal.bold}non-color ${AnsiTerminal.blue}output${AnsiTerminal.resetColor} also non-color${AnsiTerminal.resetBold}'),
       );
-    }, overrides: <Type, Generator>{
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
     });
 
-    testUsingContext('nesting color within color works', () {
+    testWithoutContext('nesting color within color works', () {
       expect(
         terminal.color(terminal.color('output', TerminalColor.blue), TerminalColor.magenta),
         equals('${AnsiTerminal.magenta}${AnsiTerminal.blue}output${AnsiTerminal.resetColor}${AnsiTerminal.magenta}${AnsiTerminal.resetColor}'),
@@ -98,12 +94,9 @@
         terminal.color('magenta ${terminal.color('output', TerminalColor.blue)} also magenta', TerminalColor.magenta),
         equals('${AnsiTerminal.magenta}magenta ${AnsiTerminal.blue}output${AnsiTerminal.resetColor}${AnsiTerminal.magenta} also magenta${AnsiTerminal.resetColor}'),
       );
-    }, overrides: <Type, Generator>{
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
     });
 
-    testUsingContext('nesting bold within bold works', () {
+    testWithoutContext('nesting bold within bold works', () {
       expect(
         terminal.bolden(terminal.bolden('output')),
         equals('${AnsiTerminal.bold}output${AnsiTerminal.resetBold}'),
@@ -112,9 +105,6 @@
         terminal.bolden('bold ${terminal.bolden('output')} still bold'),
         equals('${AnsiTerminal.bold}bold output still bold${AnsiTerminal.resetBold}'),
       );
-    }, overrides: <Type, Generator>{
-      OutputPreferences: () => OutputPreferences(showColor: true),
-      Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
     });
   });
 
@@ -122,17 +112,22 @@
     AnsiTerminal terminalUnderTest;
 
     setUp(() {
-      terminalUnderTest = TestTerminal();
+      terminalUnderTest = TestTerminal(stdio: MockStdio());
     });
 
-    testUsingContext('character prompt throws if usesTerminalUi is false', () async {
+    testWithoutContext('character prompt throws if usesTerminalUi is false', () async {
       expect(terminalUnderTest.promptForCharInput(
         <String>['a', 'b', 'c'],
         prompt: 'Please choose something',
+        logger: null,
       ), throwsA(isInstanceOf<StateError>()));
     });
 
-    testUsingContext('character prompt', () async {
+    testWithoutContext('character prompt', () async {
+      final BufferLogger bufferLogger = BufferLogger(
+        terminal: terminalUnderTest,
+        outputPreferences: OutputPreferences.test(),
+      );
       terminalUnderTest.usesTerminalUi = true;
       mockStdInStream = Stream<String>.fromFutures(<Future<String>>[
         Future<String>.value('d'), // Not in accepted list.
@@ -142,17 +137,22 @@
       final String choice = await terminalUnderTest.promptForCharInput(
         <String>['a', 'b', 'c'],
         prompt: 'Please choose something',
+        logger: bufferLogger,
       );
       expect(choice, 'b');
       expect(
-          testLogger.statusText,
+          bufferLogger.statusText,
           'Please choose something [a|b|c]: d\n'
           'Please choose something [a|b|c]: \n'
           '\n'
           'Please choose something [a|b|c]: b\n');
     });
 
-    testUsingContext('default character choice without displayAcceptedCharacters', () async {
+    testWithoutContext('default character choice without displayAcceptedCharacters', () async {
+      final BufferLogger bufferLogger = BufferLogger(
+        terminal: terminalUnderTest,
+        outputPreferences: OutputPreferences.test(),
+      );
       terminalUnderTest.usesTerminalUi = true;
       mockStdInStream = Stream<String>.fromFutures(<Future<String>>[
         Future<String>.value('\n'), // Not in accepted list
@@ -162,21 +162,26 @@
         prompt: 'Please choose something',
         displayAcceptedCharacters: false,
         defaultChoiceIndex: 1, // which is b.
+        logger: bufferLogger,
       );
+
       expect(choice, 'b');
       expect(
-          testLogger.statusText,
+          bufferLogger.statusText,
           'Please choose something: \n'
           '\n');
     });
 
-    testUsingContext('Does not set single char mode when a terminal is not attached', () {
+    testWithoutContext('Does not set single char mode when a terminal is not attached', () {
+      final Stdio stdio = MockStdio();
       when(stdio.stdin).thenThrow(StateError('This should not be called'));
       when(stdio.stdinHasTerminal).thenReturn(false);
+      final AnsiTerminal ansiTerminal = AnsiTerminal(
+        stdio: stdio,
+        platform: const LocalPlatform()
+      );
 
-      globals.terminal.singleCharMode = true;
-    }, overrides: <Type, Generator>{
-      Stdio: () => MockStdio(),
+      expect(() => ansiTerminal.singleCharMode = true, returnsNormally);
     });
   });
 }
@@ -184,10 +189,17 @@
 Stream<String> mockStdInStream;
 
 class TestTerminal extends AnsiTerminal {
+  TestTerminal({
+    Stdio stdio,
+    Platform platform = const LocalPlatform(),
+  }) : super(stdio: stdio, platform: platform);
+
   @override
   Stream<String> get keystrokes {
     return mockStdInStream;
   }
+
+  bool singleCharMode = false;
 }
 
 class MockStdio extends Mock implements Stdio {}
diff --git a/packages/flutter_tools/test/general.shard/cold_test.dart b/packages/flutter_tools/test/general.shard/cold_test.dart
index 28df639..25ddc49 100644
--- a/packages/flutter_tools/test/general.shard/cold_test.dart
+++ b/packages/flutter_tools/test/general.shard/cold_test.dart
@@ -7,6 +7,7 @@
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/compile.dart';
 import 'package:flutter_tools/src/device.dart';
@@ -15,6 +16,7 @@
 import 'package:flutter_tools/src/vmservice.dart';
 import 'package:meta/meta.dart';
 import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
 
 import '../src/common.dart';
 import '../src/context.dart';
@@ -26,7 +28,13 @@
     BufferLogger mockLogger;
 
     setUp(() {
-      mockLogger = BufferLogger();
+      mockLogger = BufferLogger(
+        terminal: AnsiTerminal(
+          stdio: null,
+          platform: const LocalPlatform(),
+        ),
+        outputPreferences: OutputPreferences.test(),
+    );
       residentCompiler = MockResidentCompiler();
     });
 
@@ -114,23 +122,16 @@
   });
 
   group('cold run', () {
-    BufferLogger mockLogger;
-
-    setUp(() {
-      mockLogger = BufferLogger();
-    });
-
     testUsingContext('returns 1 if not prebuilt mode & mainPath does not exist', () async {
       final MockDevice mockDevice = MockDevice();
       final MockFlutterDevice mockFlutterDevice = MockFlutterDevice();
       when(mockFlutterDevice.device).thenReturn(mockDevice);
       final List<FlutterDevice> devices = <FlutterDevice>[mockFlutterDevice];
       final int result = await ColdRunner(devices).run();
+
       expect(result, 1);
-      expect(mockLogger.errorText, matches(r'Tried to run .*, but that file does not exist\.'));
-      expect(mockLogger.errorText, matches(r'Consider using the -t option to specify the Dart file to start\.'));
-    }, overrides: <Type, Generator>{
-      Logger: () => mockLogger,
+      expect(testLogger.errorText, matches(r'Tried to run .*, but that file does not exist\.'));
+      expect(testLogger.errorText, matches(r'Consider using the -t option to specify the Dart file to start\.'));
     });
 
     testUsingContext('calls runCold on attached device', () async {
@@ -148,13 +149,12 @@
         applicationBinary: applicationBinary,
         debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
       ).run();
+
       expect(result, 1);
       verify(mockFlutterDevice.runCold(
           coldRunner: anyNamed('coldRunner'),
           route: anyNamed('route'),
       ));
-    }, overrides: <Type, Generator>{
-      Logger: () => mockLogger,
     });
   });
 }
diff --git a/packages/flutter_tools/test/general.shard/commands/build_aot_test.dart b/packages/flutter_tools/test/general.shard/commands/build_aot_test.dart
index 23e056f..de6b58c 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_aot_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_aot_test.dart
@@ -4,7 +4,6 @@
 
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/artifacts.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/process.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
@@ -22,14 +21,12 @@
   MockXcode mockXcode;
   MemoryFileSystem memoryFileSystem;
   MockProcessManager mockProcessManager;
-  BufferLogger bufferLogger;
   MockPlistUtils mockPlistUtils;
 
   setUp(() {
     mockXcode = MockXcode();
     memoryFileSystem = MemoryFileSystem(style: FileSystemStyle.posix);
     mockProcessManager = MockProcessManager();
-    bufferLogger = BufferLogger();
     mockPlistUtils = MockPlistUtils();
   });
 
@@ -67,7 +64,6 @@
     FileSystem: () => memoryFileSystem,
     ProcessManager: () => mockProcessManager,
     Xcode: () => mockXcode,
-    Logger: () => bufferLogger,
     PlistParser: () => mockPlistUtils,
   });
 
@@ -91,7 +87,6 @@
     FileSystem: () => memoryFileSystem,
     ProcessManager: () => mockProcessManager,
     Xcode: () => mockXcode,
-    Logger: () => bufferLogger,
     PlistParser: () => mockPlistUtils,
   });
 
@@ -114,7 +109,6 @@
     FileSystem: () => memoryFileSystem,
     ProcessManager: () => mockProcessManager,
     Xcode: () => mockXcode,
-    Logger: () => bufferLogger,
     PlistParser: () => mockPlistUtils,
   });
 
@@ -143,7 +137,6 @@
     FileSystem: () => memoryFileSystem,
     ProcessManager: () => mockProcessManager,
     Xcode: () => mockXcode,
-    Logger: () => bufferLogger,
     PlistParser: () => mockPlistUtils,
   });
 
@@ -162,13 +155,12 @@
 
     await validateBitcode(BuildMode.release, TargetPlatform.ios);
 
-    expect(bufferLogger.statusText, '');
+    expect(testLogger.statusText, '');
   }, overrides: <Type, Generator>{
     Artifacts: () => LocalEngineArtifacts('/engine', 'ios_profile', 'host_profile'),
     FileSystem: () => memoryFileSystem,
     ProcessManager: () => mockProcessManager,
     Xcode: () => mockXcode,
-    Logger: () => bufferLogger,
     PlistParser: () => mockPlistUtils,
   });
 
@@ -187,13 +179,12 @@
 
     await validateBitcode(BuildMode.release, TargetPlatform.ios);
 
-    expect(bufferLogger.statusText, '');
+    expect(testLogger.statusText, '');
   }, overrides: <Type, Generator>{
     Artifacts: () => LocalEngineArtifacts('/engine', 'ios_profile', 'host_profile'),
     FileSystem: () => memoryFileSystem,
     ProcessManager: () => mockProcessManager,
     Xcode: () => mockXcode,
-    Logger: () => bufferLogger,
     PlistParser: () => mockPlistUtils,
   });
 }
diff --git a/packages/flutter_tools/test/general.shard/compile_expression_test.dart b/packages/flutter_tools/test/general.shard/compile_expression_test.dart
index eaa7f25..9f28534 100644
--- a/packages/flutter_tools/test/general.shard/compile_expression_test.dart
+++ b/packages/flutter_tools/test/general.shard/compile_expression_test.dart
@@ -6,7 +6,6 @@
 
 import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/io.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/compile.dart';
@@ -103,7 +102,6 @@
   }, overrides: <Type, Generator>{
     ProcessManager: () => mockProcessManager,
     OutputPreferences: () => OutputPreferences(showColor: false),
-    Logger: () => BufferLogger(),
     Platform: kNoColorTerminalPlatform,
   });
 
diff --git a/packages/flutter_tools/test/general.shard/config_test.dart b/packages/flutter_tools/test/general.shard/config_test.dart
index 49d7ac7..5a437be 100644
--- a/packages/flutter_tools/test/general.shard/config_test.dart
+++ b/packages/flutter_tools/test/general.shard/config_test.dart
@@ -6,6 +6,8 @@
 import 'package:flutter_tools/src/base/config.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
+import 'package:platform/platform.dart';
 
 import '../src/common.dart';
 
@@ -49,7 +51,13 @@
   });
 
   test('Config parse error', () {
-    final BufferLogger bufferLogger =BufferLogger();
+    final BufferLogger bufferLogger = BufferLogger(
+      terminal: AnsiTerminal(
+        stdio: null,
+        platform: const LocalPlatform(),
+      ),
+      outputPreferences: OutputPreferences.test(),
+    );
     final File file = memoryFileSystem.file('example')
       ..writeAsStringSync('{"hello":"bar');
     config = Config(file, bufferLogger);
diff --git a/packages/flutter_tools/test/general.shard/hot_test.dart b/packages/flutter_tools/test/general.shard/hot_test.dart
index 47ac6c1..b12ef62 100644
--- a/packages/flutter_tools/test/general.shard/hot_test.dart
+++ b/packages/flutter_tools/test/general.shard/hot_test.dart
@@ -6,7 +6,6 @@
 
 import 'package:flutter_tools/src/artifacts.dart';
 import 'package:flutter_tools/src/base/io.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/compile.dart';
 import 'package:flutter_tools/src/devfs.dart';
@@ -269,12 +268,10 @@
 
   group('hot attach', () {
     MockResidentCompiler residentCompiler = MockResidentCompiler();
-    BufferLogger mockLogger;
     MockLocalEngineArtifacts mockArtifacts;
 
     setUp(() {
       residentCompiler = MockResidentCompiler();
-      mockLogger = BufferLogger();
       mockArtifacts = MockLocalEngineArtifacts();
     });
 
@@ -298,13 +295,12 @@
         debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
       ).attach();
       expect(exitCode, 2);
-      expect(mockLogger.statusText, contains('If you are using an emulator running Android Q Beta, '
+      expect(testLogger.statusText, contains('If you are using an emulator running Android Q Beta, '
           'consider using an emulator running API level 29 or lower.'));
-      expect(mockLogger.statusText, contains('Learn more about the status of this issue on '
+      expect(testLogger.statusText, contains('Learn more about the status of this issue on '
           'https://issuetracker.google.com/issues/132325318'));
     }, overrides: <Type, Generator>{
       Artifacts: () => mockArtifacts,
-      Logger: () => mockLogger,
       HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
     });
 
@@ -327,13 +323,12 @@
         debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
       ).attach();
       expect(exitCode, 2);
-      expect(mockLogger.statusText, contains('If you are using an emulator running Android Q Beta, '
+      expect(testLogger.statusText, contains('If you are using an emulator running Android Q Beta, '
           'consider using an emulator running API level 29 or lower.'));
-      expect(mockLogger.statusText, contains('Learn more about the status of this issue on '
+      expect(testLogger.statusText, contains('Learn more about the status of this issue on '
           'https://issuetracker.google.com/issues/132325318'));
     }, overrides: <Type, Generator>{
       Artifacts: () => mockArtifacts,
-      Logger: () => mockLogger,
       HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
     });
   });
diff --git a/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart b/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart
index 973bfa1..7fc9fd0 100644
--- a/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart
@@ -654,6 +654,8 @@
 Stream<String> mockTerminalStdInStream;
 
 class TestTerminal extends AnsiTerminal {
+  TestTerminal() : super(stdio: globals.stdio, platform: globals.platform);
+
   @override
   String bolden(String message) => '<bold>$message</bold>';
 
diff --git a/packages/flutter_tools/test/general.shard/project_file_invalidator_test.dart b/packages/flutter_tools/test/general.shard/project_file_invalidator_test.dart
index d46e5629..70d50b80 100644
--- a/packages/flutter_tools/test/general.shard/project_file_invalidator_test.dart
+++ b/packages/flutter_tools/test/general.shard/project_file_invalidator_test.dart
@@ -4,22 +4,36 @@
 
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/run_hot.dart';
 
 import 'package:platform/platform.dart';
 
 import '../src/common.dart';
+import '../src/mocks.dart';
 
 // assumption: tests have a timeout less than 100 days
 final DateTime inFuture = DateTime.now().add(const Duration(days: 100));
 
 void main() {
+  BufferLogger bufferLogger;
+
+  setUp(() {
+    bufferLogger = BufferLogger(
+      terminal: AnsiTerminal(
+        stdio: MockStdio(),
+        platform: FakePlatform(),
+      ),
+      outputPreferences: OutputPreferences.test(),
+    );
+  });
+
   for (final bool asyncScanning in <bool>[true, false]) {
     testWithoutContext('No last compile, asyncScanning: $asyncScanning', () async {
       final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator(
         fileSystem: MemoryFileSystem(),
         platform: FakePlatform(),
-        logger: BufferLogger(),
+        logger: bufferLogger,
       );
 
       expect(
@@ -37,7 +51,7 @@
       final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator(
         fileSystem: MemoryFileSystem(),
         platform: FakePlatform(),
-        logger: BufferLogger(),
+        logger: bufferLogger,
       );
 
       expect(
@@ -55,7 +69,7 @@
       final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator(
         fileSystem: MemoryFileSystem(),
         platform: FakePlatform(),
-        logger: BufferLogger(),
+        logger: bufferLogger,
       );
 
       expect(
diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart
index 854c39f..0c15839 100644
--- a/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart
@@ -7,6 +7,7 @@
 import 'package:dwds/dwds.dart';
 import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/base/net.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/device.dart';
@@ -17,6 +18,7 @@
 import 'package:flutter_tools/src/build_runner/web_fs.dart';
 import 'package:meta/meta.dart';
 import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
 import 'package:vm_service/vm_service.dart';
 
 import '../src/common.dart';
@@ -84,7 +86,13 @@
     expect(debugConnectionInfo.wsUri, null);
     verify(mockStatus.stop()).called(1);
   }, overrides: <Type, Generator>{
-    Logger: () => DelegateLogger(BufferLogger()),
+    Logger: () => DelegateLogger(BufferLogger(
+      terminal: AnsiTerminal(
+        stdio: null,
+        platform: const LocalPlatform(),
+      ),
+      outputPreferences: OutputPreferences.test(),
+    )),
   }));
 
   test('Can full restart after attaching', () => testbed.run(() async {
diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
index da322c4..96698bf 100644
--- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
@@ -11,6 +11,7 @@
 import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/net.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/build_runner/resident_web_runner.dart';
 import 'package:flutter_tools/src/build_runner/web_fs.dart';
@@ -26,6 +27,7 @@
 import 'package:flutter_tools/src/web/web_device.dart';
 import 'package:meta/meta.dart';
 import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
 import 'package:vm_service/vm_service.dart';
 import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
 
@@ -257,7 +259,13 @@
     expect(bufferLogger.statusText, contains('Debug service listening on ws://127.0.0.1/abcd/'));
     expect(debugConnectionInfo.wsUri.toString(), 'ws://127.0.0.1/abcd/');
   }, overrides: <Type, Generator>{
-    Logger: () => DelegateLogger(BufferLogger()),
+    Logger: () => DelegateLogger(BufferLogger(
+      terminal: AnsiTerminal(
+        stdio: null,
+        platform: const LocalPlatform(),
+      ),
+      outputPreferences: OutputPreferences.test(),
+    )),
   }));
 
   test('Can successfully run and disconnect with --no-resident', () => testbed.run(() async {
@@ -1080,7 +1088,13 @@
     await expectation;
     verify(mockStatus.stop()).called(2);
   }, overrides: <Type, Generator>{
-    Logger: () => DelegateLogger(BufferLogger())
+    Logger: () => DelegateLogger(BufferLogger(
+      terminal: AnsiTerminal(
+        stdio: null,
+        platform: const LocalPlatform(),
+      ),
+      outputPreferences: OutputPreferences.test(),
+    ))
   }));
 }
 
diff --git a/packages/flutter_tools/test/general.shard/vmservice_test.dart b/packages/flutter_tools/test/general.shard/vmservice_test.dart
index 138ac83..5e4fb07 100644
--- a/packages/flutter_tools/test/general.shard/vmservice_test.dart
+++ b/packages/flutter_tools/test/general.shard/vmservice_test.dart
@@ -7,10 +7,12 @@
 
 import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/device.dart';
 import 'package:flutter_tools/src/vmservice.dart';
 import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
 import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
 import 'package:quiver/testing/async.dart';
 
 import '../src/common.dart';
@@ -167,6 +169,7 @@
 void main() {
   MockStdio mockStdio;
   group('VMService', () {
+
     setUp(() {
       mockStdio = MockStdio();
     });
@@ -188,8 +191,12 @@
         expect(mockStdio.writtenToStderr.join(''), '');
       });
     }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      Stdio: () => mockStdio,
+      Logger: () => StdoutLogger(
+        outputPreferences: OutputPreferences.test(),
+        stdio: mockStdio,
+        terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
+        timeoutConfiguration: const TimeoutConfiguration(),
+      ),
       WebSocketConnector: () => (String url, {CompressionOptions compression}) async => throw const SocketException('test'),
     });
 
@@ -263,8 +270,12 @@
         expect(mockStdio.writtenToStderr.join(''), '');
       });
     }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      Stdio: () => mockStdio,
+      Logger: () => StdoutLogger(
+        outputPreferences: outputPreferences,
+        terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
+        stdio: mockStdio,
+        timeoutConfiguration: const TimeoutConfiguration(),
+      ),
     });
 
     testUsingContext('registers hot UI method', () {
@@ -276,8 +287,12 @@
         expect(mockPeer.registeredMethods, contains('reloadMethod'));
       });
     }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      Stdio: () => mockStdio,
+      Logger: () => StdoutLogger(
+        outputPreferences: outputPreferences,
+        terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
+        stdio: mockStdio,
+        timeoutConfiguration: const TimeoutConfiguration(),
+      ),
     });
 
     testUsingContext('registers flutterMemoryInfo service', () {
@@ -290,8 +305,12 @@
         expect(mockPeer.registeredMethods, contains('flutterMemoryInfo'));
       });
     }, overrides: <Type, Generator>{
-      Logger: () => StdoutLogger(),
-      Stdio: () => mockStdio,
+      Logger: () => StdoutLogger(
+        outputPreferences: outputPreferences,
+        terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
+        stdio: mockStdio,
+        timeoutConfiguration: const TimeoutConfiguration(),
+      ),
     });
   });
 }
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index 8c29688..80d2d30 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -92,6 +92,7 @@
       return context.run<dynamic>(
         name: 'mocks',
         overrides: <Type, Generator>{
+          AnsiTerminal: () => AnsiTerminal(platform: globals.platform, stdio: globals.stdio),
           Config: () => buildConfig(globals.fs),
           DeviceManager: () => FakeDeviceManager(),
           Doctor: () => FakeDoctor(),
@@ -103,7 +104,10 @@
             return mock;
           },
           OutputPreferences: () => OutputPreferences.test(),
-          Logger: () => BufferLogger(),
+          Logger: () => BufferLogger(
+            terminal: globals.terminal,
+            outputPreferences: outputPreferences,
+          ),
           OperatingSystemUtils: () => FakeOperatingSystemUtils(),
           PersistentToolState: () => buildPersistentToolState(globals.fs),
           SimControl: () => MockSimControl(),
diff --git a/packages/flutter_tools/test/src/testbed.dart b/packages/flutter_tools/test/src/testbed.dart
index ab68e92..5934d28 100644
--- a/packages/flutter_tools/test/src/testbed.dart
+++ b/packages/flutter_tools/test/src/testbed.dart
@@ -38,7 +38,10 @@
   // Keeps tests fast by avoiding the actual file system.
   FileSystem: () => MemoryFileSystem(style: globals.platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix),
   ProcessManager: () => FakeProcessManager.any(),
-  Logger: () => BufferLogger(), // Allows reading logs and prevents stdout.
+  Logger: () => BufferLogger(
+    terminal: AnsiTerminal(stdio: globals.stdio, platform: globals.platform),  // Danger, using real stdio.
+    outputPreferences: OutputPreferences.test(),
+  ), // Allows reading logs and prevents stdout.
   OperatingSystemUtils: () => FakeOperatingSystemUtils(),
   OutputPreferences: () => OutputPreferences.test(), // configures BufferLogger to avoid color codes.
   Usage: () => NoOpUsage(), // prevent addition of analytics from burdening test mocks