Update contacts tab widgets

This commit is contained in:
Janez T
2026-03-13 10:25:23 +01:00
parent 1e70f52069
commit 77e43843bf
5 changed files with 525 additions and 246 deletions

View File

@@ -401,13 +401,20 @@ class _ContactsTabState extends State<ContactsTab> {
); );
} }
/// Show the add channel dialog /// Show the add channel sheet
Future<void> _showAddChannelDialog(BuildContext context) async { Future<void> _showAddChannelDialog(BuildContext context) async {
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
await showDialog( await showModalBottomSheet(
context: context, context: context,
builder: (context) => AddChannelDialog( isScrollControlled: true,
showDragHandle: true,
builder: (context) => SafeArea(
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: AddChannelSheet(
onCreateChannel: (name, secret) async { onCreateChannel: (name, secret) async {
final connectionProvider = context.read<ConnectionProvider>(); final connectionProvider = context.read<ConnectionProvider>();
try { try {
@@ -437,6 +444,8 @@ class _ContactsTabState extends State<ContactsTab> {
} }
}, },
), ),
),
),
); );
} }
@@ -627,6 +636,13 @@ class _ContactsTabState extends State<ContactsTab> {
_showSavedGroupsForSection(ContactSection.repeaters) _showSavedGroupsForSection(ContactSection.repeaters)
? savedRepeaterGroups ? savedRepeaterGroups
: const <_RenderedSavedGroup>[]; : const <_RenderedSavedGroup>[];
final ungroupedRepeaters = _excludeGroupedContacts(
repeaters,
visibleSavedRepeaterGroups,
);
final showRepeatersOthersGroup =
visibleSavedRepeaterGroups.length > 1 &&
ungroupedRepeaters.isNotEmpty;
final rooms = _filterContactsForSection( final rooms = _filterContactsForSection(
allRooms, allRooms,
ContactSection.rooms, ContactSection.rooms,
@@ -783,11 +799,22 @@ class _ContactsTabState extends State<ContactsTab> {
visibleSavedRepeaterGroups, visibleSavedRepeaterGroups,
ContactSection.repeaters, ContactSection.repeaters,
), ),
if (showRepeatersOthersGroup)
_InferredContactGroupCard(
label: 'Others',
contacts: ungroupedRepeaters,
kindLabel: 'Auto group',
compactContacts: true,
currentPosition: _currentPosition,
calculateDistance: _calculateDistanceInMeters,
formatDistance: _formatDistance,
onNavigateToMap: widget.onNavigateToMap,
onNavigateToMessages: widget.onNavigateToMessages,
)
else
..._buildContactSectionItems( ..._buildContactSectionItems(
_excludeGroupedContacts( ungroupedRepeaters,
repeaters, compact: true,
visibleSavedRepeaterGroups,
),
), ),
const Divider(height: 32), const Divider(height: 32),
], ],
@@ -894,11 +921,15 @@ class _ContactsTabState extends State<ContactsTab> {
); );
} }
List<Widget> _buildContactSectionItems(List<Contact> contacts) { List<Widget> _buildContactSectionItems(
List<Contact> contacts, {
bool compact = false,
}) {
return contacts return contacts
.map( .map(
(contact) => ContactTile( (contact) => ContactTile(
contact: contact, contact: contact,
compact: compact,
currentPosition: _currentPosition, currentPosition: _currentPosition,
calculateDistance: _calculateDistanceInMeters, calculateDistance: _calculateDistanceInMeters,
formatDistance: _formatDistance, formatDistance: _formatDistance,
@@ -932,6 +963,7 @@ class _ContactsTabState extends State<ContactsTab> {
(group) => _InferredContactGroupCard( (group) => _InferredContactGroupCard(
label: group.group.label, label: group.group.label,
contacts: group.contacts, contacts: group.contacts,
compactContacts: section == ContactSection.repeaters,
currentPosition: _currentPosition, currentPosition: _currentPosition,
calculateDistance: _calculateDistanceInMeters, calculateDistance: _calculateDistanceInMeters,
formatDistance: _formatDistance, formatDistance: _formatDistance,
@@ -1317,6 +1349,7 @@ class _InferredContactGroupCard extends StatelessWidget {
final String label; final String label;
final List<Contact> contacts; final List<Contact> contacts;
final String? kindLabel; final String? kindLabel;
final bool compactContacts;
final Position? currentPosition; final Position? currentPosition;
final double Function(double, double, double, double) calculateDistance; final double Function(double, double, double, double) calculateDistance;
final String Function(double) formatDistance; final String Function(double) formatDistance;
@@ -1328,6 +1361,7 @@ class _InferredContactGroupCard extends StatelessWidget {
required this.label, required this.label,
required this.contacts, required this.contacts,
this.kindLabel, this.kindLabel,
this.compactContacts = false,
required this.currentPosition, required this.currentPosition,
required this.calculateDistance, required this.calculateDistance,
required this.formatDistance, required this.formatDistance,
@@ -1414,6 +1448,7 @@ class _InferredContactGroupCard extends StatelessWidget {
(contact) => ContactTile( (contact) => ContactTile(
contact: contact, contact: contact,
groupLabel: label, groupLabel: label,
compact: compactContacts,
currentPosition: currentPosition, currentPosition: currentPosition,
calculateDistance: calculateDistance, calculateDistance: calculateDistance,
formatDistance: formatDistance, formatDistance: formatDistance,

View File

@@ -1,20 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../l10n/app_localizations.dart'; import '../../l10n/app_localizations.dart';
/// Dialog for adding a new channel /// Bottom sheet for adding a new channel
class AddChannelDialog extends StatefulWidget { class AddChannelSheet extends StatefulWidget {
final Future<void> Function(String name, String secret) onCreateChannel; final Future<void> Function(String name, String secret) onCreateChannel;
const AddChannelDialog({ const AddChannelSheet({super.key, required this.onCreateChannel});
super.key,
required this.onCreateChannel,
});
@override @override
State<AddChannelDialog> createState() => _AddChannelDialogState(); State<AddChannelSheet> createState() => _AddChannelSheetState();
} }
class _AddChannelDialogState extends State<AddChannelDialog> { class _AddChannelSheetState extends State<AddChannelSheet> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController(); final _nameController = TextEditingController();
final _secretController = TextEditingController(); final _secretController = TextEditingController();
@@ -109,21 +106,46 @@ class _AddChannelDialogState extends State<AddChannelDialog> {
final normalizedName = _nameController.text.trimLeft(); final normalizedName = _nameController.text.trimLeft();
final isHashChannel = normalizedName.startsWith('#'); final isHashChannel = normalizedName.startsWith('#');
return AlertDialog( return ConstrainedBox(
title: Text(l10n.addChannel), constraints: BoxConstraints(
content: SingleChildScrollView( maxHeight: MediaQuery.of(context).size.height * 0.85,
),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 20),
child: Form( child: Form(
key: _formKey, key: _formKey,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Info banner explaining channel types Row(
children: [
Expanded(
child: Text(
l10n.addChannel,
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w700,
),
),
),
IconButton(
onPressed: _isCreating
? null
: () => Navigator.of(context).pop(),
icon: const Icon(Icons.close),
tooltip: l10n.close,
),
],
),
const SizedBox(height: 8),
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer.withValues(alpha: 0.3), color: theme.colorScheme.primaryContainer.withValues(
borderRadius: BorderRadius.circular(8), alpha: 0.3,
),
borderRadius: BorderRadius.circular(12),
border: Border.all( border: Border.all(
color: theme.colorScheme.primary.withValues(alpha: 0.3), color: theme.colorScheme.primary.withValues(alpha: 0.3),
), ),
@@ -149,8 +171,6 @@ class _AddChannelDialogState extends State<AddChannelDialog> {
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
// Channel Name Field
TextFormField( TextFormField(
controller: _nameController, controller: _nameController,
decoration: InputDecoration( decoration: InputDecoration(
@@ -168,14 +188,13 @@ class _AddChannelDialogState extends State<AddChannelDialog> {
textInputAction: isHashChannel textInputAction: isHashChannel
? TextInputAction.done ? TextInputAction.done
: TextInputAction.next, : TextInputAction.next,
onChanged: (_) => setState(() {}), // Rebuild to update icon onChanged: (_) => setState(() {}),
onFieldSubmitted: (_) { onFieldSubmitted: (_) {
if (isHashChannel) { if (isHashChannel) {
_handleCreate(); _handleCreate();
} }
}, },
), ),
// Channel Secret Field (only show for private channels)
if (!isHashChannel) ...[ if (!isHashChannel) ...[
const SizedBox(height: 16), const SizedBox(height: 16),
TextFormField( TextFormField(
@@ -193,7 +212,6 @@ class _AddChannelDialogState extends State<AddChannelDialog> {
onFieldSubmitted: (_) => _handleCreate(), onFieldSubmitted: (_) => _handleCreate(),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
// Help Text for private channels
Text( Text(
l10n.channelSecretHelp, l10n.channelSecretHelp,
style: theme.textTheme.bodySmall?.copyWith( style: theme.textTheme.bodySmall?.copyWith(
@@ -201,15 +219,15 @@ class _AddChannelDialogState extends State<AddChannelDialog> {
), ),
), ),
], ],
// Help Text for hash channels
if (isHashChannel) ...[ if (isHashChannel) ...[
const SizedBox(height: 8), const SizedBox(height: 8),
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer.withValues(alpha: 0.5), color: theme.colorScheme.primaryContainer.withValues(
borderRadius: BorderRadius.circular(8), alpha: 0.5,
),
borderRadius: BorderRadius.circular(12),
), ),
child: Row( child: Row(
children: [ children: [
@@ -232,29 +250,39 @@ class _AddChannelDialogState extends State<AddChannelDialog> {
), ),
), ),
], ],
], const SizedBox(height: 20),
), Row(
), children: [
), Expanded(
actions: [ child: OutlinedButton(
// Cancel Button onPressed: _isCreating
TextButton( ? null
onPressed: _isCreating ? null : () => Navigator.of(context).pop(), : () => Navigator.of(context).pop(),
child: Text(l10n.cancel), child: Text(l10n.cancel),
), ),
),
// Create Button const SizedBox(width: 12),
FilledButton( Expanded(
child: FilledButton(
onPressed: _isCreating ? null : _handleCreate, onPressed: _isCreating ? null : _handleCreate,
child: _isCreating child: _isCreating
? const SizedBox( ? const SizedBox(
width: 16, width: 16,
height: 16, height: 16,
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator(
strokeWidth: 2,
),
) )
: Text(l10n.createChannel), : Text(l10n.createChannel),
), ),
),
], ],
),
],
),
),
),
),
); );
} }
} }

View File

@@ -22,6 +22,7 @@ import '../../l10n/app_localizations.dart';
class ContactTile extends StatelessWidget { class ContactTile extends StatelessWidget {
final Contact contact; final Contact contact;
final String? groupLabel; final String? groupLabel;
final bool compact;
final Position? currentPosition; final Position? currentPosition;
final double Function(double, double, double, double)? calculateDistance; final double Function(double, double, double, double)? calculateDistance;
final String Function(double)? formatDistance; final String Function(double)? formatDistance;
@@ -32,6 +33,7 @@ class ContactTile extends StatelessWidget {
super.key, super.key,
required this.contact, required this.contact,
this.groupLabel, this.groupLabel,
this.compact = false,
this.currentPosition, this.currentPosition,
this.calculateDistance, this.calculateDistance,
this.formatDistance, this.formatDistance,
@@ -127,7 +129,9 @@ class ContactTile extends StatelessWidget {
: colorScheme.onSurfaceVariant, : colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
); );
final Widget subtitleWidget = Column( final Widget subtitleWidget = compact
? _buildCompactSubtitle(context, distanceText)
: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 4), const SizedBox(height: 4),
@@ -294,6 +298,34 @@ class ContactTile extends StatelessWidget {
); );
} }
Widget _buildCompactSubtitle(BuildContext context, String? distanceText) {
final location = contact.displayLocation;
final compactPills = <Widget>[
if (groupLabel case final label?)
_buildMetaPill(context, icon: Icons.folder_copy_outlined, label: label),
_buildMetaPill(
context,
icon: Icons.key_outlined,
label: contact.publicKeyShort,
monospace: true,
),
if (distanceText != null) _buildDistancePill(context, distanceText),
if (contact.routeHasPath && contact.routeHopCount > 0)
_buildRoutePill(context, contact),
if (location == null)
_buildMetaPill(
context,
icon: Icons.location_disabled_outlined,
label: AppLocalizations.of(context)!.noGpsData,
),
];
return Padding(
padding: const EdgeInsets.only(top: 4),
child: Wrap(spacing: 6, runSpacing: 6, children: compactPills),
);
}
void _handlePrimaryTap(BuildContext context, Contact contact) { void _handlePrimaryTap(BuildContext context, Contact contact) {
_showContactActionSheet(context, contact); _showContactActionSheet(context, contact);
} }
@@ -570,47 +602,26 @@ class ContactTile extends StatelessWidget {
BuildContext context, BuildContext context,
Contact contact, Contact contact,
) async { ) async {
final controller = TextEditingController(text: contact.nameOverride ?? '');
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
final result = await showModalBottomSheet<String?>(
final result = await showDialog<String?>(
context: context, context: context,
builder: (dialogContext) => AlertDialog( isScrollControlled: true,
title: const Text('Edit name'), showDragHandle: true,
content: Column( builder: (dialogContext) => SafeArea(
mainAxisSize: MainAxisSize.min, child: Padding(
crossAxisAlignment: CrossAxisAlignment.start, padding: EdgeInsets.only(
children: [ bottom: MediaQuery.of(dialogContext).viewInsets.bottom,
TextField(
controller: controller,
autofocus: true,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
labelText: 'Custom name',
hintText: contact.advName,
helperText: 'Leave blank to use the advertised name.',
), ),
onSubmitted: (value) { child: _ContactNameOverrideSheet(
Navigator.of(dialogContext).pop(value); initialValue: contact.nameOverride ?? '',
}, advertisedName: contact.advName,
cancelLabel: l10n.cancel,
saveLabel: l10n.save,
), ),
],
), ),
actions: [
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(),
child: Text(l10n.cancel),
),
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(controller.text),
child: Text(l10n.save),
),
],
), ),
); );
controller.dispose();
if (result == null || !context.mounted) { if (result == null || !context.mounted) {
return; return;
} }
@@ -980,3 +991,105 @@ class ContactTile extends StatelessWidget {
); );
} }
} }
class _ContactNameOverrideSheet extends StatefulWidget {
final String initialValue;
final String advertisedName;
final String cancelLabel;
final String saveLabel;
const _ContactNameOverrideSheet({
required this.initialValue,
required this.advertisedName,
required this.cancelLabel,
required this.saveLabel,
});
@override
State<_ContactNameOverrideSheet> createState() =>
_ContactNameOverrideSheetState();
}
class _ContactNameOverrideSheetState extends State<_ContactNameOverrideSheet> {
late final TextEditingController _controller;
@override
void initState() {
super.initState();
_controller = TextEditingController(text: widget.initialValue);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _submit() {
Navigator.of(context).pop(_controller.text);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
'Edit name',
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w700,
),
),
),
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.close),
),
],
),
const SizedBox(height: 8),
TextField(
controller: _controller,
autofocus: true,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
labelText: 'Custom name',
hintText: widget.advertisedName,
helperText: 'Leave blank to use the advertised name.',
border: const OutlineInputBorder(),
),
onSubmitted: (_) => _submit(),
),
const SizedBox(height: 20),
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(widget.cancelLabel),
),
),
const SizedBox(width: 12),
Expanded(
child: FilledButton(
onPressed: _submit,
child: Text(widget.saveLabel),
),
),
],
),
],
),
),
);
}
}

