Tracking funkció fejlesztése
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
// Sheet húzható csík
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Handle extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 36,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withOpacity(0.35),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:terepi_seged/pages/tracking/presentation/controllers/tracking_controller.dart';
|
||||
|
||||
import 'stat_cell.dart';
|
||||
|
||||
class LiveStatsPanel extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
final ctrl = TrackingController.to;
|
||||
final isRec = ctrl.isRecording.value;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
||||
child: isRec
|
||||
? Column(children: [
|
||||
// Statisztika sor
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
StatCell(
|
||||
icon: Icons.timer_outlined,
|
||||
label: 'Idő',
|
||||
value: ctrl.elapsedFormatted.value,
|
||||
large: true),
|
||||
StatCell(
|
||||
icon: Icons.route,
|
||||
label: 'Távolság',
|
||||
value: _fmtDist(ctrl.sessionDistance.value)),
|
||||
StatCell(
|
||||
icon: Icons.location_on_outlined,
|
||||
label: 'Pontok',
|
||||
value: '${ctrl.livePointCount}'),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// Vezérlők
|
||||
Row(children: [
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
icon: Icon(
|
||||
ctrl.isPaused.value ? Icons.play_arrow : Icons.pause),
|
||||
label: Text(ctrl.isPaused.value ? 'Folytatás' : 'Szünet'),
|
||||
onPressed: ctrl.isPaused.value
|
||||
? ctrl.resumeRecording
|
||||
: ctrl.pauseRecording,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: FilledButton.icon(
|
||||
icon: const Icon(Icons.stop),
|
||||
label: const Text('Befejezés'),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Colors.red.shade700),
|
||||
onPressed: ctrl.stopRecording,
|
||||
),
|
||||
),
|
||||
]),
|
||||
])
|
||||
: FilledButton.icon(
|
||||
icon: const Icon(Icons.fiber_manual_record),
|
||||
label: const Text('Rögzítés indítása'),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Colors.red.shade700),
|
||||
onPressed: () => _showStartDialog(context),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _showStartDialog(BuildContext context) {
|
||||
final nameCtrl = TextEditingController(
|
||||
text: 'Track ${DateTime.now().toString().substring(5, 16)}',
|
||||
);
|
||||
Get.dialog(AlertDialog(
|
||||
title: const Text('Rögzítés indítása'),
|
||||
content: TextField(
|
||||
controller: nameCtrl,
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Track neve',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: Get.back, child: const Text('Mégse')),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
TrackingController.to.startRecording();
|
||||
},
|
||||
child: const Text('Indítás'),
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
String _fmtDist(double m) => m < 1000
|
||||
? '${m.toStringAsFixed(0)} m'
|
||||
: '${(m / 1000).toStringAsFixed(2)} km';
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Szekció fejléc (pl. "Korábbi útvonalak")
|
||||
class SectionHeader extends StatelessWidget {
|
||||
final String title;
|
||||
final Widget? trailing;
|
||||
|
||||
const SectionHeader({
|
||||
required this.title,
|
||||
this.trailing,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 8, 4),
|
||||
child: Row(children: [
|
||||
Text(
|
||||
title.toUpperCase(),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey.shade500,
|
||||
letterSpacing: 0.6,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
if (trailing != null) trailing!,
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StatCell extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final String value;
|
||||
final bool large;
|
||||
|
||||
const StatCell({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.value,
|
||||
this.large = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Icon(icon, size: 16, color: Colors.grey),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: large ? 20 : 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:terepi_seged/pages/tracking/presentation/controllers/tracking_controller.dart';
|
||||
|
||||
import 'tracking_sheet.dart';
|
||||
|
||||
/// Kompakt tracking státusz kártya a térkép bal felső sarkában.
|
||||
///
|
||||
/// Rögzítés közben: idő, távolság, pontok + szünet/stop gombok
|
||||
/// Rögzítésen kívül: egyetlen start gomb
|
||||
///
|
||||
/// Mindig látható — tap → TrackingSheet (teljes lista + vezérlők)
|
||||
class TrackInfoCard extends StatelessWidget {
|
||||
const TrackInfoCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
final ctrl = TrackingController.to;
|
||||
final isRec = ctrl.isRecording.value;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => _openSheet(context),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minWidth: 140),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.72),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: isRec
|
||||
? Colors.red.withOpacity(0.5)
|
||||
: Colors.white.withOpacity(0.12),
|
||||
),
|
||||
),
|
||||
child: isRec ? _RecordingContent(ctrl: ctrl) : _IdleContent(),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _openSheet(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (_) => const TrackingSheet(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Rögzítés közben ──────────────────────────────────────────────────────────
|
||||
|
||||
class _RecordingContent extends StatelessWidget {
|
||||
final TrackingController ctrl;
|
||||
const _RecordingContent({required this.ctrl});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Fejléc sor — piros pont + "REC" vagy "SZÜNET"
|
||||
Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
_StatusDot(paused: ctrl.isPaused.value),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
ctrl.isPaused.value ? 'SZÜNET' : 'REC',
|
||||
style: TextStyle(
|
||||
color: ctrl.isPaused.value ? Colors.orange : Colors.red,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// Kártya bezárása (csak megkisebbíti, nem állítja le)
|
||||
GestureDetector(
|
||||
onTap: () {}, // placeholder — a kártya mindig látható
|
||||
child: Icon(Icons.unfold_less,
|
||||
size: 14, color: Colors.white.withOpacity(0.3)),
|
||||
),
|
||||
]),
|
||||
|
||||
const SizedBox(height: 4),
|
||||
|
||||
// Statisztika sor
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_MiniStat(
|
||||
icon: Icons.timer_outlined,
|
||||
value: ctrl.elapsedFormatted.value,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
_MiniStat(
|
||||
icon: Icons.route,
|
||||
value: _fmtDist(ctrl.sessionDistance.value),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
_MiniStat(
|
||||
icon: Icons.location_on_outlined,
|
||||
value: '${ctrl.livePoints.length}',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 6),
|
||||
|
||||
// Vezérlő gombok
|
||||
Row(children: [
|
||||
// Szünet / Folytatás
|
||||
_CardButton(
|
||||
icon: ctrl.isPaused.value ? Icons.play_arrow : Icons.pause,
|
||||
color: ctrl.isPaused.value ? Colors.greenAccent : Colors.orange,
|
||||
onTap: ctrl.isPaused.value
|
||||
? ctrl.resumeRecording
|
||||
: ctrl.pauseRecording,
|
||||
tooltip: ctrl.isPaused.value ? 'Folytatás' : 'Szünet',
|
||||
),
|
||||
|
||||
const SizedBox(width: 6),
|
||||
|
||||
// Stop
|
||||
_CardButton(
|
||||
icon: Icons.stop,
|
||||
color: Colors.red,
|
||||
onTap: () => _confirmStop(context),
|
||||
tooltip: 'Befejezés',
|
||||
),
|
||||
|
||||
const Spacer(),
|
||||
|
||||
// Nyíl → sheet megnyitás
|
||||
Icon(Icons.keyboard_arrow_up,
|
||||
size: 14, color: Colors.white.withOpacity(0.35)),
|
||||
]),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
void _confirmStop(BuildContext context) {
|
||||
Get.dialog(AlertDialog(
|
||||
title: const Text('Rögzítés befejezése'),
|
||||
content: Obx(() => Text(
|
||||
'${ctrl.elapsedFormatted.value} · '
|
||||
'${_fmtDist(ctrl.sessionDistance.value)}\n'
|
||||
'${ctrl.livePoints.length} pont',
|
||||
)),
|
||||
actions: [
|
||||
TextButton(onPressed: Get.back, child: const Text('Mégse')),
|
||||
FilledButton(
|
||||
style: FilledButton.styleFrom(backgroundColor: Colors.red.shade700),
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
ctrl.stopRecording();
|
||||
},
|
||||
child: const Text('Befejezés'),
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
String _fmtDist(double m) => m < 1000
|
||||
? '${m.toStringAsFixed(0)} m'
|
||||
: '${(m / 1000).toStringAsFixed(2)} km';
|
||||
}
|
||||
|
||||
// ─── Idle állapot ─────────────────────────────────────────────────────────────
|
||||
|
||||
class _IdleContent extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.route_outlined,
|
||||
size: 14, color: Colors.white.withOpacity(0.5)),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'Track',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.55),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Indítás gomb
|
||||
GestureDetector(
|
||||
onTap: () => _startRecording(context),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: Colors.red.withOpacity(0.4)),
|
||||
),
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.fiber_manual_record, size: 10, color: Colors.red),
|
||||
SizedBox(width: 4),
|
||||
Text('Start',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _startRecording(BuildContext context) {
|
||||
final nameCtrl = TextEditingController(
|
||||
text: 'Track ${DateTime.now().toString().substring(5, 16)}',
|
||||
);
|
||||
Get.dialog(AlertDialog(
|
||||
title: const Text('Rögzítés indítása'),
|
||||
content: TextField(
|
||||
controller: nameCtrl,
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Track neve',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: Get.back, child: const Text('Mégse')),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
TrackingController.to.startRecording();
|
||||
// .startRecording(name: nameCtrl.text.trim());
|
||||
},
|
||||
child: const Text('Indítás'),
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Segéd widgetek ───────────────────────────────────────────────────────────
|
||||
|
||||
class _StatusDot extends StatefulWidget {
|
||||
final bool paused;
|
||||
const _StatusDot({required this.paused});
|
||||
|
||||
@override
|
||||
State<_StatusDot> createState() => _StatusDotState();
|
||||
}
|
||||
|
||||
class _StatusDotState extends State<_StatusDot>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _ctrl;
|
||||
late Animation<double> _anim;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_ctrl = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 700),
|
||||
)..repeat(reverse: true);
|
||||
_anim = Tween(begin: 0.3, end: 1.0)
|
||||
.animate(CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut));
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(_StatusDot old) {
|
||||
super.didUpdateWidget(old);
|
||||
widget.paused ? _ctrl.stop() : _ctrl.repeat(reverse: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_ctrl.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = widget.paused ? Colors.orange : Colors.red;
|
||||
return AnimatedBuilder(
|
||||
animation: _anim,
|
||||
builder: (_, __) => Opacity(
|
||||
opacity: widget.paused ? 0.6 : _anim.value,
|
||||
child: Container(
|
||||
width: 7,
|
||||
height: 7,
|
||||
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MiniStat extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String value;
|
||||
const _MiniStat({required this.icon, required this.value});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
Icon(icon, size: 11, color: Colors.white54),
|
||||
const SizedBox(width: 3),
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFeatures: [FontFeature.tabularFigures()],
|
||||
),
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
class _CardButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final Color color;
|
||||
final VoidCallback onTap;
|
||||
final String tooltip;
|
||||
|
||||
const _CardButton({
|
||||
required this.icon,
|
||||
required this.color,
|
||||
required this.onTap,
|
||||
required this.tooltip,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Tooltip(
|
||||
message: tooltip,
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 28,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.18),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: color.withOpacity(0.45)),
|
||||
),
|
||||
child: Icon(icon, size: 14, color: color),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:terepi_seged/models/track.dart';
|
||||
import 'package:terepi_seged/pages/tracking/presentation/controllers/tracking_controller.dart';
|
||||
|
||||
class TrackListItem extends StatelessWidget {
|
||||
final Track track;
|
||||
|
||||
const TrackListItem({
|
||||
super.key,
|
||||
required this.track,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
final ctrl = TrackingController.to;
|
||||
final isOverlay = ctrl.overlayTrackIds.contains(track.id);
|
||||
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: isOverlay
|
||||
? Colors.blue.withOpacity(0.2)
|
||||
: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: Icon(Icons.route,
|
||||
color: isOverlay ? Colors.blue : Colors.grey, size: 20),
|
||||
),
|
||||
title: Text(track.name, style: const TextStyle(fontSize: 13)),
|
||||
subtitle: Text(
|
||||
'${_fmtDist(track.distanceMeters)} · '
|
||||
'${track.durationFormatted} · '
|
||||
'${track.pointCount} pt',
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
// Overlay kapcsoló
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
isOverlay ? Icons.layers : Icons.layers_outlined,
|
||||
color: isOverlay ? Colors.blue : null,
|
||||
size: 20,
|
||||
),
|
||||
tooltip: isOverlay ? 'Elrejt' : 'Mutat a térképen',
|
||||
onPressed: () {
|
||||
ctrl.toggleTrackOverlay(track.id!);
|
||||
// Ha bekapcsolta → zárjuk be a sheet-et
|
||||
// hogy lássa a térképet
|
||||
if (!isOverlay) Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
|
||||
// Export
|
||||
IconButton(
|
||||
icon: const Icon(Icons.share, size: 20),
|
||||
onPressed: () => ctrl.exportTrack(track),
|
||||
),
|
||||
]),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
String _fmtDist(double m) => m < 1000
|
||||
? '${m.toStringAsFixed(0)} m'
|
||||
: '${(m / 1000).toStringAsFixed(2)} km';
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:terepi_seged/pages/tracking/presentation/controllers/tracking_controller.dart';
|
||||
|
||||
import 'handle.dart';
|
||||
import 'live_stat_panel.dart';
|
||||
import 'section_header.dart';
|
||||
import 'track_list_item.dart';
|
||||
|
||||
class TrackingSheet extends StatelessWidget {
|
||||
const TrackingSheet();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DraggableScrollableSheet(
|
||||
initialChildSize: 0.35, // kezdetben kis méret
|
||||
minChildSize: 0.2,
|
||||
maxChildSize: 0.85, // felfelé húzva nagy lista
|
||||
snap: true,
|
||||
snapSizes: const [0.2, 0.35, 0.85],
|
||||
builder: (_, scrollCtrl) => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, -4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: CustomScrollView(
|
||||
controller: scrollCtrl,
|
||||
slivers: [
|
||||
// Handle
|
||||
SliverToBoxAdapter(child: Handle()),
|
||||
|
||||
// Élő statisztika panel
|
||||
SliverToBoxAdapter(
|
||||
child: LiveStatsPanel(),
|
||||
),
|
||||
|
||||
// Mentett track-ek fejléc
|
||||
SliverToBoxAdapter(
|
||||
child: SectionHeader(
|
||||
title: 'Korábbi útvonalak',
|
||||
trailing: TextButton(
|
||||
onPressed: TrackingController.to.loadSavedTracks,
|
||||
child: const Text('Frissítés'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Track lista
|
||||
Obx(() => SliverList.separated(
|
||||
itemCount: TrackingController.to.savedTracks.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
itemBuilder: (_, i) => TrackListItem(
|
||||
track: TrackingController.to.savedTracks[i],
|
||||
),
|
||||
)),
|
||||
|
||||
const SliverPadding(padding: EdgeInsets.only(bottom: 20)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user