Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 64 additions & 7 deletions lib/ui/profile/cards.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ class _CardsViewState extends State<CardsView> {
}

Widget buildCardsList() {
final screenReaderActive = MediaQuery.accessibleNavigationOf(context);
var tempView = ReorderableListView(
header: Padding(
padding: const EdgeInsets.only(top: 10),
child: Text("Hold and drag to reorder",
child: Text(screenReaderActive ? "Use the up and down arrows to reorder" : "Hold and drag to reorder",
textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodySmall),
),
children: createList(),
Expand Down Expand Up @@ -74,19 +75,28 @@ class _CardsViewState extends State<CardsView> {
return list;
}

for (String card in _cardsDataProvider.cardOrder) {
try {
// Skip cards that aren't available
if (_cardsDataProvider.availableCards[card] == null) continue;
// Cards actually rendered, in display order - used to know which card is
// first/last so the up/down arrows can be disabled at the boundaries.
final visibleCards =
_cardsDataProvider.cardOrder.where((card) => _cardsDataProvider.availableCards[card] != null).toList();
final screenReaderActive = MediaQuery.accessibleNavigationOf(context);

for (var i = 0; i < visibleCards.length; i++) {
final card = visibleCards[i];
try {
list.add(
Card(
key: Key(card),
elevation: 2.0,
margin: EdgeInsets.fromLTRB(cardMargin, 5, cardMargin, 5),
child: ListTile(
leading: Icon(Icons.drag_handle,
color: Theme.of(context).brightness == Brightness.dark ? linkTextColorDark : linkTextColorLight),
// Dragging to reorder isn't reliably accessible to screen reader
// users, so up/down arrow buttons are shown instead whenever a
// screen reader is active.
leading: screenReaderActive
? _buildReorderArrows(card, visibleCards, isFirst: i == 0, isLast: i == visibleCards.length - 1)
: Icon(Icons.drag_handle,
color: Theme.of(context).brightness == Brightness.dark ? linkTextColorDark : linkTextColorLight),
title: Text(_cardsDataProvider.availableCards[card]!.titleText,
style: Theme.of(context).textTheme.bodyMedium),
trailing: Transform.scale(
Expand Down Expand Up @@ -115,4 +125,51 @@ class _CardsViewState extends State<CardsView> {
}
return list;
}

Widget _buildReorderArrows(String card, List<String> visibleCards, {required bool isFirst, required bool isLast}) {
final title = _cardsDataProvider.availableCards[card]!.titleText;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.arrow_upward),
iconSize: 20,
padding: EdgeInsets.zero,
constraints: BoxConstraints(minWidth: 32, minHeight: 32),
tooltip: 'Move $title up',
onPressed: isFirst ? null : () => _moveCard(card, visibleCards, -1),
),
IconButton(
icon: Icon(Icons.arrow_downward),
iconSize: 20,
padding: EdgeInsets.zero,
constraints: BoxConstraints(minWidth: 32, minHeight: 32),
tooltip: 'Move $title down',
onPressed: isLast ? null : () => _moveCard(card, visibleCards, 1),
),
],
);
}

/// Moves [card] next to the adjacent visible card. The stored order may
/// contain unavailable cards, so raw list indices cannot drive this action.
void _moveCard(String card, List<String> visibleCards, int delta) {
final order = _cardsDataProvider.cardOrder;
final visibleIndex = visibleCards.indexOf(card);
final targetVisibleIndex = visibleIndex + delta;
if (visibleIndex == -1 || targetVisibleIndex < 0 || targetVisibleIndex >= visibleCards.length) return;

final targetCard = visibleCards[targetVisibleIndex];
final currentIndex = order.indexOf(card);
final targetIndex = order.indexOf(targetCard);
if (currentIndex == -1 || targetIndex == -1) return;

setState(() {
order.removeAt(currentIndex);
order.insert(targetIndex, card);
// Checks against stored user order in remote profile
_cardsDataProvider.updateCardOrder(isUserReorder: true);
});
}
}
Loading