Skip to content

Commit 2f73978

Browse files
authored
[Deep Links] Show a more informative dialog when Flutter project is not configured for iOS or Android (#9571)
1 parent 79f0a20 commit 2f73978

3 files changed

Lines changed: 123 additions & 5 deletions

File tree

packages/devtools_app/lib/src/screens/deep_link_validation/project_root_selection/select_project_view.dart

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ import '../../../shared/analytics/constants.dart' as gac;
1313
import '../../../shared/globals.dart';
1414
import '../../../shared/primitives/utils.dart';
1515
import '../../../shared/server/server.dart' as server;
16+
import '../../../shared/ui/common_widgets.dart';
1617
import '../deep_links_controller.dart';
1718
import '../deep_links_model.dart';
1819
import 'root_selector.dart';
1920

2021
const _kLinearProgressIndicatorWidth = 280.0;
2122

23+
enum DeepLinksTarget { android, ios }
24+
2225
/// A view for selecting a Flutter project.
2326
class SelectProjectView extends StatefulWidget {
2427
const SelectProjectView({super.key});
@@ -131,7 +134,7 @@ class _SelectProjectViewState extends State<SelectProjectView> {
131134
gac.deeplink,
132135
gac.AnalyzeFlutterProject.flutterInvalidProjectSelected.name,
133136
);
134-
await showNonFlutterProjectDialog();
137+
await _showFlutterProjectMissingBuildOptionsDialog(directory);
135138
return;
136139
}
137140
controller.selectedProject.value = FlutterProject(
@@ -144,6 +147,119 @@ class _SelectProjectViewState extends State<SelectProjectView> {
144147
});
145148
}
146149

150+
Future<void> _showFlutterProjectMissingBuildOptionsDialog(
151+
String appPath,
152+
) async {
153+
await showDialog(
154+
context: context,
155+
builder: (_) {
156+
final theme = Theme.of(context);
157+
return DevToolsDialog(
158+
title: const Text('No iOS or Android build options found.'),
159+
content: SizedBox(
160+
width: defaultDialogWidth,
161+
child: Column(
162+
crossAxisAlignment: CrossAxisAlignment.start,
163+
children: [
164+
const Text(
165+
'DevTools could not verify the build options for this project.',
166+
),
167+
const SizedBox(height: largeSpacing),
168+
..._deepLinksInstructions(
169+
theme: theme,
170+
appPath: appPath,
171+
target: DeepLinksTarget.android,
172+
),
173+
const SizedBox(height: largeSpacing),
174+
..._deepLinksInstructions(
175+
theme: theme,
176+
appPath: appPath,
177+
target: DeepLinksTarget.ios,
178+
),
179+
],
180+
),
181+
),
182+
actions: const [DialogCloseButton()],
183+
);
184+
},
185+
);
186+
setState(() {
187+
_retrievingFlutterProject = false;
188+
});
189+
}
190+
191+
List<Widget> _deepLinksInstructions({
192+
required ThemeData theme,
193+
required String appPath,
194+
required DeepLinksTarget target,
195+
}) {
196+
final commandStyle = theme.subtleTextStyle.copyWith(
197+
fontFamily: 'RobotoMono',
198+
);
199+
const commandPadding = EdgeInsets.symmetric(
200+
horizontal: denseSpacing,
201+
vertical: densePadding,
202+
);
203+
final title = switch (target) {
204+
DeepLinksTarget.android => 'Android',
205+
DeepLinksTarget.ios => 'iOS',
206+
};
207+
final directory = switch (target) {
208+
DeepLinksTarget.android => '/android',
209+
DeepLinksTarget.ios => '/ios',
210+
};
211+
final documentationUrl = switch (target) {
212+
DeepLinksTarget.android => 'https://docs.flutter.dev/deployment/android',
213+
DeepLinksTarget.ios => 'https://docs.flutter.dev/deployment/ios',
214+
};
215+
final command = switch (target) {
216+
DeepLinksTarget.android =>
217+
'flutter analyze --android --list-build-variants',
218+
DeepLinksTarget.ios => 'flutter analyze --ios --list-build-options',
219+
};
220+
221+
return [
222+
Text('For $title', style: theme.textTheme.titleMedium),
223+
const SizedBox(height: intermediateSpacing),
224+
RichText(
225+
text: TextSpan(
226+
style: theme.regularTextStyle,
227+
children: [
228+
TextSpan(
229+
text:
230+
'These are configured in the $directory directory. Please refer to the ',
231+
),
232+
GaLinkTextSpan(
233+
link: GaLink(
234+
display: 'Flutter documentation',
235+
url: documentationUrl,
236+
),
237+
context: context,
238+
),
239+
const TextSpan(text: ' for more information.'),
240+
],
241+
),
242+
),
243+
const SizedBox(height: intermediateSpacing),
244+
const Text(
245+
'To confirm your setup, run the following command in your terminal:',
246+
),
247+
const SizedBox(height: denseSpacing),
248+
Card(
249+
child: Padding(
250+
padding: commandPadding,
251+
child: Row(
252+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
253+
children: [
254+
Flexible(child: Text('$command $appPath', style: commandStyle)),
255+
CopyToClipboardControl(dataProvider: () => '$command $appPath'),
256+
],
257+
),
258+
),
259+
),
260+
];
261+
}
262+
147263
@override
148264
Widget build(BuildContext context) {
149265
final theme = Theme.of(context);
@@ -156,7 +272,7 @@ class _SelectProjectViewState extends State<SelectProjectView> {
156272
Padding(
157273
padding: const EdgeInsets.all(extraLargeSpacing),
158274
child: Text(
159-
'Select a local flutter project to check the status of all deep links.',
275+
'Select a local Flutter project to check the status of all deep links.',
160276
textAlign: TextAlign.center,
161277
style: Theme.of(context).textTheme.titleMedium,
162278
),

packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ TODO: Remove this section if there are not any general updates.
5151

5252
## Deep links tool updates
5353

54-
TODO: Remove this section if there are not any general updates.
54+
- Added more informative dialog if Deep Links tool is unable to find build
55+
options for the iOS or Android app. -
56+
[#9571](https://github.com/flutter/devtools/pull/9571)
5557

5658
## VS Code Sidebar updates
5759

packages/devtools_app/test/screens/deep_link_validation/select_project_view_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ void main() {
6363
) async {
6464
await pumpSelectProjectView(tester);
6565
expect(
66-
find.textContaining('Select a local flutter project to check'),
66+
find.textContaining('Select a local Flutter project to check'),
6767
findsOneWidget,
6868
);
6969
expect(find.byType(ProjectRootsDropdown), findsOneWidget);
@@ -95,7 +95,7 @@ void main() {
9595
(WidgetTester tester) async {
9696
await pumpSelectProjectView(tester);
9797
expect(
98-
find.textContaining('Select a local flutter project to check'),
98+
find.textContaining('Select a local Flutter project to check'),
9999
findsOneWidget,
100100
);
101101
expect(find.byType(ProjectRootsDropdown), findsOneWidget);

0 commit comments

Comments
 (0)