further improved accessibility across the application and add tooltips to action buttons
This commit is contained in:
@@ -18,6 +18,14 @@ class LoginErrorBanner extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
final showDetails = details != null
|
||||||
|
? () => InfoDialog.show(
|
||||||
|
context,
|
||||||
|
details!,
|
||||||
|
copyable: true,
|
||||||
|
title: 'Fehlerdetails',
|
||||||
|
)
|
||||||
|
: null;
|
||||||
return AnimatedSize(
|
return AnimatedSize(
|
||||||
duration: const Duration(milliseconds: 180),
|
duration: const Duration(milliseconds: 180),
|
||||||
curve: Curves.easeOut,
|
curve: Curves.easeOut,
|
||||||
@@ -25,18 +33,20 @@ class LoginErrorBanner extends StatelessWidget {
|
|||||||
? const SizedBox(height: 0, width: double.infinity)
|
? const SizedBox(height: 0, width: double.infinity)
|
||||||
: Padding(
|
: Padding(
|
||||||
padding: const EdgeInsets.only(top: 14),
|
padding: const EdgeInsets.only(top: 14),
|
||||||
|
child: Semantics(
|
||||||
|
button: showDetails != null,
|
||||||
|
label: showDetails != null
|
||||||
|
? '${message!}, Fehlerdetails anzeigen'
|
||||||
|
: message,
|
||||||
|
onTap: showDetails,
|
||||||
|
child: ExcludeSemantics(
|
||||||
child: Material(
|
child: Material(
|
||||||
color: theme.colorScheme.errorContainer.withValues(alpha: 0.6),
|
color: theme.colorScheme.errorContainer.withValues(
|
||||||
|
alpha: 0.6,
|
||||||
|
),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: details != null
|
onTap: showDetails,
|
||||||
? () => InfoDialog.show(
|
|
||||||
context,
|
|
||||||
details!,
|
|
||||||
copyable: true,
|
|
||||||
title: 'Fehlerdetails',
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@@ -76,6 +86,8 @@ class LoginErrorBanner extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class FilesSortActions extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
PopupMenuButton<bool>(
|
PopupMenuButton<bool>(
|
||||||
|
tooltip:
|
||||||
|
'Sortierrichtung: ${ascending ? 'aufsteigend' : 'absteigend'}',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
ascending ? Icons.text_rotate_up : Icons.text_rotation_down,
|
ascending ? Icons.text_rotate_up : Icons.text_rotation_down,
|
||||||
),
|
),
|
||||||
@@ -47,6 +49,8 @@ class FilesSortActions extends StatelessWidget {
|
|||||||
onSelected: onDirectionChanged,
|
onSelected: onDirectionChanged,
|
||||||
),
|
),
|
||||||
PopupMenuButton<SortOption>(
|
PopupMenuButton<SortOption>(
|
||||||
|
tooltip:
|
||||||
|
'Sortieren nach: ${SortOptions.getOption(currentSort).displayName}',
|
||||||
icon: const Icon(Icons.sort),
|
icon: const Icon(Icons.sort),
|
||||||
itemBuilder: (context) => SortOptions.options.keys
|
itemBuilder: (context) => SortOptions.options.keys
|
||||||
.map(
|
.map(
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class GradeAveragesView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuButton<bool>(
|
PopupMenuButton<bool>(
|
||||||
|
tooltip:
|
||||||
|
'Notensystem: ${bloc.isMiddleSchool() ? 'Realschule' : 'Oberstufe'}',
|
||||||
initialValue: bloc.isMiddleSchool(),
|
initialValue: bloc.isMiddleSchool(),
|
||||||
icon: const Icon(Icons.more_horiz),
|
icon: const Icon(Icons.more_horiz),
|
||||||
itemBuilder: (context) => [true, false]
|
itemBuilder: (context) => [true, false]
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ class HolidaysView extends StatelessWidget {
|
|||||||
onPressed: showDisclaimer,
|
onPressed: showDisclaimer,
|
||||||
),
|
),
|
||||||
PopupMenuButton<bool>(
|
PopupMenuButton<bool>(
|
||||||
|
tooltip:
|
||||||
|
'Vergangene Ferien ${bloc.showPastHolidays() ? 'ausblenden' : 'anzeigen'}',
|
||||||
initialValue: bloc.showPastHolidays(),
|
initialValue: bloc.showPastHolidays(),
|
||||||
icon: const Icon(Icons.history),
|
icon: const Icon(Icons.history),
|
||||||
itemBuilder: (context) => [true, false]
|
itemBuilder: (context) => [true, false]
|
||||||
@@ -82,9 +84,7 @@ class HolidaysView extends StatelessWidget {
|
|||||||
text: 'Keine Schulferien verfügbar',
|
text: 'Keine Schulferien verfügbar',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ListViewUtil.fromList<McHoliday>(
|
return ListViewUtil.fromList<McHoliday>(holidays, (holiday) {
|
||||||
holidays,
|
|
||||||
(holiday) {
|
|
||||||
String holidayYear() {
|
String holidayYear() {
|
||||||
final startYear = holiday.startDate.year;
|
final startYear = holiday.startDate.year;
|
||||||
final endYear = holiday.endDate.year;
|
final endYear = holiday.endDate.year;
|
||||||
@@ -94,9 +94,7 @@ class HolidaysView extends StatelessWidget {
|
|||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.calendar_month)),
|
leading: const CenteredLeading(Icon(Icons.calendar_month)),
|
||||||
title: Text(
|
title: Text('${holiday.longName} ${holidayYear()}'),
|
||||||
'${holiday.longName} ${holidayYear()}',
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'${holiday.startDate.formatDate()} - ${holiday.endDate.formatDate()}',
|
'${holiday.startDate.formatDate()} - ${holiday.endDate.formatDate()}',
|
||||||
),
|
),
|
||||||
@@ -148,8 +146,7 @@ class HolidaysView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ class MarianumDatesView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) =>
|
Widget build(
|
||||||
BlocModule<MarianumDatesBloc, LoadableState<MarianumDatesState>>(
|
BuildContext context,
|
||||||
|
) => BlocModule<MarianumDatesBloc, LoadableState<MarianumDatesState>>(
|
||||||
create: (context) => MarianumDatesBloc(),
|
create: (context) => MarianumDatesBloc(),
|
||||||
autoRebuild: true,
|
autoRebuild: true,
|
||||||
child: (context, bloc, state) => Scaffold(
|
child: (context, bloc, state) => Scaffold(
|
||||||
@@ -41,6 +42,8 @@ class MarianumDatesView extends StatelessWidget {
|
|||||||
title: const Text('Marianum Termine'),
|
title: const Text('Marianum Termine'),
|
||||||
actions: [
|
actions: [
|
||||||
PopupMenuButton<bool>(
|
PopupMenuButton<bool>(
|
||||||
|
tooltip:
|
||||||
|
'Vergangene Termine ${bloc.showPastEvents() ? 'ausblenden' : 'anzeigen'}',
|
||||||
initialValue: bloc.showPastEvents(),
|
initialValue: bloc.showPastEvents(),
|
||||||
icon: const Icon(Icons.history),
|
icon: const Icon(Icons.history),
|
||||||
itemBuilder: (context) => [true, false]
|
itemBuilder: (context) => [true, false]
|
||||||
@@ -57,9 +60,7 @@ class MarianumDatesView extends StatelessWidget {
|
|||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
Text(
|
Text(e ? 'Alle anzeigen' : 'Nur zukünftige anzeigen'),
|
||||||
e ? 'Alle anzeigen' : 'Nur zukünftige anzeigen',
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -91,7 +91,14 @@ class _ChatTileState extends State<ChatTile> {
|
|||||||
: null,
|
: null,
|
||||||
leading: Stack(
|
leading: Stack(
|
||||||
children: [
|
children: [
|
||||||
circleAvatar,
|
// Der Name steht bereits im Titel der Zeile – das Avatarbild selbst
|
||||||
|
// muss der Screenreader nicht ansagen. Nur bei Gruppen ist der
|
||||||
|
// Hinweis „Gruppe" nützlich (sonst nicht vom Einzelchat zu
|
||||||
|
// unterscheiden).
|
||||||
|
Semantics(
|
||||||
|
label: isGroup ? A11yLabels.group : null,
|
||||||
|
child: ExcludeSemantics(child: circleAvatar),
|
||||||
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: widget.data.isFavorite,
|
visible: widget.data.isFavorite,
|
||||||
child: Positioned(
|
child: Positioned(
|
||||||
|
|||||||
@@ -106,6 +106,10 @@ class _TimetableState extends State<Timetable> {
|
|||||||
.canViewForeignTimetables;
|
.canViewForeignTimetables;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
// Der Kalender scrollt nicht (nur ziehen/reloaden), aber seine internen
|
||||||
|
// Scrollables feuern ScrollNotifications, die sonst den Material-3
|
||||||
|
// "scrolled under"-Farbwechsel der AppBar dauerhaft auslösen.
|
||||||
|
notificationPredicate: (_) => false,
|
||||||
title: const Text('Stunden & Vertretungsplan'),
|
title: const Text('Stunden & Vertretungsplan'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -114,6 +118,7 @@ class _TimetableState extends State<Timetable> {
|
|||||||
onPressed: atToday ? null : _jumpToToday,
|
onPressed: atToday ? null : _jumpToToday,
|
||||||
),
|
),
|
||||||
PopupMenuButton<_CalendarAction>(
|
PopupMenuButton<_CalendarAction>(
|
||||||
|
tooltip: 'Kalendereinträge',
|
||||||
icon: const Icon(Icons.edit_calendar_outlined),
|
icon: const Icon(Icons.edit_calendar_outlined),
|
||||||
onSelected: _onAction,
|
onSelected: _onAction,
|
||||||
itemBuilder: (_) => const [
|
itemBuilder: (_) => const [
|
||||||
@@ -176,6 +181,9 @@ class _TimetableState extends State<Timetable> {
|
|||||||
.canViewForeignTimetables;
|
.canViewForeignTimetables;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
// Siehe _buildOwnPlan: den scroll-under-Farbwechsel unterdrücken, weil
|
||||||
|
// der Kalender nicht scrollt, aber ScrollNotifications feuert.
|
||||||
|
notificationPredicate: (_) => false,
|
||||||
title: const Text('Stunden & Vertretungsplan'),
|
title: const Text('Stunden & Vertretungsplan'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -285,7 +293,9 @@ class _ViewingBanner extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
compactButton(
|
compactButton(
|
||||||
icon: isFavorite ? Icons.star : Icons.star_border,
|
icon: isFavorite ? Icons.star : Icons.star_border,
|
||||||
tooltip: isFavorite ? 'Favorit entfernen' : 'Als Favorit markieren',
|
tooltip: isFavorite
|
||||||
|
? 'Favorit entfernen'
|
||||||
|
: 'Als Favorit markieren',
|
||||||
onPressed: () => _toggleFavorite(context),
|
onPressed: () => _toggleFavorite(context),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
|
|||||||
@@ -34,7 +34,15 @@ class _WeekGrid extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
_PeriodRuler(schedule: schedule, layout: layout, width: rulerWidth),
|
// Reine Orientierungshilfe – der Screenreader soll die Zeitleiste nicht
|
||||||
|
// vorlesen; jede Termin-Kachel trägt ihre Uhrzeit selbst im Label.
|
||||||
|
ExcludeSemantics(
|
||||||
|
child: _PeriodRuler(
|
||||||
|
schedule: schedule,
|
||||||
|
layout: layout,
|
||||||
|
width: rulerWidth,
|
||||||
|
),
|
||||||
|
),
|
||||||
for (var d = 0; d < 5; d++)
|
for (var d = 0; d < 5; d++)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _DayColumn(
|
child: _DayColumn(
|
||||||
@@ -279,9 +287,25 @@ class _DayColumn extends StatelessWidget {
|
|||||||
final isTablet = MediaQuery.of(context).size.shortestSide >= 600;
|
final isTablet = MediaQuery.of(context).size.shortestSide >= 600;
|
||||||
final laidOut = assignLanes(dayAppointments, maxLanes: isTablet ? 3 : 2);
|
final laidOut = assignLanes(dayAppointments, maxLanes: isTablet ? 3 : 2);
|
||||||
|
|
||||||
return GestureDetector(
|
final dayName = DateFormat(
|
||||||
|
'EEEE',
|
||||||
|
Localizations.localeOf(context).toString(),
|
||||||
|
).format(date);
|
||||||
|
final headerLabel = isToday
|
||||||
|
? '$dayName, ${date.formatDateShort()}, ${A11yLabels.today}'
|
||||||
|
: '$dayName, ${date.formatDateShort()}';
|
||||||
|
|
||||||
|
// container + OrdinalSortKey ⇒ der Screenreader liest Tag für Tag (Mo→Fr),
|
||||||
|
// jeden Tag vollständig, statt zeilenweise quer über alle Spalten. Der
|
||||||
|
// Header steckt als erstes Element in der Spalte, damit „Montag: …" vor den
|
||||||
|
// Stunden angesagt wird.
|
||||||
|
return Semantics(
|
||||||
|
container: true,
|
||||||
|
sortKey: OrdinalSortKey(date.weekday.toDouble()),
|
||||||
|
child: GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onLongPressStart: (details) => _handleLongPress(details, dayAppointments),
|
onLongPressStart: (details) =>
|
||||||
|
_handleLongPress(details, dayAppointments),
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isToday ? theme.colorScheme.primary.withAlpha(14) : null,
|
color: isToday ? theme.colorScheme.primary.withAlpha(14) : null,
|
||||||
@@ -298,6 +322,16 @@ class _DayColumn extends StatelessWidget {
|
|||||||
return Stack(
|
return Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Semantics(
|
||||||
|
header: true,
|
||||||
|
label: headerLabel,
|
||||||
|
child: const SizedBox(height: 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
for (final period in schedule.periods)
|
for (final period in schedule.periods)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: layout.topOf(period),
|
top: layout.topOf(period),
|
||||||
@@ -352,8 +386,11 @@ class _DayColumn extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
LaidOutOverflow(:final appointments) => Semantics(
|
LaidOutOverflow(:final appointments) => Semantics(
|
||||||
button: true,
|
button: true,
|
||||||
label: A11yLabels.moreAppointments(appointments.length),
|
label: A11yLabels.moreAppointments(
|
||||||
onTap: () => _showOverflowSheet(context, appointments),
|
appointments.length,
|
||||||
|
),
|
||||||
|
onTap: () =>
|
||||||
|
_showOverflowSheet(context, appointments),
|
||||||
child: ExcludeSemantics(
|
child: ExcludeSemantics(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
@@ -379,6 +416,7 @@ class _DayColumn extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/semantics.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:syncfusion_flutter_calendar/calendar.dart';
|
import 'package:syncfusion_flutter_calendar/calendar.dart';
|
||||||
|
|
||||||
@@ -103,11 +104,7 @@ class CustomWorkWeekCalendarState extends State<CustomWorkWeekCalendar> {
|
|||||||
final newTotalWeeks =
|
final newTotalWeeks =
|
||||||
newLastMonday.difference(newFirstMonday).inDays ~/ 7 + 1;
|
newLastMonday.difference(newFirstMonday).inDays ~/ 7 + 1;
|
||||||
final visibleWeekStart = _firstMonday.addDays(_currentWeekIndex * 7);
|
final visibleWeekStart = _firstMonday.addDays(_currentWeekIndex * 7);
|
||||||
final newIndex = visibleWeekStart
|
final newIndex = visibleWeekStart.difference(newFirstMonday).inDays ~/ 7;
|
||||||
.difference(newFirstMonday)
|
|
||||||
.inDays
|
|
||||||
~/
|
|
||||||
7;
|
|
||||||
final clampedIndex = newIndex.clamp(0, newTotalWeeks - 1);
|
final clampedIndex = newIndex.clamp(0, newTotalWeeks - 1);
|
||||||
final oldController = _pageController;
|
final oldController = _pageController;
|
||||||
_pageController = PageController(initialPage: clampedIndex);
|
_pageController = PageController(initialPage: clampedIndex);
|
||||||
@@ -167,6 +164,10 @@ class CustomWorkWeekCalendarState extends State<CustomWorkWeekCalendar> {
|
|||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// Die visuelle „MO/14"-Leiste ist für den Screenreader unbrauchbar;
|
||||||
|
// die Tag-Info wird stattdessen als Header in jede Tagesspalte
|
||||||
|
// eingehängt (siehe _DayColumn).
|
||||||
|
child: ExcludeSemantics(
|
||||||
child: _DayHeaderStrip(
|
child: _DayHeaderStrip(
|
||||||
key: ValueKey(visibleWeekStart),
|
key: ValueKey(visibleWeekStart),
|
||||||
weekStart: visibleWeekStart,
|
weekStart: visibleWeekStart,
|
||||||
@@ -175,6 +176,7 @@ class CustomWorkWeekCalendarState extends State<CustomWorkWeekCalendar> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
ClipRect(
|
ClipRect(
|
||||||
child: AnimatedSize(
|
child: AnimatedSize(
|
||||||
duration: const Duration(milliseconds: 320),
|
duration: const Duration(milliseconds: 320),
|
||||||
|
|||||||
@@ -16,15 +16,17 @@ abstract final class A11yLabels {
|
|||||||
static const draft = 'Entwurf';
|
static const draft = 'Entwurf';
|
||||||
static const yourReaction = 'deine Reaktion';
|
static const yourReaction = 'deine Reaktion';
|
||||||
|
|
||||||
// Bilder
|
// Bilder / Kontakte
|
||||||
static const profilePicture = 'Profilbild';
|
static const profilePicture = 'Profilbild';
|
||||||
static const groupPicture = 'Gruppenbild';
|
static const groupPicture = 'Gruppenbild';
|
||||||
|
static const group = 'Gruppe';
|
||||||
static const roomPlan = 'Raumplan der Schule als Grafik';
|
static const roomPlan = 'Raumplan der Schule als Grafik';
|
||||||
|
|
||||||
// Stundenplan
|
// Stundenplan
|
||||||
static const cancelled = 'Ausfall';
|
static const cancelled = 'Ausfall';
|
||||||
static const breakTime = 'Pause';
|
static const breakTime = 'Pause';
|
||||||
static const allDay = 'Ganztägig';
|
static const allDay = 'Ganztägig';
|
||||||
|
static const today = 'heute';
|
||||||
|
|
||||||
/// Beschreibt eine Stundenplan-Kachel für den Screenreader, z.B.
|
/// Beschreibt eine Stundenplan-Kachel für den Screenreader, z.B.
|
||||||
/// „Mathe, Raum 101, 08:00–08:45, Ausfall". Zeilenumbrüche in [location]
|
/// „Mathe, Raum 101, 08:00–08:45, Ausfall". Zeilenumbrüche in [location]
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ Future<void> showDetailsBottomSheet(
|
|||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: 16 + MediaQuery.viewInsetsOf(sheetContext).bottom,
|
bottom: 16 + MediaQuery.viewInsetsOf(sheetContext).bottom,
|
||||||
),
|
),
|
||||||
|
// Ohne dieses innere Material malt ListTile-Ink aufs Sheet-Material
|
||||||
|
// außerhalb des Scrollbereichs und stretcht beim Overscroll nicht mit.
|
||||||
|
child: Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@@ -32,5 +36,6 @@ Future<void> showDetailsBottomSheet(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,8 @@ class _DownloadTrayHostState extends State<DownloadTrayHost>
|
|||||||
/// The chip only shows when there's something the inline UI can't already
|
/// The chip only shows when there's something the inline UI can't already
|
||||||
/// convey: multiple downloads, a finished/failed one (no inline progress
|
/// convey: multiple downloads, a finished/failed one (no inline progress
|
||||||
/// left), or a lone download whose originating screen the user has left.
|
/// left), or a lone download whose originating screen the user has left.
|
||||||
bool _shouldShowChip(List<DownloadJob> jobs, int epoch) => shouldShowDownloadChip(
|
bool _shouldShowChip(List<DownloadJob> jobs, int epoch) =>
|
||||||
|
shouldShowDownloadChip(
|
||||||
sheetOpen: _sheetOpen,
|
sheetOpen: _sheetOpen,
|
||||||
jobCount: jobs.length,
|
jobCount: jobs.length,
|
||||||
anySurfaced: jobs.any(
|
anySurfaced: jobs.any(
|
||||||
@@ -311,7 +312,12 @@ class _TrayChip extends StatelessWidget {
|
|||||||
label = doneCount == 1 ? 'Download fertig' : '$doneCount fertig';
|
label = doneCount == 1 ? 'Download fertig' : '$doneCount fertig';
|
||||||
}
|
}
|
||||||
|
|
||||||
return Material(
|
return Semantics(
|
||||||
|
button: true,
|
||||||
|
label: '$label, Downloads anzeigen',
|
||||||
|
onTap: onTap,
|
||||||
|
child: ExcludeSemantics(
|
||||||
|
child: Material(
|
||||||
color: colors.surfaceContainerHigh,
|
color: colors.surfaceContainerHigh,
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
@@ -320,7 +326,10 @@ class _TrayChip extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@@ -337,11 +346,17 @@ class _TrayChip extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Icon(Icons.expand_less, size: 20, color: colors.onSurfaceVariant),
|
Icon(
|
||||||
|
Icons.expand_less,
|
||||||
|
size: 20,
|
||||||
|
color: colors.onSurfaceVariant,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ class _FileViewerState extends State<FileViewer> {
|
|||||||
...actions,
|
...actions,
|
||||||
if (showActionsMenu)
|
if (showActionsMenu)
|
||||||
PopupMenuButton<FileViewingActions>(
|
PopupMenuButton<FileViewingActions>(
|
||||||
|
tooltip: 'Dateiaktionen',
|
||||||
onSelected: _handleAction,
|
onSelected: _handleAction,
|
||||||
itemBuilder: (context) => _availableActions()
|
itemBuilder: (context) => _availableActions()
|
||||||
.map(
|
.map(
|
||||||
@@ -282,8 +283,10 @@ class _FileViewerState extends State<FileViewer> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildPdfView() =>
|
Widget _buildPdfView() => Scaffold(
|
||||||
Scaffold(appBar: _appbar(), body: DeferredPdfViewer(path: widget.path));
|
appBar: _appbar(),
|
||||||
|
body: DeferredPdfViewer(path: widget.path),
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildVideoView() => Scaffold(
|
Widget _buildVideoView() => Scaffold(
|
||||||
appBar: _appbar(),
|
appBar: _appbar(),
|
||||||
|
|||||||
Reference in New Issue
Block a user