fix(mobile): group section view controls with readable selection

Replace disconnected chips with full-width segments, tighten the sheet header and give Apply clear contrast. Stack choices at enlarged text sizes and keep draft controls scrollable.

Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-10-19 15:10:52 -07:00
parent d9b2f7bb8c
commit 00ea385538
2 changed files with 103 additions and 49 deletions

View File

@@ -137,22 +137,50 @@ class _SectionViewSheet extends HookConsumerWidget {
children: [
Text(label, style: context.textTheme.titleSmall),
const SizedBox(height: Grid.xxs),
Wrap(
spacing: Grid.xxs,
runSpacing: Grid.xxs,
children: [
SegmentedButton<T>(
segments: [
for (final entry in values.entries)
ChoiceChip(
label: Text(entry.value),
selected: entry.key == selected,
onSelected: change == null
? null
: (_) {
dirty.value = true;
change(entry.key);
},
),
ButtonSegment(value: entry.key, label: Text(entry.value)),
],
selected: {selected},
showSelectedIcon: false,
direction: MediaQuery.textScalerOf(context).scale(14) > 20
? Axis.vertical
: Axis.horizontal,
expandedInsets: MediaQuery.textScalerOf(context).scale(14) > 20
? null
: EdgeInsets.zero,
style: ButtonStyle(
foregroundColor: WidgetStateProperty.resolveWith(
(states) => states.contains(WidgetState.disabled)
? context.colors.onSurface.withValues(alpha: 0.5)
: states.contains(WidgetState.selected)
? context.colors.onSurface
: context.colors.onSurfaceVariant,
),
backgroundColor: WidgetStateProperty.resolveWith(
(states) => states.contains(WidgetState.selected)
? context.colors.surface
: context.colors.onSurface.withValues(alpha: 0.06),
),
side: WidgetStatePropertyAll(
BorderSide(color: context.colors.outline.withValues(alpha: 0.25)),
),
shape: WidgetStatePropertyAll(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(Radii.sm),
),
),
padding: const WidgetStatePropertyAll(
EdgeInsets.symmetric(horizontal: Grid.xxs, vertical: Grid.twelve),
),
),
onSelectionChanged: change == null
? null
: (selection) {
dirty.value = true;
change(selection.single);
},
),
const SizedBox(height: Grid.xs),
],
@@ -165,14 +193,24 @@ class _SectionViewSheet extends HookConsumerWidget {
maxHeight: MediaQuery.sizeOf(context).height * 0.85,
),
child: Padding(
padding: const EdgeInsets.all(Grid.xs),
padding: const EdgeInsets.fromLTRB(
Grid.gutter,
Grid.xxs,
Grid.gutter,
Grid.twelve,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Expanded(
child: Text(title, style: context.textTheme.titleLarge),
child: Text(
title,
style: context.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
),
IconButton(
tooltip: 'Close',
@@ -259,28 +297,35 @@ class _SectionViewSheet extends HookConsumerWidget {
),
),
const SizedBox(height: Grid.xxs),
Wrap(
alignment: WrapAlignment.end,
spacing: Grid.xxs,
runSpacing: Grid.xxs,
children: [
TextButton(
onPressed: saving.value ? null : () => close(),
child: const Text('Cancel'),
),
FilledButton(
onPressed: saving.value || manager?.busy == true
? null
: apply,
child: Text(
saving.value
? 'Saving…'
: failed.value
? 'Retry'
: 'Apply',
Align(
alignment: Alignment.centerRight,
child: Wrap(
alignment: WrapAlignment.end,
spacing: Grid.xxs,
runSpacing: Grid.xxs,
children: [
TextButton(
onPressed: saving.value ? null : () => close(),
child: const Text('Cancel'),
),
),
],
FilledButton(
style: FilledButton.styleFrom(
backgroundColor: context.colors.onSurface,
foregroundColor: context.colors.surface,
),
onPressed: saving.value || manager?.busy == true
? null
: apply,
child: Text(
saving.value
? 'Saving…'
: failed.value
? 'Retry'
: 'Apply',
),
),
],
),
),
],
),

View File

@@ -1,3 +1,4 @@
import 'package:buzz/features/channels/channel_sort/channel_sort_storage.dart';
import 'package:nostr/nostr.dart' as nostr;
import 'package:buzz/features/channels/channel_filter/channel_filter_provider.dart';
import 'package:buzz/features/channels/channel_filter/channel_filter_manager.dart';
@@ -225,10 +226,13 @@ void main() {
await tester.tap(find.byTooltip('Channels options'));
await tester.pumpAndSettle();
expect(find.text('general'), findsNothing);
final all = tester.widget<ChoiceChip>(
find.ancestor(of: find.text('All'), matching: find.byType(ChoiceChip)),
final all = tester.widget<SegmentedButton<ChannelFilterMode>>(
find.ancestor(
of: find.text('All'),
matching: find.byType(SegmentedButton<ChannelFilterMode>),
),
);
expect(all.onSelected, isNull);
expect(all.onSelectionChanged, isNull);
await tester.tap(find.text('Cancel'));
await tester.pumpAndSettle();
await tester.tap(find.text('Show hidden channels · 1'));
@@ -258,6 +262,7 @@ void main() {
await tester.pumpAndSettle();
await tester.tap(find.text('Active'));
await tester.pumpAndSettle();
await tester.ensureVisible(find.text('7 days'));
await tester.tap(find.text('7 days'));
await tester.pumpAndSettle();
expect(manager.store.modeFor('channels'), ChannelFilterMode.all);
@@ -267,13 +272,13 @@ void main() {
expect(manager.store.windowFor('channels'), 7 * 86400);
await tester.tap(find.byTooltip('Channels options'));
await tester.pumpAndSettle();
final days = tester.widget<ChoiceChip>(
final days = tester.widget<SegmentedButton<int>>(
find.ancestor(
of: find.text('7 days'),
matching: find.byType(ChoiceChip),
matching: find.byType(SegmentedButton<int>),
),
);
expect(days.selected, isTrue);
expect(days.selected, {7});
await tester.tap(find.text('All'));
await tester.tap(find.text('Cancel'));
await tester.pumpAndSettle();
@@ -306,6 +311,7 @@ void main() {
await tester.pumpAndSettle();
await tester.tap(find.text('Active'));
await tester.pumpAndSettle();
await tester.ensureVisible(find.text('7 days'));
await tester.tap(find.text('7 days'));
await tester.pumpAndSettle();
await tester.ensureVisible(find.text('Apply'));
@@ -317,13 +323,13 @@ void main() {
),
findsOneWidget,
);
final selected = tester.widget<ChoiceChip>(
final selected = tester.widget<SegmentedButton<int>>(
find.ancestor(
of: find.text('7 days'),
matching: find.byType(ChoiceChip),
matching: find.byType(SegmentedButton<int>),
),
);
expect(selected.selected, isTrue);
expect(selected.selected, {7});
await tester.tap(find.text('Cancel'));
await tester.pumpAndSettle();
expect(find.byType(AlertDialog), findsNothing);
@@ -469,10 +475,13 @@ void main() {
expect(find.text('A–Z'), findsOneWidget);
expect(find.text('Show'), findsOneWidget);
expect(find.byType(AlertDialog), findsNothing);
final alpha = tester.widget<ChoiceChip>(
find.ancestor(of: find.text('A–Z'), matching: find.byType(ChoiceChip)),
final alpha = tester.widget<SegmentedButton<ChannelSortMode>>(
find.ancestor(
of: find.text('A–Z'),
matching: find.byType(SegmentedButton<ChannelSortMode>),
),
);
expect(alpha.selected, isTrue);
expect(alpha.selected, {ChannelSortMode.alpha});
await tester.tap(find.text('Cancel'));
await tester.pumpAndSettle();