fApp/lib/mediagrid.dart
2025-06-02 11:10:42 +02:00

219 lines
6.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:f0ckapp/services/api.dart';
import 'package:f0ckapp/models/mediaitem.dart';
import 'package:f0ckapp/screens/detailview.dart';
import 'dart:async';
class MediaGrid extends StatefulWidget {
const MediaGrid({super.key});
@override
State createState() => _MediaGridState();
}
class _MediaGridState extends State<MediaGrid> {
final ScrollController _scrollController = ScrollController();
List<MediaItem> mediaItems = [];
bool isLoading = false;
Timer? _debounceTimer;
Completer<void>? _navigationCompleter;
int _crossAxisCount = 3;
String _selectedMode = "alles";
@override
void initState() {
super.initState();
_loadMedia();
_scrollController.addListener(() {
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 100) {
_debounceLoadMedia();
}
});
}
void _debounceLoadMedia() {
_debounceTimer?.cancel();
_debounceTimer = Timer(const Duration(milliseconds: 500), _loadMedia);
}
Future<void> _loadMedia() async {
if (isLoading) return;
setState(() => isLoading = true);
try {
final newMedia = await fetchMedia(
older: mediaItems.isNotEmpty ? mediaItems.last.id.toString() : null,
mode: _selectedMode,
);
if (mounted) {
setState(() => mediaItems.addAll(newMedia));
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Fehler beim Laden der Medien: $e')),
);
}
} finally {
if (mounted) setState(() => isLoading = false);
}
}
Future<void> _refreshMedia() async {
setState(() => isLoading = true);
try {
final freshMedia = await fetchMedia(older: null, mode: _selectedMode);
if (mounted) {
setState(() {
mediaItems.clear();
mediaItems.addAll(freshMedia);
});
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Fehler beim Aktualisieren: $e')),
);
}
} finally {
if (mounted) setState(() => isLoading = false);
}
}
Future<void> _navigateToDetail(MediaItem item) async {
if (_navigationCompleter?.isCompleted == false) return;
_navigationCompleter = Completer();
try {
if (mounted) {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailView(
initialItemId: item.id,
mediaItems: mediaItems,
mode: _selectedMode,
),
),
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Fehler beim Laden der Details: $e')),
);
}
} finally {
_navigationCompleter?.complete();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color.fromARGB(255, 43, 43, 43),
foregroundColor: const Color.fromARGB(255, 255, 255, 255),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [const Text('f0cks')],
),
),
bottomNavigationBar: BottomAppBar(
color: const Color.fromARGB(255, 43, 43, 43),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DropdownButton<String>(
value: _selectedMode,
dropdownColor: const Color.fromARGB(255, 43, 43, 43),
iconEnabledColor: Colors.white,
items: ["alles", "image", "video", "audio"].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value, style: TextStyle(color: Colors.white)),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
setState(() {
_selectedMode = newValue;
_refreshMedia();
});
}
},
),
DropdownButton<int>(
value: _crossAxisCount,
dropdownColor: const Color.fromARGB(255, 43, 43, 43),
iconEnabledColor: Colors.white,
items: [3, 4].map((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(
'$value Spalten',
style: TextStyle(color: Colors.white),
),
);
}).toList(),
onChanged: (int? newValue) {
if (newValue != null) {
setState(() {
_crossAxisCount = newValue;
});
}
},
),
],
),
),
),
body: RefreshIndicator(
onRefresh: _refreshMedia,
child: GridView.builder(
controller: _scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: _crossAxisCount,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
itemCount: mediaItems.length + (isLoading ? 1 : 0),
itemBuilder: (context, index) {
if (index >= mediaItems.length) {
return const Center(child: CircularProgressIndicator());
}
final item = mediaItems[index];
final mode =
{1: Colors.green, 2: Colors.red}[item.mode] ?? Colors.yellow;
return InkWell(
onTap: () => _navigateToDetail(item),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Image.network(item.thumbnailUrl, fit: BoxFit.cover),
Align(
alignment: FractionalOffset.bottomRight,
child: Icon(Icons.square, color: mode, size: 15.0),
),
],
),
);
},
),
),
);
}
@override
void dispose() {
_scrollController.dispose();
_debounceTimer?.cancel();
super.dispose();
}
}