From 366f11aad4c9fda78c99df969b65e3009c734f4c Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Thu, 11 Dec 2025 15:38:56 -0800 Subject: [PATCH 1/5] Fixes null error when parsing universal link settings --- .../src/deeplink/universal_link_settings.dart | 4 +- .../universal_link_settings_test.dart | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 packages/devtools_shared/test/deeplink/universal_link_settings_test.dart diff --git a/packages/devtools_shared/lib/src/deeplink/universal_link_settings.dart b/packages/devtools_shared/lib/src/deeplink/universal_link_settings.dart index 486b09a74a8..0cc78b5a96a 100644 --- a/packages/devtools_shared/lib/src/deeplink/universal_link_settings.dart +++ b/packages/devtools_shared/lib/src/deeplink/universal_link_settings.dart @@ -21,10 +21,10 @@ extension type const UniversalLinkSettings._(Map _json) { }); /// The bundle identifier of the iOS build of this Flutter project. - String get bundleIdentifier => _json[_kBundleIdentifierKey] as String; + String? get bundleIdentifier => _json[_kBundleIdentifierKey] as String?; /// The team identifier of the iOS build of this Flutter project. - String get teamIdentifier => _json[_kTeamIdentifierKey] as String; + String? get teamIdentifier => _json[_kTeamIdentifierKey] as String?; /// The associated domains of the iOS build of this Flutter project. List get associatedDomains => diff --git a/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart b/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart new file mode 100644 index 00000000000..b1cb3d7b61e --- /dev/null +++ b/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart @@ -0,0 +1,64 @@ +// Copyright 2024 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. + +import 'dart:convert'; + +import 'package:devtools_shared/src/deeplink/universal_link_settings.dart'; +import 'package:test/test.dart'; + +void main() { + group('UniversalLinkSettings', () { + test('parses json correctly', () { + const json = ''' +{ + "bundleIdentifier": "com.example.app", + "teamIdentifier": "TEAMID", + "associatedDomains": ["applinks:example.com"] +} +'''; + final settings = UniversalLinkSettings.fromJson(json); + expect(settings.bundleIdentifier, 'com.example.app'); + expect(settings.teamIdentifier, 'TEAMID'); + expect(settings.associatedDomains, ['applinks:example.com']); + }); + + test('handles null bundleIdentifier', () { + const json = ''' +{ + "teamIdentifier": "TEAMID", + "associatedDomains": ["applinks:example.com"] +} +'''; + final settings = UniversalLinkSettings.fromJson(json); + expect(settings.bundleIdentifier, isNull); + expect(settings.teamIdentifier, 'TEAMID'); + expect(settings.associatedDomains, ['applinks:example.com']); + }); + + test('handles null teamIdentifier', () { + const json = ''' +{ + "bundleIdentifier": "com.example.app", + "associatedDomains": ["applinks:example.com"] +} +'''; + final settings = UniversalLinkSettings.fromJson(json); + expect(settings.bundleIdentifier, 'com.example.app'); + expect(settings.teamIdentifier, isNull); + expect(settings.associatedDomains, ['applinks:example.com']); + }); + + test('handles missing associatedDomains', () { + // This might crash based on my reading of the code, but let's see. + // The code does: (_json[_kAssociatedDomainsKey] as List).cast().toList(); + // If _json[_kAssociatedDomainsKey] is null, 'as List' will throw. + // But the user only asked for bundleIdentifier and teamIdentifier. + // I will stick to what was asked first, but maybe add a test for associatedDomains if I'm feeling generous or if I want to be thorough. + // Actually, let's just stick to the requested tests first to be safe and not overstep. + // But wait, if I see a potential crash, I should probably fix it or at least test it? + // The user specifically asked for "bundleIdentifier and teamIdentifier can be null and not crash". + // I'll add those. + }); + }); +} From a66fcf65178fb21542baa2c06a7d69e7e9396afe Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Thu, 11 Dec 2025 15:41:43 -0800 Subject: [PATCH 2/5] update --- .../test/deeplink/universal_link_settings_test.dart | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart b/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart index b1cb3d7b61e..3ae30424bb3 100644 --- a/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart +++ b/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart @@ -48,17 +48,5 @@ void main() { expect(settings.teamIdentifier, isNull); expect(settings.associatedDomains, ['applinks:example.com']); }); - - test('handles missing associatedDomains', () { - // This might crash based on my reading of the code, but let's see. - // The code does: (_json[_kAssociatedDomainsKey] as List).cast().toList(); - // If _json[_kAssociatedDomainsKey] is null, 'as List' will throw. - // But the user only asked for bundleIdentifier and teamIdentifier. - // I will stick to what was asked first, but maybe add a test for associatedDomains if I'm feeling generous or if I want to be thorough. - // Actually, let's just stick to the requested tests first to be safe and not overstep. - // But wait, if I see a potential crash, I should probably fix it or at least test it? - // The user specifically asked for "bundleIdentifier and teamIdentifier can be null and not crash". - // I'll add those. - }); }); } From 78630fdc38d53bab943408796d258d6f8ad23b27 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Thu, 11 Dec 2025 15:43:09 -0800 Subject: [PATCH 3/5] update --- packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index c691a0484f2..1f193db5cb0 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -55,6 +55,8 @@ TODO: Remove this section if there are not any general updates. - Added more informative dialog if Deep Links tool is unable to find build options for the iOS or Android app. - [#9571](https://github.com/flutter/devtools/pull/9571) +- Fixed null error when parsing universal link settings - + [#9581](https://github.com/flutter/devtools/pull/9581) ## VS Code Sidebar updates From daa402c2ea0b9b24aebdf528b6b218ad4fd2c0b8 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Thu, 11 Dec 2025 15:51:26 -0800 Subject: [PATCH 4/5] analyze --- .../test/deeplink/universal_link_settings_test.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart b/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart index 3ae30424bb3..395539c4603 100644 --- a/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart +++ b/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. -import 'dart:convert'; - import 'package:devtools_shared/src/deeplink/universal_link_settings.dart'; import 'package:test/test.dart'; From abd13e649affb85f4d3f3a1620376d091108e630 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Fri, 12 Dec 2025 09:16:57 -0800 Subject: [PATCH 5/5] address comment --- .../test/deeplink/universal_link_settings_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart b/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart index 395539c4603..15a56adb54c 100644 --- a/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart +++ b/packages/devtools_shared/test/deeplink/universal_link_settings_test.dart @@ -1,4 +1,4 @@ -// Copyright 2024 The Flutter Authors +// Copyright 2025 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.