Files
plezy/lib/widgets/video_controls/sheets/sheet_selection_column.dart
T
edde746 352b88109b refactor: extract shared mixins and helpers, drop dead abstractions
Introduces shared seams for paginated views, D-pad reorder, media control
routing, async singletons and the device method channel, then points the
open-coded copies at them.

Also removes unused models and duplicated provider/server plumbing, folds
the twice-implemented artifact store in the server, and factors the
repeated Flutter toolchain prologue in CI into a composite action.
2026-07-26 06:09:48 +02:00

94 lines
2.9 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import '../../../utils/scroll_utils.dart';
import '../../../widgets/overlay_sheet.dart';
import 'sheet_column_header.dart';
/// Per-row handle handed to [SheetSelectionColumn.itemBuilder].
abstract class SheetSelectionColumnScope {
/// Key for the row at [index]. Only the first row is keyed, so the one-time
/// initial scroll can measure a real item height.
Key? keyFor(int index);
/// Runs an async selection: re-entrant taps are ignored while one is in
/// flight, a progress bar is shown meanwhile, and the sheet is closed once
/// [action] completes.
void runExclusive(Future<void> Function() action);
}
/// Shared scaffold for the selectable columns inside the video control sheets:
/// an optional header, a one-shot scroll to the selected row, the async
/// selection guard, the scrolling list, and an optional footer.
class SheetSelectionColumn extends StatefulWidget {
/// Header text, or null to omit the header entirely.
final String? headerLabel;
final int itemCount;
/// Row to scroll into view on first build; ignored when null or <= 0.
final int? initialIndex;
final Widget Function(BuildContext context, int index, SheetSelectionColumnScope scope) itemBuilder;
final List<Widget> footer;
const SheetSelectionColumn({
super.key,
this.headerLabel,
required this.itemCount,
required this.initialIndex,
required this.itemBuilder,
this.footer = const [],
});
@override
State<SheetSelectionColumn> createState() => _SheetSelectionColumnState();
}
class _SheetSelectionColumnState extends State<SheetSelectionColumn> implements SheetSelectionColumnScope {
final _initialScroll = InitialItemScrollController();
bool _selectionPending = false;
@override
void dispose() {
_initialScroll.dispose();
super.dispose();
}
@override
Key? keyFor(int index) => index == 0 ? _initialScroll.firstItemKey : null;
@override
void runExclusive(Future<void> Function() action) => unawaited(_select(action));
Future<void> _select(Future<void> Function() action) async {
if (_selectionPending) return;
setState(() => _selectionPending = true);
try {
await action();
if (mounted) OverlaySheetController.of(context).close();
} finally {
if (mounted) setState(() => _selectionPending = false);
}
}
@override
Widget build(BuildContext context) {
_initialScroll.maybeScrollTo(widget.initialIndex);
return Column(
children: [
if (widget.headerLabel != null) SheetColumnHeader(label: widget.headerLabel!),
if (_selectionPending) const LinearProgressIndicator(minHeight: 2),
Expanded(
child: ListView.builder(
controller: _initialScroll.controller,
itemCount: widget.itemCount,
itemBuilder: (context, index) => widget.itemBuilder(context, index, this),
),
),
...widget.footer,
],
);
}
}