Skip to content

Commit 459893b

Browse files
expand feedback diagnostics
1 parent 468f5ea commit 459893b

15 files changed

Lines changed: 426 additions & 88 deletions

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ jobs:
1616
fail-fast: false
1717
matrix:
1818
sdk:
19-
- '3.27.0'
19+
- '3.29.0'
2020
- stable
2121

2222
steps:
2323
- name: Check out repository
2424
uses: actions/checkout@v6
2525

26-
- name: Set up Flutter 3.27.0
27-
if: matrix.sdk == '3.27.0'
26+
- name: Set up Flutter 3.29.0
27+
if: matrix.sdk == '3.29.0'
2828
uses: subosito/flutter-action@v2
2929
with:
3030
channel: stable
31-
flutter-version: '3.27.0'
31+
flutter-version: '3.29.0'
3232
cache: true
3333

3434
- name: Set up current Flutter stable

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
## 0.4.0
2+
3+
* Add system and resolved application locales to runtime diagnostics.
4+
* Add current-view logical size and device pixel ratio when a diagnostics
5+
context is available.
6+
* Add native process architecture and a privacy-safe device model where the
7+
platform exposes one reliably.
8+
* Add an optional `diagnosticsContext` argument to `sendEmail`; the packaged
9+
widget supplies it automatically.
10+
* Keep package and device metadata collection independent and best-effort.
11+
* Add `device_info_plus` 11.3.3 through 11.x and raise the package minimums to
12+
Dart 3.7 and Flutter 3.29.
13+
114
## 0.3.0
215

316
* Add an optional pre-filled email body.

README.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ client with a pre-filled feedback message.
88
## Features
99

1010
- Pre-fills the recipient, subject, and optional email body.
11-
- Optionally includes application and operating-system diagnostics.
11+
- Optionally includes privacy-safe application, operating-system, device, and
12+
display diagnostics.
1213
- Accepts application-specific diagnostic fields.
1314

1415
## Requirements
1516

16-
- Dart 3.6 or later.
17-
- Flutter 3.27 or later.
17+
- Dart 3.7 or later.
18+
- Flutter 3.29 or later.
1819
- This package uses `package_info_plus` 8.3.1 through 8.x.
20+
- This package uses `device_info_plus` 11.3.3 through 11.x.
1921
- Android builds require API 19 or later, compile SDK 34, Java 17, Android
2022
Gradle Plugin 8.3 or later, and Gradle 8.4 or later.
2123
- Apple builds require iOS 12 or later and macOS 10.14 or later.
@@ -47,9 +49,16 @@ class FeedbackButton extends StatelessWidget {
4749
```
4850

4951
When runtime diagnostics are enabled, the package includes the application
50-
name, package identifier, version/build number, operating-system name, and OS
51-
version. Values passed through `additionalDiagnostics` are controlled entirely
52-
by the calling application.
52+
name, package identifier, version/build number, operating-system name and
53+
version, system locales, native process architecture, and a non-identifying
54+
device model where the platform provides one reliably. The widget also reports
55+
the resolved app locale, logical window size, and device pixel ratio from its
56+
own widget context.
57+
58+
The default diagnostics deliberately exclude CPU names, emulator status,
59+
user-assigned device and host names, usernames, GUIDs, machine IDs, vendor
60+
identifiers, and other unique identifiers. Values passed through
61+
`additionalDiagnostics` are controlled entirely by the calling application.
5362

5463
If runtime metadata cannot be collected, the email launch is still attempted
5564
and the message reports that runtime diagnostics were unavailable.
@@ -70,6 +79,7 @@ final launchRequested = await sendEmail(
7079
emailSubject: 'App feedback',
7180
emailBody: 'Please describe the issue:',
7281
includeRuntimeDiagnostics: true,
82+
diagnosticsContext: context,
7383
additionalDiagnostics: {
7484
'Current screen': 'Export',
7585
},
@@ -79,3 +89,9 @@ if (!launchRequested) {
7989
// Show an alternative contact method.
8090
}
8191
```
92+
93+
Passing `diagnosticsContext` lets the utility report the locale resolved by the
94+
nearest Flutter `Localizations` widget and metrics for the current view. It is
95+
optional: calls without it still receive system locales and the other global
96+
runtime diagnostics. This distinction matters when an app falls back to a
97+
different language than the user's first system locale.

example/macos/Flutter/GeneratedPluginRegistrant.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import FlutterMacOS
66
import Foundation
77

8+
import device_info_plus
89
import package_info_plus
910
import url_launcher_macos
1011

1112
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
13+
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
1214
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
1315
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
1416
}

