initial commit
This commit is contained in:
@@ -52,16 +52,32 @@ class _MonthlyViewState extends State<MonthlyView> {
|
||||
DateTime.sunday: 0,
|
||||
};
|
||||
|
||||
late final ScrollController _hCtrl;
|
||||
late final ScrollController _vCtrl;
|
||||
// Scroll-Controller
|
||||
late final ScrollController _hHeaderCtrl; // Header (nur mitgeführt)
|
||||
late final ScrollController _hBodyCtrl; // Body (führend)
|
||||
late final ScrollController _vCtrl; // Body vertikal
|
||||
bool _syncingH = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final now = DateTime.now();
|
||||
_monthStart = DateTime(now.year, now.month, 1);
|
||||
_hCtrl = ScrollController();
|
||||
|
||||
_hHeaderCtrl = ScrollController();
|
||||
_hBodyCtrl = ScrollController();
|
||||
_vCtrl = ScrollController();
|
||||
|
||||
// Body scrollt -> Header folgt
|
||||
_hBodyCtrl.addListener(() {
|
||||
if (_syncingH) return;
|
||||
_syncingH = true;
|
||||
if (_hHeaderCtrl.hasClients) {
|
||||
_hHeaderCtrl.jumpTo(_hBodyCtrl.offset);
|
||||
}
|
||||
_syncingH = false;
|
||||
});
|
||||
|
||||
_client = http.Client();
|
||||
_bookingApi = BookingApi(client: _client);
|
||||
_dailyApi = DailyWorkingApi(client: _client);
|
||||
@@ -76,7 +92,8 @@ class _MonthlyViewState extends State<MonthlyView> {
|
||||
for (final c in _controllers.values) {
|
||||
c.dispose();
|
||||
}
|
||||
_hCtrl.dispose();
|
||||
_hHeaderCtrl.dispose();
|
||||
_hBodyCtrl.dispose();
|
||||
_vCtrl.dispose();
|
||||
_client.close();
|
||||
super.dispose();
|
||||
@@ -235,42 +252,34 @@ class _MonthlyViewState extends State<MonthlyView> {
|
||||
),
|
||||
),
|
||||
|
||||
// FIXIERTE KOPFZEILE
|
||||
Scrollbar(
|
||||
controller: _hCtrl,
|
||||
notificationPredicate: (n) => n.metrics.axis == Axis.horizontal,
|
||||
thumbVisibility: true,
|
||||
child: SingleChildScrollView(
|
||||
controller: _hCtrl,
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: _tableMinWidth),
|
||||
child: DataTableTheme(
|
||||
data: DataTableThemeData(
|
||||
headingRowHeight: 30,
|
||||
columnSpacing: 10,
|
||||
headingTextStyle: const TextStyle(fontWeight: FontWeight.w700).copyWith(
|
||||
fontSize: _fontSize,
|
||||
),
|
||||
),
|
||||
child: DataTable(
|
||||
showCheckboxColumn: false,
|
||||
columns: headerColumns,
|
||||
rows: const <DataRow>[],
|
||||
// FIXIERTE KOPFZEILE (mitgeführt, nicht interaktiv)
|
||||
SingleChildScrollView(
|
||||
controller: _hHeaderCtrl,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: _tableMinWidth),
|
||||
child: DataTableTheme(
|
||||
data: DataTableThemeData(
|
||||
headingRowHeight: 30,
|
||||
columnSpacing: 10,
|
||||
headingTextStyle: const TextStyle(fontWeight: FontWeight.w700).copyWith(
|
||||
fontSize: _fontSize,
|
||||
),
|
||||
),
|
||||
child: const _HeaderOnlyDataTable(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// BODY
|
||||
// BODY (horiz. & vert. Scroll; horizontale Scrollbar hier)
|
||||
Expanded(
|
||||
child: Scrollbar(
|
||||
controller: _hCtrl,
|
||||
controller: _hBodyCtrl,
|
||||
notificationPredicate: (n) => n.metrics.axis == Axis.horizontal,
|
||||
thumbVisibility: true,
|
||||
child: SingleChildScrollView(
|
||||
controller: _hCtrl,
|
||||
controller: _hBodyCtrl,
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: ConstrainedBox(
|
||||
@@ -283,7 +292,7 @@ class _MonthlyViewState extends State<MonthlyView> {
|
||||
controller: _vCtrl,
|
||||
child: DataTableTheme(
|
||||
data: const DataTableThemeData(
|
||||
headingRowHeight: 0,
|
||||
headingRowHeight: 0, // Header im Body ausblenden
|
||||
dataRowMinHeight: 30,
|
||||
dataRowMaxHeight: 34,
|
||||
columnSpacing: 10,
|
||||
@@ -498,6 +507,15 @@ class _MonthlyViewState extends State<MonthlyView> {
|
||||
return cells;
|
||||
}
|
||||
|
||||
// kleines Mono-Text-Widget für Zahlen
|
||||
Widget _monoSmall(String s) => Text(
|
||||
s,
|
||||
style: const TextStyle(
|
||||
fontSize: _fontSize,
|
||||
fontFeatures: [FontFeature.tabularFigures()],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _codeDropdown(int dayIndex, WorkDay day) {
|
||||
final value = day.code;
|
||||
final values = <String?>[null, ...kAbsenceCodes];
|
||||
@@ -672,14 +690,6 @@ class _MonthlyViewState extends State<MonthlyView> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _monoSmall(String s) => Text(
|
||||
s,
|
||||
style: const TextStyle(
|
||||
fontSize: _fontSize,
|
||||
fontFeatures: [FontFeature.tabularFigures()],
|
||||
),
|
||||
);
|
||||
|
||||
TextAlign _toTextAlign(Alignment a) {
|
||||
if (a == Alignment.centerRight) return TextAlign.right;
|
||||
if (a == Alignment.centerLeft) return TextAlign.left;
|
||||
@@ -687,6 +697,23 @@ Widget _monoSmall(String s) => Text(
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimaler Header-Table, damit die Kopfzeile fixiert bleiben kann.
|
||||
/// Nutzt die gleichen Spalten wie der Body, aber ohne Rows.
|
||||
class _HeaderOnlyDataTable extends StatelessWidget {
|
||||
const _HeaderOnlyDataTable();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final state = context.findAncestorStateOfType<_MonthlyViewState>()!;
|
||||
final cols = state._buildColumns();
|
||||
return DataTable(
|
||||
showCheckboxColumn: false,
|
||||
columns: cols,
|
||||
rows: const <DataRow>[],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MonthHeader extends StatelessWidget {
|
||||
final DateTime month;
|
||||
final VoidCallback onPrev;
|
||||
|
||||
Reference in New Issue
Block a user