startWithStdin method
Starts a process with stdin forwarding for interactive commands.
This method is specifically designed for interactive commands that require user input (like Flutter run with hot reload support).
Parameters:
workingDirectory: The working directory for the processprogressOut: Callback for stdout linesprogressErr: Callback for stderr linesshowLog: Whether to show log output
Implementation
Future<int> startWithStdin({
String? workingDirectory,
void Function(String line)? progressOut,
void Function(String line)? progressErr,
bool showLog = true,
bool singleCharacterMode = false,
}) async {
final commandCli = CommandlineConverter().convert(this);
if (commandCli.isEmpty) {
throw ArgumentError('Command cannot be empty');
}
final executable = commandCli.first;
final arguments = commandCli.sublist(1);
try {
final process = await Process.start(
executable,
arguments,
runInShell: true,
workingDirectory: workingDirectory,
environment: Platform.environment,
);
// Forward stdout
process.stdout.transform(utf8.decoder).listen((line) {
final cleanLine = line.replaceAll(RegExp(r'[\s\n]+$'), '');
progressOut?.call(cleanLine);
if (showLog) printMessage(cleanLine);
});
// Forward stderr
process.stderr.transform(utf8.decoder).listen((line) {
final cleanLine = line.replaceAll(RegExp(r'[\s\n]+$'), '');
progressErr?.call(cleanLine);
if (showLog) printerrMessage(cleanLine);
});
// Enable single character mode for stdin
if (singleCharacterMode) {
stdin.lineMode = false;
stdin.echoMode = false;
}
// Forward stdin - this is the key part for interactive features
// Use a more robust approach for stdin forwarding
final subscription = stdin.listen(
(data) {
try {
process.stdin.add(data);
} catch (e) {
// Ignore errors when writing to stdin (process might have exited)
}
},
onError: (error) {
// Handle stdin errors
},
onDone: () {
// Close stdin when done
process.stdin.close();
},
);
// Handle process exit
final exitCode = await process.exitCode;
// Cancel stdin subscription
await subscription.cancel();
if (exitCode > 0) {
throw Exception('Command "$this" exited with code $exitCode');
}
return exitCode;
} catch (e) {
if (e is ProcessException) {
throw Exception('Failed to start command "$this": ${e.message}');
}
rethrow;
}
}