example/pubspec.lock

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ packages:
4949
url: "https://pub.dev"
5050
source: hosted
5151
version: "1.0.9"
52+
device_info_plus:
53+
dependency: transitive
54+
description:
55+
name: device_info_plus
56+
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
57+
url: "https://pub.dev"
58+
source: hosted
59+
version: "11.5.0"
60+
device_info_plus_platform_interface:
61+
dependency: transitive
62+
description:
63+
name: device_info_plus_platform_interface
64+
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
65+
url: "https://pub.dev"
66+
source: hosted
67+
version: "7.0.3"
5268
fake_async:
5369
dependency: transitive
5470
description:
@@ -65,6 +81,14 @@ packages:
6581
url: "https://pub.dev"
6682
source: hosted
6783
version: "2.2.0"
84+
file:
85+
dependency: transitive
86+
description:
87+
name: file
88+
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
89+
url: "https://pub.dev"
90+
source: hosted
91+
version: "7.0.1"
6892
flutter:
6993
dependency: "direct main"
7094
description: flutter
@@ -140,10 +164,10 @@ packages:
140164
dependency: transitive
141165
description:
142166
name: matcher
143-
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
167+
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd"
144168
url: "https://pub.dev"
145169
source: hosted
146-
version: "0.12.19"
170+
version: "0.12.20"
147171
material_color_utilities:
148172
dependency: transitive
149173
description:
@@ -156,10 +180,10 @@ packages:
156180
dependency: transitive
157181
description:
158182
name: meta
159-
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
183+
sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9"
160184
url: "https://pub.dev"
161185
source: hosted
162-
version: "1.18.0"
186+
version: "1.19.0"
163187
package_info_plus:
164188
dependency: transitive
165189
description:
@@ -198,7 +222,7 @@ packages:
198222
path: ".."
199223
relative: true
200224
source: path
201-
version: "0.3.0"
225+
version: "0.4.0"
202226
sky_engine:
203227
dependency: transitive
204228
description: flutter
@@ -248,10 +272,10 @@ packages:
248272
dependency: transitive
249273
description:
250274
name: test_api
251-
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
275+
sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11"
252276
url: "https://pub.dev"
253277
source: hosted
254-
version: "0.7.11"
278+
version: "0.7.12"
255279
typed_data:
256280
dependency: transitive
257281
description:
@@ -328,10 +352,10 @@ packages:
328352
dependency: transitive
329353
description:
330354
name: vector_math
331-
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
355+
sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47
332356
url: "https://pub.dev"
333357
source: hosted
334-
version: "2.2.0"
358+
version: "2.4.2"
335359
vm_service:
336360
dependency: transitive
337361
description:
@@ -356,6 +380,14 @@ packages:
356380
url: "https://pub.dev"
357381
source: hosted
358382
version: "5.15.0"
383+
win32_registry:
384+
dependency: transitive
385+
description:
386+
name: win32_registry
387+
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
388+
url: "https://pub.dev"
389+
source: hosted
390+
version: "2.1.0"
359391
sdks:
360392
dart: ">=3.12.0 <4.0.0"
361393
flutter: ">=3.44.0"

example/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
1919
version: 1.0.0+1
2020

2121
environment:
22-
sdk: ^3.6.0
22+
sdk: ^3.7.0
2323

2424
# Dependencies specify other packages that your package needs in order to work.
2525
# To automatically upgrade your package dependencies to the latest versions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:flutter/material.dart';
2+
3+
/// Collects diagnostics that are scoped to a specific Flutter widget tree.
4+
///
5+
/// The resolved application locale and current view metrics cannot be inferred
6+
/// reliably without a [BuildContext], particularly in multi-view apps.
7+
Map<String, String> collectFeedbackContextDiagnostics(
8+
final BuildContext? context,
9+
) {
10+
if (context == null) {
11+
return const {};
12+
}
13+
14+
final values = <String, String>{};
15+
final appLocale = Localizations.maybeLocaleOf(context);
16+
if (appLocale != null) {
17+
values['App locale'] = appLocale.toLanguageTag();
18+
}
19+
20+
final view = View.maybeOf(context);
21+
if (view != null) {
22+
final devicePixelRatio = view.devicePixelRatio;
23+
final logicalSize = view.physicalSize / devicePixelRatio;
24+
values['Window size (logical pixels)'] =
25+
'${logicalSize.width.round()}x${logicalSize.height.round()}';
26+
values['Device pixel ratio'] = devicePixelRatio.toStringAsFixed(2);
27+
}
28+
29+
return values;
30+
}

lib/src/feedback_email.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'package:flutter/foundation.dart' show visibleForTesting;
2+
import 'package:flutter/widgets.dart' show BuildContext;
23
import 'package:url_launcher/url_launcher.dart';
34

5+
import 'feedback_context_diagnostics.dart';
46
import 'feedback_runtime_diagnostics.dart';
57

68
const Duration _defaultRuntimeDiagnosticsTimeout = Duration(seconds: 2);
@@ -123,12 +125,15 @@ Future<Uri> buildFeedbackEmailUri({
123125
/// allowed URI schemes are reported as successful because the browser does not
124126
/// expose whether the navigation completed. Runtime metadata collection is
125127
/// limited to two seconds; if it fails or times out, the launch is still
126-
/// attempted with an unavailable diagnostic marker.
128+
/// attempted with an unavailable diagnostic marker. When
129+
/// [diagnosticsContext] is provided, the resolved app locale and current view
130+
/// metrics are also included.
127131
Future<bool> sendEmail({
128132
required final String emailAddress,
129133
required final String emailSubject,
130134
final String? emailBody,
131135
final bool includeRuntimeDiagnostics = false,
136+
final BuildContext? diagnosticsContext,
132137
final Map<String, String?> additionalDiagnostics = const {},
133138
final String diagnosticsHeading = 'Diagnostics:',
134139
}) async {
@@ -148,12 +153,21 @@ Future<bool> sendEmail({
148153
return launchUrl(uri);
149154
}
150155

156+
// Context-dependent values must be read before the first asynchronous gap.
157+
// Caller-provided values retain the existing override behavior.
158+
final contextDiagnostics = collectFeedbackContextDiagnostics(
159+
diagnosticsContext,
160+
);
161+
final resolvedAdditionalDiagnostics = <String, String?>{
162+
...contextDiagnostics,
163+
...additionalDiagnostics,
164+
};
151165
final uri = await buildFeedbackEmailUri(
152166
emailAddress: emailAddress,
153167
emailSubject: emailSubject,
154168
emailBody: emailBody,
155169
includeRuntimeDiagnostics: true,
156-
additionalDiagnostics: additionalDiagnostics,
170+
additionalDiagnostics: resolvedAdditionalDiagnostics,
157171
diagnosticsHeading: diagnosticsHeading,
158172
);
159173
// Using the current browsing context keeps web launches working after an

0 commit comments

Comments
 (0)