View File

@@ -4,11 +4,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:meshcore_sar_app/l10n/app_localizations.dart'; import 'package:meshcore_sar_app/l10n/app_localizations.dart';
import 'package:meshcore_sar_app/models/contact.dart'; import 'package:meshcore_sar_app/models/contact.dart';
import 'package:meshcore_sar_app/models/contact_group.dart';
import 'package:meshcore_sar_app/providers/connection_provider.dart'; import 'package:meshcore_sar_app/providers/connection_provider.dart';
import 'package:meshcore_sar_app/providers/contacts_provider.dart'; import 'package:meshcore_sar_app/providers/contacts_provider.dart';
import 'package:meshcore_sar_app/providers/map_provider.dart'; import 'package:meshcore_sar_app/providers/map_provider.dart';
import 'package:meshcore_sar_app/providers/messages_provider.dart'; import 'package:meshcore_sar_app/providers/messages_provider.dart';
import 'package:meshcore_sar_app/screens/contacts_tab.dart'; import 'package:meshcore_sar_app/screens/contacts_tab.dart';
import 'package:meshcore_sar_app/utils/contact_grouping.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@@ -36,13 +38,39 @@ void main() {
); );
} }
Contact buildRepeater({required int seed, required String name}) {
final publicKey = Uint8List(32);
publicKey[0] = seed;
publicKey[1] = seed + 1;
return Contact(
publicKey: publicKey,
type: ContactType.repeater,
flags: 0,
outPathLen: -1,
outPath: Uint8List(0),
advName: name,
lastAdvert: DateTime.now().millisecondsSinceEpoch ~/ 1000,
advLat: 46056000 + seed,
advLon: 14505000 + seed,
lastMod: DateTime.now().millisecondsSinceEpoch ~/ 1000,
);
}
Future<void> pumpContactsTab( Future<void> pumpContactsTab(
WidgetTester tester, { WidgetTester tester, {
required List<Contact> channels, List<Contact> contacts = const [],
List<SavedContactGroup> savedGroups = const [],
}) async { }) async {
final contactsProvider = ContactsProvider(); final contactsProvider = ContactsProvider();
for (final channel in channels) { for (final contact in contacts) {
contactsProvider.addOrUpdateContact(channel); contactsProvider.addOrUpdateContact(contact);
}
if (savedGroups.isNotEmpty) {
await contactsProvider.replaceAutoGroupsForSection(
'repeaters',
savedGroups,
);
} }
await tester.pumpWidget( await tester.pumpWidget(
@@ -69,7 +97,7 @@ void main() {
) async { ) async {
await pumpContactsTab( await pumpContactsTab(
tester, tester,
channels: [buildChannel(name: 'Ops', channelIndex: 3)], contacts: [buildChannel(name: 'Ops', channelIndex: 3)],
); );
expect(find.text('Ops'), findsOneWidget); expect(find.text('Ops'), findsOneWidget);
@@ -89,4 +117,79 @@ void main() {
findsOneWidget, findsOneWidget,
); );
}); });
testWidgets('repeaters show Others group when multiple groups exist', (
tester,
) async {
final repeaters = [
buildRepeater(seed: 10, name: 'AL-1'),
buildRepeater(seed: 11, name: 'AL-2'),
buildRepeater(seed: 12, name: 'AL-3'),
buildRepeater(seed: 13, name: 'AL-4'),
buildRepeater(seed: 20, name: 'BR-1'),
buildRepeater(seed: 21, name: 'BR-2'),
buildRepeater(seed: 22, name: 'BR-3'),
buildRepeater(seed: 23, name: 'BR-4'),
buildRepeater(seed: 30, name: 'Lone Relay'),
];
final inferredGroups = ContactGrouping.inferGroups(repeaters);
final savedGroups = inferredGroups
.map(
(group) => SavedContactGroup(
id: 'repeaters_${group.key}',
sectionKey: 'repeaters',
label: group.label,
query: group.label,
createdAt: DateTime(2026, 3, 13, 10),
matchPrefixes: group.matchPrefixes,
isAutoGroup: true,
),
)
.toList();
await pumpContactsTab(
tester,
contacts: repeaters,
savedGroups: savedGroups,
);
expect(find.text('AL-'), findsOneWidget);
expect(find.text('BR-'), findsOneWidget);
expect(find.text('Others'), findsOneWidget);
expect(find.text('Lone Relay'), findsNothing);
});
testWidgets('repeaters stay flat when only one group exists', (tester) async {
final repeaters = [
buildRepeater(seed: 40, name: 'AL-1'),
buildRepeater(seed: 41, name: 'AL-2'),
buildRepeater(seed: 42, name: 'AL-3'),
buildRepeater(seed: 43, name: 'AL-4'),
buildRepeater(seed: 50, name: 'Lone Relay'),
];
final inferredGroups = ContactGrouping.inferGroups(repeaters);
final savedGroups = inferredGroups
.map(
(group) => SavedContactGroup(
id: 'repeaters_${group.key}',
sectionKey: 'repeaters',
label: group.label,
query: group.label,
createdAt: DateTime(2026, 3, 13, 10),
matchPrefixes: group.matchPrefixes,
isAutoGroup: true,
),
)
.toList();
await pumpContactsTab(
tester,
contacts: repeaters,
savedGroups: savedGroups,
);
expect(find.text('AL-'), findsOneWidget);
expect(find.text('Others'), findsNothing);
expect(find.text('Lone Relay'), findsOneWidget);
});
} }

