Add a new flutter cli command, running-apps, using mDNS app discovery#180098
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a significant feature: mDNS-based discovery and advertisement of running Flutter applications. It adds a new running-apps command to display these apps, enhances network utilities, and integrates mDNS advertisement into the resident runners for both mobile/desktop and web platforms. The changes are well-structured and include comprehensive tests for the new functionality. My review includes a few suggestions for improving type safety and performance in the new code.
| apps.sort((Map<String, dynamic> a, Map<String, dynamic> b) { | ||
| final int? epochA = int.tryParse(a['epoch'] as String? ?? ''); | ||
| final int? epochB = int.tryParse(b['epoch'] as String? ?? ''); | ||
| if (epochA == null && epochB == null) { | ||
| return 0; | ||
| } | ||
| if (epochA == null) { | ||
| return 1; // Put unknown age last | ||
| } | ||
| if (epochB == null) { | ||
| return -1; // Put unknown age last | ||
| } | ||
| return epochB.compareTo(epochA); | ||
| }); |
There was a problem hiding this comment.
The apps list is of type List<Map<String, String>>. The sort callback should use Map<String, String> for its parameters to match this type. This improves type safety and allows removing the unnecessary cast.
| apps.sort((Map<String, dynamic> a, Map<String, dynamic> b) { | |
| final int? epochA = int.tryParse(a['epoch'] as String? ?? ''); | |
| final int? epochB = int.tryParse(b['epoch'] as String? ?? ''); | |
| if (epochA == null && epochB == null) { | |
| return 0; | |
| } | |
| if (epochA == null) { | |
| return 1; // Put unknown age last | |
| } | |
| if (epochB == null) { | |
| return -1; // Put unknown age last | |
| } | |
| return epochB.compareTo(epochA); | |
| }); | |
| apps.sort((Map<String, String> a, Map<String, String> b) { | |
| final int? epochA = int.tryParse(a['epoch'] ?? ''); | |
| final int? epochB = int.tryParse(b['epoch'] ?? ''); | |
| if (epochA == null && epochB == null) { | |
| return 0; | |
| } | |
| if (epochA == null) { | |
| return 1; // Put unknown age last | |
| } | |
| if (epochB == null) { | |
| return -1; // Put unknown age last | |
| } | |
| return epochB.compareTo(epochA); | |
| }); |
There was a problem hiding this comment.
+1, these type parameters can just be String.
| for (final Map<String, dynamic> app in apps) { | ||
| final String projectName = app['project_name'] as String? ?? 'Unknown'; | ||
| final String mode = app['mode'] as String? ?? 'Unknown'; | ||
| final String deviceName = app['device_name'] as String? ?? 'Unknown'; | ||
| final String deviceId = app['device_id'] as String? ?? 'Unknown'; | ||
| final String platform = app['target_platform'] as String? ?? 'Unknown'; | ||
| final String vmServiceUri = app['ws_uri'] as String? ?? 'Unknown'; | ||
| final String age = getProcessAge(app['epoch'] as String?, globals.systemClock); |
There was a problem hiding this comment.
The apps list is of type List<Map<String, String>>. The loop variable app should be typed as Map<String, String> to match. This improves type safety and allows removing the unnecessary casts.
| for (final Map<String, dynamic> app in apps) { | |
| final String projectName = app['project_name'] as String? ?? 'Unknown'; | |
| final String mode = app['mode'] as String? ?? 'Unknown'; | |
| final String deviceName = app['device_name'] as String? ?? 'Unknown'; | |
| final String deviceId = app['device_id'] as String? ?? 'Unknown'; | |
| final String platform = app['target_platform'] as String? ?? 'Unknown'; | |
| final String vmServiceUri = app['ws_uri'] as String? ?? 'Unknown'; | |
| final String age = getProcessAge(app['epoch'] as String?, globals.systemClock); | |
| for (final Map<String, String> app in apps) { | |
| final String projectName = app['project_name'] ?? 'Unknown'; | |
| final String mode = app['mode'] ?? 'Unknown'; | |
| final String deviceName = app['device_name'] ?? 'Unknown'; | |
| final String deviceId = app['device_id'] ?? 'Unknown'; | |
| final String platform = app['target_platform'] ?? 'Unknown'; | |
| final String vmServiceUri = app['ws_uri'] ?? 'Unknown'; | |
| final String age = getProcessAge(app['epoch'], globals.systemClock); |
| final MDNSService mdnsService = await MDNSService.create( | ||
| instance: 'Flutter Tools on $hostname', | ||
| service: '_flutter_devices._tcp', | ||
| port: await findUnusedPort(), |
There was a problem hiding this comment.
The port for the mDNS service should correspond to the service being advertised, which is the VM service. Using vmServiceUri.port is more accurate and avoids the overhead of calling findUnusedPort() repeatedly in a loop for each network interface. findUnusedPort() binds and closes a socket, which is an unnecessary operation here.
| port: await findUnusedPort(), | |
| port: vmServiceUri.port, |
| appName: FlutterProject.fromDirectory( | ||
| globals.fs.directory(projectRootPath), | ||
| ).manifest.appName, |
There was a problem hiding this comment.
This can just be FlutterProject.current(). It wouldn't hurt to hoist this out of the loop, although it shouldn't cause repeated I/O.
9dbae97 to
1dda0ac
Compare
| } on SocketException { | ||
| // If the bind operation fails for any reason (e.g., no interfaces available), | ||
| // rethrow. | ||
| rethrow; |
There was a problem hiding this comment.
If we're just rethrowing, I don't think we need this block.
| // This callback can't throw. | ||
| unawaited( | ||
| appStartedTimeRecorder.future.then<void>((_) { | ||
| // This callback is executed once the application has successfully started (or re-started). |
There was a problem hiding this comment.
Shouldn't this only run once when the application started?
| ]); | ||
| } | ||
|
|
||
| // Calculate column widths |
There was a problem hiding this comment.
We also duplicate this logic in Emulator.descriptions and Device.descriptions. We should probably create a helper utility to handle sizing and printing tables to the terminal.
There was a problem hiding this comment.
I considered it for this PR, but it should be a follow up, given the size of this PR already. Created an issue, and commented in the code. -- #180949
| // Join columns into lines of text | ||
| for (final row in table) { | ||
| globals.printStatus( | ||
| ' ${indices.map<String>((int i) => row[i].padRight(widths[i])).followedBy(<String>[row.last]).join(' • ')}', |
There was a problem hiding this comment.
Nit: avoid making multiple function calls in the context of string interpolation.
| // Let's try to split by common delimiters just in case, or handle it as a single pair. | ||
| // Given the serving side uses a list of strings, they might come as separate TXT records or joined. | ||
|
|
||
| final List<String> parts = txt.text.split('\n'); // Try newline first |
There was a problem hiding this comment.
This comment says "Try newline first", but I don't see logic trying anything else?
| globals.printStatus('Found ${apps.length} running Flutter ${pluralize('app', apps.length)}:'); | ||
| final table = <List<String>>[]; | ||
| for (final Map<String, dynamic> app in apps) { | ||
| final String projectName = app['project_name'] as String? ?? 'Unknown'; |
There was a problem hiding this comment.
We should define constants for all of these keys and use them in place of string literals.
| // used instead of mdns_dart as the discovery functionality is insufficient | ||
| // in the mdns_dart package, only discovering a maximum of one service. | ||
|
|
||
| globals.printStatus('Searching for running Flutter apps...'); |
There was a problem hiding this comment.
We're trying to avoid introducing additional uses of globals in new code, so any instances accessed via globals should be passed into the command's constructor.
| // dynamically assign a free port by using port 0. | ||
| socket = await ServerSocket.bind(hostname, 0); | ||
|
|
||
| final int port = socket.port; |
There was a problem hiding this comment.
Nit: this can just be return socket.port;
| @visibleForTesting SystemClock? systemClock, | ||
| required Logger logger, | ||
| }) : _mdnsClient = mdnsClient, | ||
| _systemClock = systemClock ?? globals.systemClock, |
There was a problem hiding this comment.
The systemClock parameter should just be required so we don't need to use globals here. In general, we shouldn't be importing globals.dart in any new files.
| // in the mdns_dart package, only discovering a maximum of one service. | ||
|
|
||
| _logger.printStatus('Searching for running Flutter apps...'); | ||
| final MDnsClient client = _mdnsClient ?? MDnsClient(); |
There was a problem hiding this comment.
Consider just setting _mdnsClient = mdnsClient ?? MDnsClient() in the initialization list, then _mdnsClient can be non-nullable.
| // Listen for pointer records (PTR) to find services | ||
| await for (final PtrResourceRecord ptr | ||
| in client | ||
| .lookup<PtrResourceRecord>(ResourceRecordQuery.serverPointer('_flutter_devices._tcp')) |
There was a problem hiding this comment.
Nit: '_flutter_devices._tcp' should be defined as a constant.
| for (final part in parts) { | ||
| final int equalsIndex = part.indexOf('='); | ||
| if (equalsIndex != -1) { | ||
| metadata[part.substring(0, equalsIndex)] = part.substring(equalsIndex + 1); |
There was a problem hiding this comment.
It's unlikely that we'll be seeing different formats given that it's other tool instances that will be advertising, but should we consider trimming these key/value pairs to ensure there's no extra whitespace?
|
I fixed a few bot issues and addressed a bunch of comments, not all done, but getting closer. |
natebosch
left a comment
There was a problem hiding this comment.
Are there security implications for this? Should we make responding to discovery an optional feature?
| /// Finds all non-loopback IPv4 and IPv6 addresses of the local machine. | ||
| /// | ||
| /// If no non-loopback addresses are found, returns loopback addresses. | ||
| Future<List<InternetAddress>> getLocalInternetAddresses() async { |
There was a problem hiding this comment.
https://dart.dev/effective-dart/design#avoid-starting-a-method-name-with-get
How about get localInternetAddresses and get localIpAddress
| // Use mDNS to discover running Flutter apps, the multicast_dns package is | ||
| // used instead of mdns_dart as the discovery functionality is insufficient | ||
| // in the mdns_dart package, only discovering a maximum of one service. |
There was a problem hiding this comment.
[nit] this comment seems very temporally local - is this information that future readers of this code will need to be reminded of each time they read it?
How about recording this detail in the commit message?
|
|
||
| /// Formats the elapsed time since the given epoch. | ||
| @visibleForTesting | ||
| String getProcessAge(String? epochString, SystemClock systemClock) { |
There was a problem hiding this comment.
How about a noun-named method String processAge(String? epochString, SystemClock systemClock)
| return 'unknown age'; | ||
| } | ||
| final Duration elapsed = systemClock.now().difference(DateTime.fromMillisecondsSinceEpoch(epoch)); | ||
| if (elapsed.inDays > 0) { |
There was a problem hiding this comment.
[optional] Consider a switch expression.
return switch (systemClock.now().difference(
DateTime.fromMillisecondsSinceEpoch(epoch),
)) {
Duration(:final inDays) when inDays > 0 => '${inDays}d',
Duration(:final inHours) when inHours > 0 => '${inHours}h',
Duration(:final inMinutes) when inMinutes > 0 => '${inMinutes}m',
Duration(:final inSeconds) => '${inSeconds}s',
};
There was a problem hiding this comment.
Holding off for now, I am going to revisit having this method (see TODO.)
| })(), | ||
| ); | ||
| } | ||
| await Future.wait(pendingLookups); |
There was a problem hiding this comment.
Consider using await pendingLookups.wait;
There was a problem hiding this comment.
Done, here and in the other location it was used.
|
|
||
| /// Discovers devices via mDNS and advertises the current Flutter application to them. | ||
| class MDNSDeviceDiscovery { | ||
| /// Creates a new [MDNSDeviceDiscovery] instance. |
There was a problem hiding this comment.
[nit] I'm not sure the Flutter repo stance on fully redundant doc comments. In Dart repos and internally to google3 we prefer omitting docs over including docs which are redundant with the signature. Consider expanding this, and if it can't be expanded consider removing this depending on Flutter repo conventions.
There was a problem hiding this comment.
removed this comment. Thanks Nate.
| /// Advertises the Flutter application via mDNS. | ||
| /// | ||
| /// The advertisement includes metadata about the application, device, and environment. | ||
| Future<void> advertise({required String appName, required Uri? vmServiceUri}) async { |
There was a problem hiding this comment.
[nit] This method is over 80 lines and indents to at least 5 levels. Consider splitting this up into composed submethods to reduce nesting and method length.
| } | ||
|
|
||
| if (await botDetector.isRunningOnBot || | ||
| platform.environment['BOT'] == 'true' || |
There was a problem hiding this comment.
Are these two cases not covered by isRunningOnBot?
There was a problem hiding this comment.
At one point I wasn't convinced, given that the bots are in a good state now, I am removing them to see for sure.
| const kFiveSecondsMs = 5_000; | ||
| const kFiftyNineSecondsMs = 59_000; | ||
| const kOneMinuteMs = 60_000; | ||
| const kFiftyNineMinutesMs = 3_540_000; // 59 * 60 * 1000 |
There was a problem hiding this comment.
Ubernit: maybe it's worth defining these constants based on each other rather than using the final integers?
Something like:
const kSecondMs = 1000;
const kMinuteMs = kSecondMs * 60;
const kHourMs = kMinuteMs * 60;
const kDayMs = kHourMs * 24;
// ...| ); | ||
| } | ||
|
|
||
| Future<int> findUnusedPort({String hostname = '0.0.0.0'}) async { |
There was a problem hiding this comment.
Is this used outside of this file? If not, I think we can remove the optional named parameter.
| onTimeout: (EventSink<PtrResourceRecord> sink) => sink.close(), | ||
| )) { | ||
| pendingLookups.add( | ||
| (() async { |
There was a problem hiding this comment.
There's still a closure being evaluated here. Is that intended?
| )) { | ||
| final metadata = <String, String>{}; | ||
| // The multicast_dns package joins the strings of a TXT record with newlines. | ||
| final List<String> parts = txt.text.split('\n'); |
There was a problem hiding this comment.
Will these always be Unix style line endings? Would it be safer to use LineSplitter.split(...)?
| client.stop(); | ||
| } | ||
|
|
||
| if (boolArg('machine')) { |
There was a problem hiding this comment.
Use FlutterGlobalOptions.kMachineFlag instead.
| } | ||
|
|
||
| if (boolArg('machine')) { | ||
| _logger.printStatus(json.encode(apps)); |
There was a problem hiding this comment.
We should just return after this print and remove the else statement to reduce nesting.
| // Used with the new compiler to generate a bootstrap file containing plugins | ||
| // and platform initialization. | ||
| Directory? _generatedEntrypointDirectory; | ||
| final _mdnsDiscoveries = <MDNSDeviceDiscovery>[]; |
There was a problem hiding this comment.
It is for cleanup which I didn't add, see resident_web_runner.dart now.
| required this.botDetector, | ||
| }); | ||
|
|
||
| static const String _kWsUri = 'ws_uri'; |
There was a problem hiding this comment.
We should be using the same constants as we use in the command class to ensure they don't diverge.
There was a problem hiding this comment.
resolved by having a single object now, all the constants are defined in one place.
| // Silence mDNS logs unless verbose logging is enabled. | ||
| // mdns_dart uses the 'mdns_dart' logger. | ||
| log.hierarchicalLoggingEnabled = true; | ||
| if (logger.isVerbose) { |
There was a problem hiding this comment.
Still needs to be addressed.
| try { | ||
| final MDNSService mdnsService = await MDNSService.create( | ||
| instance: 'Flutter Tools on $hostname', | ||
| service: '_flutter_devices._tcp', |
There was a problem hiding this comment.
We should be using a shared constant here.
| // is modified during iteration. | ||
| final serversToStop = List<MDNSServer>.of(_servers); | ||
| _servers.clear(); | ||
| for (final server in serversToStop) { |
There was a problem hiding this comment.
Ubernit: maybe this instead?
await serversToStop
.map(
(s) => s.stop().catchError(
(e) => logger.printTrace('Error stopping mDNS server: $e'),
),
)
.wait;That way we don't have to block waiting on each server to start individually.
There was a problem hiding this comment.
done. Tests have been updated as well. PTAL.
| if (seenUris.contains(uri)) { | ||
| continue; | ||
| } | ||
| seenUris.add(uri); |
There was a problem hiding this comment.
Set.add returns a boolean you can check to see if it was new:
| if (seenUris.contains(uri)) { | |
| continue; | |
| } | |
| seenUris.add(uri); | |
| if (!seenUris.add(uri)) { | |
| continue; | |
| } |
| } | ||
| seenUris.add(uri); | ||
| } | ||
| apps.add(metadata); |
There was a problem hiding this comment.
Do we want to do this if the uri is null?
There was a problem hiding this comment.
In my opinion if devs are running the new command, flutter running-apps and they get content back without a URI to use, it would be better to know that the rest of the mechanism is working -- reporting an app, just not with uri. @bkonyi -- does this seem reasonable to you?
There was a problem hiding this comment.
I don't think there's anything actionable on the user side if there's an app without a URI, so I don't think it would hurt to just throw the entry away. Is it even possible to advertise without a VM service URI?
There was a problem hiding this comment.
It looks like FlutterVmService only has null HTTP / WS URI entries in tests, so they should never be null in practice and we should never encounter a null uri. We should update the type to be non-nullable to indicate that.
| import 'package:path/path.dart' as p; // flutter_ignore: package_path_import | ||
| import 'package:process/process.dart'; | ||
|
|
||
| import '../convert.dart'; |
There was a problem hiding this comment.
It is needed in this class for the Encoding object reference.
|
|
||
| void addEnableLocalDiscoveryFlag() { | ||
| argParser.addFlag( | ||
| 'enable-local-discovery', |
There was a problem hiding this comment.
This should probably just be part of run.dart since we're not using it in any other command. Also, we should define a constant for enable-local-discovery and use it in the error message where it's referenced.
| /// The advertisement includes metadata about the application, device, and environment. | ||
| Future<void> advertise({required String appName, required Uri? vmServiceUri}) async { | ||
| try { | ||
| await stop(); // Stop any existing advertisements before starting new ones. |
There was a problem hiding this comment.
Do we expect this to be called more than once for a given instance?
There was a problem hiding this comment.
The method advertise is called only once per MDNSDeviceDiscovery instance, immediately after the device connects. However, the stop() call at the beginning of advertise serves as a defensive measure.
There was a problem hiding this comment.
Since we never expect for advertise to be invoked more than once I think it'd be better to fail loudly. Being defensive here instead of raising an exception could result in unexpected behavior that could be hard to pin down.
| } | ||
|
|
||
| // Silence mDNS logs unless verbose logging is enabled. | ||
| // mdns_dart uses the 'mdns_dart' logger. |
There was a problem hiding this comment.
Nit: is this logger within package:mdns_dart? If so, I'd reference package:mdns_dart instead of just mdns_dart here.
There was a problem hiding this comment.
Also, maybe it makes sense to move this further up in the call stack where it can only be invoked once? Maybe even behind a static flag in the constructor of this class? It feels strange that we're resetting this state each time this method is invoked, particularly since hierarchicalLoggingEnabled applies to all loggers and could technically be set elsewhere.
|
|
||
| /// Stops the mDNS advertisement. | ||
| Future<void> stop() async { | ||
| // Create a copy of the list to avoid ConcurrentModificationError as the list |
There was a problem hiding this comment.
Is this true? I don't see _servers being modified in the serversToStop.map(...) code.
|
|
||
| /// A class representing the metadata discovered from a running Flutter application | ||
| /// via mDNS. | ||
| class MDnsObservation { |
There was a problem hiding this comment.
Nit: MDNSObservation for consistency.
| final String? targetPlatform; | ||
| final String? mode; | ||
| final String? wsUri; | ||
| final String? epoch; |
There was a problem hiding this comment.
Nit: I think epoch and pid can still be int or double as there's implicit toString() calls when using string interpolation.
| /// Finds all non-loopback IPv4 and IPv6 addresses of the local machine. | ||
| /// | ||
| /// If no non-loopback addresses are found, returns loopback addresses. | ||
| Future<List<InternetAddress>> get localInternetAddresses async { |
There was a problem hiding this comment.
These functions don't actually seem to be used outside of tests. Are they needed?
There was a problem hiding this comment.
removed, tests removed as well.
| static const String _kDartVersion = 'dart_version'; | ||
| static const String _kHostname = 'hostname'; | ||
|
|
||
| final String? hostname; |
There was a problem hiding this comment.
I think these types should all be non-nullable and we should reject any records that don't match the expected signature.
Roll Flutter from e8f9dc50356d to 9bda20a11f1e (34 revisions) flutter/flutter@e8f9dc5...9bda20a 2026-02-10 brackenavaron@gmail.com Remove Material import from focus_traversal_test.dart (flutter/flutter#180994) 2026-02-10 engine-flutter-autoroll@skia.org Roll Skia from 6e217430c052 to cffb3bf918df (1 revision) (flutter/flutter#182131) 2026-02-10 divyansh.shah.ece23@itbhu.ac.in Encourage splitting large test files in testing documentation 2 (flutter/flutter#182051) 2026-02-10 34465683+rkishan516@users.noreply.github.com refactor: migrate CupertinoPageTransitionsBuilder to cupertino folder (flutter/flutter#179776) 2026-02-10 30870216+gaaclarke@users.noreply.github.com Delete the last remaining skia only fragment shader tests (flutter/flutter#182127) 2026-02-10 jhy03261997@gmail.com [a11y][android] Set new CheckState APIs for android API 36 (flutter/flutter#182113) 2026-02-10 dkwingsmt@users.noreply.github.com Add missing dependencies to framework_tests_misc_leak_tracking (flutter/flutter#181929) 2026-02-10 engine-flutter-autoroll@skia.org Roll Dart SDK from eee0e2e11174 to 69eb951f8f7e (2 revisions) (flutter/flutter#182128) 2026-02-10 fluttergithubbot@gmail.com Marks Linux_android_emu android_display_cutout to be flaky (flutter/flutter#181901) 2026-02-10 737941+loic-sharma@users.noreply.github.com Bump Dart to 3.10 (flutter/flutter#174066) 2026-02-10 engine-flutter-autoroll@skia.org Roll Skia from d4b7e24a209b to 6e217430c052 (6 revisions) (flutter/flutter#182126) 2026-02-09 15619084+vashworth@users.noreply.github.com Intercept UIScene device log and print a guided warning (flutter/flutter#181515) 2026-02-09 47866232+chunhtai@users.noreply.github.com Introduce ScrollCacheExtent and also fixes unbound shrinkwrap cache ex… (flutter/flutter#181092) 2026-02-09 43054281+camsim99@users.noreply.github.com [Android] Add mechanism for setting Android engine flags via Android manifest (take 2) (flutter/flutter#181632) 2026-02-09 116356835+AbdeMohlbi@users.noreply.github.com Fix wrong comment about default impeller value (flutter/flutter#181831) 2026-02-09 huqian123hq@hotmail.com fix build fail for wayland only platform (flutter/flutter#182057) 2026-02-09 jesswon@google.com [AGP 9] Added Warning Against Updating to AGP 9 (flutter/flutter#181977) 2026-02-09 30870216+gaaclarke@users.noreply.github.com Updated Shaderc dep (flutter/flutter#180976) 2026-02-09 47866232+chunhtai@users.noreply.github.com Refactor accessibility guidelines out to widget layer (flutter/flutter#181672) 2026-02-09 engine-flutter-autoroll@skia.org Roll Skia from 68dff53238e5 to d4b7e24a209b (2 revisions) (flutter/flutter#182087) 2026-02-09 ikramhasan.dev@gmail.com fix: OutlineInputBorder not respecting BorderSide stroke alignment (flutter/flutter#180487) 2026-02-09 97480502+b-luk@users.noreply.github.com Adds opengles to engine dart tests (flutter/flutter#181933) 2026-02-09 15619084+vashworth@users.noreply.github.com Add command to build a Swift Package for Add to App and generate FlutterPluginRegistrant (flutter/flutter#181224) 2026-02-09 116356835+AbdeMohlbi@users.noreply.github.com Remove unused constant in `bundle.dart` (flutter/flutter#182023) 2026-02-09 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from iqtwdXlgKIyZkL5Li... to 7BGf7mPQvgLi7Axb6... (flutter/flutter#182082) 2026-02-09 116356835+AbdeMohlbi@users.noreply.github.com Remove unused getters in `user_messages.dart` (flutter/flutter#181867) 2026-02-09 engine-flutter-autoroll@skia.org Roll Packages from 7805d3e to 3d5eaa5 (3 revisions) (flutter/flutter#182083) 2026-02-09 engine-flutter-autoroll@skia.org Roll Skia from 5d891cd7fb7f to 68dff53238e5 (1 revision) (flutter/flutter#182080) 2026-02-09 robert.ancell@canonical.com Update example description (flutter/flutter#182067) 2026-02-09 engine-flutter-autoroll@skia.org Roll Dart SDK from 965b51c219d3 to eee0e2e11174 (1 revision) (flutter/flutter#182073) 2026-02-09 engine-flutter-autoroll@skia.org Roll Skia from 9533d7533d59 to 5d891cd7fb7f (6 revisions) (flutter/flutter#182070) 2026-02-09 jwren@google.com Add a new flutter cli command, running-apps, using mDNS app discovery (flutter/flutter#180098) 2026-02-09 engine-flutter-autoroll@skia.org Roll Skia from b7db9f35f0f2 to 9533d7533d59 (2 revisions) (flutter/flutter#182069) 2026-02-08 robert.ancell@canonical.com Improve FlWindowMonitor API (flutter/flutter#181885) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC boetger@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 ...
…flutter#180098) Includes: - Display running apps in a formatted table with age calculation - Implement mDNS discovery for running apps (multiple devices/interfaces) - Deduplicate apps by WebSocket URI - Centralize mDNS device advertisement in MDNSDeviceDiscovery - Ensure ResidentRunner advertises correct app name and cleans up - Add network utility functions and JSON support for running-apps - Add comprehensive tests for discovery and list command Lastly, this change uses mDNS to discover running Flutter apps, the multicast_dns package is used instead of mdns_dart as the discovery functionality is insufficient in the mdns_dart package, only discovering a maximum of one service.
…flutter#180098) Includes: - Display running apps in a formatted table with age calculation - Implement mDNS discovery for running apps (multiple devices/interfaces) - Deduplicate apps by WebSocket URI - Centralize mDNS device advertisement in MDNSDeviceDiscovery - Ensure ResidentRunner advertises correct app name and cleans up - Add network utility functions and JSON support for running-apps - Add comprehensive tests for discovery and list command Lastly, this change uses mDNS to discover running Flutter apps, the multicast_dns package is used instead of mdns_dart as the discovery functionality is insufficient in the mdns_dart package, only discovering a maximum of one service.
…utility function. (flutter#182196) This resolves flutter#180949 This is follow-up on flutter#180098
…flutter#180098) Includes: - Display running apps in a formatted table with age calculation - Implement mDNS discovery for running apps (multiple devices/interfaces) - Deduplicate apps by WebSocket URI - Centralize mDNS device advertisement in MDNSDeviceDiscovery - Ensure ResidentRunner advertises correct app name and cleans up - Add network utility functions and JSON support for running-apps - Add comprehensive tests for discovery and list command Lastly, this change uses mDNS to discover running Flutter apps, the multicast_dns package is used instead of mdns_dart as the discovery functionality is insufficient in the mdns_dart package, only discovering a maximum of one service.
…utility function. (flutter#182196) This resolves flutter#180949 This is follow-up on flutter#180098
…r#10992) Roll Flutter from e8f9dc50356d to 9bda20a11f1e (34 revisions) flutter/flutter@e8f9dc5...9bda20a 2026-02-10 brackenavaron@gmail.com Remove Material import from focus_traversal_test.dart (flutter/flutter#180994) 2026-02-10 engine-flutter-autoroll@skia.org Roll Skia from 6e217430c052 to cffb3bf918df (1 revision) (flutter/flutter#182131) 2026-02-10 divyansh.shah.ece23@itbhu.ac.in Encourage splitting large test files in testing documentation 2 (flutter/flutter#182051) 2026-02-10 34465683+rkishan516@users.noreply.github.com refactor: migrate CupertinoPageTransitionsBuilder to cupertino folder (flutter/flutter#179776) 2026-02-10 30870216+gaaclarke@users.noreply.github.com Delete the last remaining skia only fragment shader tests (flutter/flutter#182127) 2026-02-10 jhy03261997@gmail.com [a11y][android] Set new CheckState APIs for android API 36 (flutter/flutter#182113) 2026-02-10 dkwingsmt@users.noreply.github.com Add missing dependencies to framework_tests_misc_leak_tracking (flutter/flutter#181929) 2026-02-10 engine-flutter-autoroll@skia.org Roll Dart SDK from eee0e2e11174 to 69eb951f8f7e (2 revisions) (flutter/flutter#182128) 2026-02-10 fluttergithubbot@gmail.com Marks Linux_android_emu android_display_cutout to be flaky (flutter/flutter#181901) 2026-02-10 737941+loic-sharma@users.noreply.github.com Bump Dart to 3.10 (flutter/flutter#174066) 2026-02-10 engine-flutter-autoroll@skia.org Roll Skia from d4b7e24a209b to 6e217430c052 (6 revisions) (flutter/flutter#182126) 2026-02-09 15619084+vashworth@users.noreply.github.com Intercept UIScene device log and print a guided warning (flutter/flutter#181515) 2026-02-09 47866232+chunhtai@users.noreply.github.com Introduce ScrollCacheExtent and also fixes unbound shrinkwrap cache ex… (flutter/flutter#181092) 2026-02-09 43054281+camsim99@users.noreply.github.com [Android] Add mechanism for setting Android engine flags via Android manifest (take 2) (flutter/flutter#181632) 2026-02-09 116356835+AbdeMohlbi@users.noreply.github.com Fix wrong comment about default impeller value (flutter/flutter#181831) 2026-02-09 huqian123hq@hotmail.com fix build fail for wayland only platform (flutter/flutter#182057) 2026-02-09 jesswon@google.com [AGP 9] Added Warning Against Updating to AGP 9 (flutter/flutter#181977) 2026-02-09 30870216+gaaclarke@users.noreply.github.com Updated Shaderc dep (flutter/flutter#180976) 2026-02-09 47866232+chunhtai@users.noreply.github.com Refactor accessibility guidelines out to widget layer (flutter/flutter#181672) 2026-02-09 engine-flutter-autoroll@skia.org Roll Skia from 68dff53238e5 to d4b7e24a209b (2 revisions) (flutter/flutter#182087) 2026-02-09 ikramhasan.dev@gmail.com fix: OutlineInputBorder not respecting BorderSide stroke alignment (flutter/flutter#180487) 2026-02-09 97480502+b-luk@users.noreply.github.com Adds opengles to engine dart tests (flutter/flutter#181933) 2026-02-09 15619084+vashworth@users.noreply.github.com Add command to build a Swift Package for Add to App and generate FlutterPluginRegistrant (flutter/flutter#181224) 2026-02-09 116356835+AbdeMohlbi@users.noreply.github.com Remove unused constant in `bundle.dart` (flutter/flutter#182023) 2026-02-09 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from iqtwdXlgKIyZkL5Li... to 7BGf7mPQvgLi7Axb6... (flutter/flutter#182082) 2026-02-09 116356835+AbdeMohlbi@users.noreply.github.com Remove unused getters in `user_messages.dart` (flutter/flutter#181867) 2026-02-09 engine-flutter-autoroll@skia.org Roll Packages from 7805d3e to 3d5eaa5 (3 revisions) (flutter/flutter#182083) 2026-02-09 engine-flutter-autoroll@skia.org Roll Skia from 5d891cd7fb7f to 68dff53238e5 (1 revision) (flutter/flutter#182080) 2026-02-09 robert.ancell@canonical.com Update example description (flutter/flutter#182067) 2026-02-09 engine-flutter-autoroll@skia.org Roll Dart SDK from 965b51c219d3 to eee0e2e11174 (1 revision) (flutter/flutter#182073) 2026-02-09 engine-flutter-autoroll@skia.org Roll Skia from 9533d7533d59 to 5d891cd7fb7f (6 revisions) (flutter/flutter#182070) 2026-02-09 jwren@google.com Add a new flutter cli command, running-apps, using mDNS app discovery (flutter/flutter#180098) 2026-02-09 engine-flutter-autoroll@skia.org Roll Skia from b7db9f35f0f2 to 9533d7533d59 (2 revisions) (flutter/flutter#182069) 2026-02-08 robert.ancell@canonical.com Improve FlWindowMonitor API (flutter/flutter#181885) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC boetger@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 ...
Includes:
Lastly, this change uses mDNS to discover running Flutter apps, the multicast_dns package is used instead of mdns_dart as the discovery functionality is insufficient in the mdns_dart package, only discovering a maximum of one service.