showLauncherMenu method
Opens at most one reporting menu/flow, including OS screenshot invocation.
Implementation
Future<void> showLauncherMenu() async {
final context = navigatorKey.currentContext;
if (!_active || isPresentingUi || context == null || !_foreground) return;
_launcherOpen = true;
_generation++;
_notifyUi();
try {
final action = await showModalBottomSheet<String>(
context: context,
routeSettings: _ReporterRouteSettings(this),
isScrollControlled: true,
sheetAnimationStyle: AnimationStyle.noAnimation,
builder: (sheetContext) => Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.viewInsetsOf(sheetContext).bottom,
),
child: SafeArea(
top: false,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.edit_note),
title: const Text('Report an issue'),
subtitle: const Text(
'Review your screenshot and notes before sending.',
),
onTap: () => Navigator.pop(sheetContext, 'report'),
),
ListTile(
leading: Icon(
_recording
? Icons.stop_circle_outlined
: Icons.play_circle_outline,
),
title: Text(
_recording
? 'Stop and discard replay'
: 'Start replay recording',
),
subtitle: const Text(
'Recording requires separate consent. No audio is captured.',
),
onTap: () => Navigator.pop(
sheetContext,
_recording ? 'stop' : 'record',
),
),
],
),
),
),
),
);
WidgetsBinding.instance.scheduleFrame();
await WidgetsBinding.instance.endOfFrame;
if (!_active || _disposed || !context.mounted) return;
_launcherOpen = false;
switch (action) {
case 'report':
await showReport();
case 'record':
await requestRecording(context);
case 'stop':
stopRecording();
}
} finally {
_launcherOpen = false;
_notifyUi();
}
}