View File

@@ -4,7 +4,7 @@ import 'package:meshcore_sar_app/l10n/app_localizations.dart';
import 'package:meshcore_sar_app/widgets/contacts/add_channel_dialog.dart'; import 'package:meshcore_sar_app/widgets/contacts/add_channel_dialog.dart';
void main() { void main() {
Future<void> pumpDialog( Future<void> pumpSheet(
WidgetTester tester, { WidgetTester tester, {
required Future<void> Function(String name, String secret) onCreateChannel, required Future<void> Function(String name, String secret) onCreateChannel,
}) async { }) async {
@@ -15,7 +15,7 @@ void main() {
home: Scaffold( home: Scaffold(
body: Builder( body: Builder(
builder: (context) => Center( builder: (context) => Center(
child: AddChannelDialog(onCreateChannel: onCreateChannel), child: AddChannelSheet(onCreateChannel: onCreateChannel),
), ),
), ),
), ),
@@ -27,7 +27,7 @@ void main() {
String? submittedName; String? submittedName;
String? submittedSecret; String? submittedSecret;
await pumpDialog( await pumpSheet(
tester, tester,
onCreateChannel: (name, secret) async { onCreateChannel: (name, secret) async {
submittedName = name; submittedName = name;
@@ -47,7 +47,7 @@ void main() {
testWidgets('uses done action for hash channels', (tester) async { testWidgets('uses done action for hash channels', (tester) async {
String? submittedName; String? submittedName;
await pumpDialog( await pumpSheet(
tester, tester,
onCreateChannel: (name, secret) async { onCreateChannel: (name, secret) async {
submittedName = name; submittedName = name;