detailview lol
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:f0ckapp/services/api.dart';
 | 
					import 'package:f0ckapp/services/api.dart';
 | 
				
			||||||
import 'package:f0ckapp/models/mediaitem.dart';
 | 
					import 'package:f0ckapp/models/mediaitem.dart';
 | 
				
			||||||
import 'package:f0ckapp/screens/detail.dart';
 | 
					import 'package:f0ckapp/screens/detailview.dart';
 | 
				
			||||||
import 'dart:async';
 | 
					import 'dart:async';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MediaGrid extends StatefulWidget {
 | 
					class MediaGrid extends StatefulWidget {
 | 
				
			||||||
@@ -18,6 +18,7 @@ class _MediaGridState extends State<MediaGrid> {
 | 
				
			|||||||
  Timer? _debounceTimer;
 | 
					  Timer? _debounceTimer;
 | 
				
			||||||
  Completer<void>? _navigationCompleter;
 | 
					  Completer<void>? _navigationCompleter;
 | 
				
			||||||
  int _crossAxisCount = 3;
 | 
					  int _crossAxisCount = 3;
 | 
				
			||||||
 | 
					  String _selectedMode = "alles";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
@@ -43,6 +44,7 @@ class _MediaGridState extends State<MediaGrid> {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final newMedia = await fetchMedia(
 | 
					      final newMedia = await fetchMedia(
 | 
				
			||||||
        older: mediaItems.isNotEmpty ? mediaItems.last.id.toString() : null,
 | 
					        older: mediaItems.isNotEmpty ? mediaItems.last.id.toString() : null,
 | 
				
			||||||
 | 
					        mode: _selectedMode,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      if (mounted) {
 | 
					      if (mounted) {
 | 
				
			||||||
        setState(() => mediaItems.addAll(newMedia));
 | 
					        setState(() => mediaItems.addAll(newMedia));
 | 
				
			||||||
@@ -61,7 +63,7 @@ class _MediaGridState extends State<MediaGrid> {
 | 
				
			|||||||
  Future<void> _refreshMedia() async {
 | 
					  Future<void> _refreshMedia() async {
 | 
				
			||||||
    setState(() => isLoading = true);
 | 
					    setState(() => isLoading = true);
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final freshMedia = await fetchMedia(older: null);
 | 
					      final freshMedia = await fetchMedia(older: null, mode: _selectedMode);
 | 
				
			||||||
      if (mounted) {
 | 
					      if (mounted) {
 | 
				
			||||||
        setState(() {
 | 
					        setState(() {
 | 
				
			||||||
          mediaItems.clear();
 | 
					          mediaItems.clear();
 | 
				
			||||||
@@ -80,17 +82,20 @@ class _MediaGridState extends State<MediaGrid> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<void> _navigateToDetail(MediaItem item) async {
 | 
					  Future<void> _navigateToDetail(MediaItem item) async {
 | 
				
			||||||
    print('Tapped item: ${item.id}');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (_navigationCompleter?.isCompleted == false) return;
 | 
					    if (_navigationCompleter?.isCompleted == false) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _navigationCompleter = Completer();
 | 
					    _navigationCompleter = Completer();
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final details = await fetchMediaDetail(item.id);
 | 
					 | 
				
			||||||
      if (mounted) {
 | 
					      if (mounted) {
 | 
				
			||||||
        await Navigator.push(
 | 
					        await Navigator.push(
 | 
				
			||||||
          context,
 | 
					          context,
 | 
				
			||||||
          MaterialPageRoute(builder: (context) => DetailView(details: details)),
 | 
					          MaterialPageRoute(
 | 
				
			||||||
 | 
					            builder: (context) => DetailView(
 | 
				
			||||||
 | 
					              initialItemId: item.id,
 | 
				
			||||||
 | 
					              mediaItems: mediaItems,
 | 
				
			||||||
 | 
					              mode: _selectedMode,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
@@ -114,6 +119,25 @@ class _MediaGridState extends State<MediaGrid> {
 | 
				
			|||||||
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
					          mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            const Text('f0cks'),
 | 
					            const Text('f0cks'),
 | 
				
			||||||
 | 
					            DropdownButton<String>(
 | 
				
			||||||
 | 
					              value: _selectedMode,
 | 
				
			||||||
 | 
					              dropdownColor: const Color.fromARGB(255, 43, 43, 43),
 | 
				
			||||||
 | 
					              iconEnabledColor: const Color.fromARGB(255, 255, 255, 255),
 | 
				
			||||||
 | 
					              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>(
 | 
					            DropdownButton<int>(
 | 
				
			||||||
              value: _crossAxisCount,
 | 
					              value: _crossAxisCount,
 | 
				
			||||||
              dropdownColor: const Color.fromARGB(255, 43, 43, 43),
 | 
					              dropdownColor: const Color.fromARGB(255, 43, 43, 43),
 | 
				
			||||||
@@ -121,7 +145,10 @@ class _MediaGridState extends State<MediaGrid> {
 | 
				
			|||||||
              items: [3, 4].map((int value) {
 | 
					              items: [3, 4].map((int value) {
 | 
				
			||||||
                return DropdownMenuItem<int>(
 | 
					                return DropdownMenuItem<int>(
 | 
				
			||||||
                  value: value,
 | 
					                  value: value,
 | 
				
			||||||
                  child: Text('$value Spalten', style: TextStyle(color: Colors.white)),
 | 
					                  child: Text(
 | 
				
			||||||
 | 
					                    '$value Spalten',
 | 
				
			||||||
 | 
					                    style: TextStyle(color: Colors.white),
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
              }).toList(),
 | 
					              }).toList(),
 | 
				
			||||||
              onChanged: (int? newValue) {
 | 
					              onChanged: (int? newValue) {
 | 
				
			||||||
@@ -152,7 +179,7 @@ class _MediaGridState extends State<MediaGrid> {
 | 
				
			|||||||
            final item = mediaItems[index];
 | 
					            final item = mediaItems[index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final mode =
 | 
					            final mode =
 | 
				
			||||||
                {1: Colors.green, 2: Colors.red}[item.tagid] ?? Colors.yellow;
 | 
					                {1: Colors.green, 2: Colors.red}[item.mode] ?? Colors.yellow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return InkWell(
 | 
					            return InkWell(
 | 
				
			||||||
              onTap: () => _navigateToDetail(item),
 | 
					              onTap: () => _navigateToDetail(item),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,51 @@
 | 
				
			|||||||
class MediaItem {
 | 
					class MediaItem {
 | 
				
			||||||
  final int id;
 | 
					  final int id;
 | 
				
			||||||
  final String mime;
 | 
					  final String mime;
 | 
				
			||||||
  final int tagid;
 | 
					  final int size;
 | 
				
			||||||
 | 
					  final int stamp;
 | 
				
			||||||
 | 
					  final String dest;
 | 
				
			||||||
 | 
					  final int mode;
 | 
				
			||||||
 | 
					  final List<Tag> tags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  MediaItem({required this.id, required this.mime, required this.tagid});
 | 
					  MediaItem({
 | 
				
			||||||
 | 
					    required this.id,
 | 
				
			||||||
 | 
					    required this.mime,
 | 
				
			||||||
 | 
					    required this.size,
 | 
				
			||||||
 | 
					    required this.stamp,
 | 
				
			||||||
 | 
					    required this.dest,
 | 
				
			||||||
 | 
					    required this.mode,
 | 
				
			||||||
 | 
					    required this.tags,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  factory MediaItem.fromJson(Map<String, dynamic> json) {
 | 
					  factory MediaItem.fromJson(Map<String, dynamic> json) {
 | 
				
			||||||
    return MediaItem(
 | 
					    return MediaItem(
 | 
				
			||||||
      id: json['id'],
 | 
					      id: json['id'],
 | 
				
			||||||
      mime: json['mime'],
 | 
					      mime: json['mime'],
 | 
				
			||||||
      tagid: json['tag_id'],
 | 
					      size: json['size'],
 | 
				
			||||||
 | 
					      stamp: json['stamp'],
 | 
				
			||||||
 | 
					      dest: json['dest'],
 | 
				
			||||||
 | 
					      mode: json['mode'],
 | 
				
			||||||
 | 
					      tags: (json['tags'] as List<dynamic>)
 | 
				
			||||||
 | 
					          .map((tagJson) => Tag.fromJson(tagJson))
 | 
				
			||||||
 | 
					          .toList(),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String get thumbnailUrl => 'https://f0ck.me/t/$id.webp';
 | 
					  String get thumbnailUrl => 'https://f0ck.me/t/$id.webp';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Tag {
 | 
				
			||||||
 | 
					  final int id;
 | 
				
			||||||
 | 
					  final String tag;
 | 
				
			||||||
 | 
					  final String normalized;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Tag({required this.id, required this.tag, required this.normalized});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  factory Tag.fromJson(Map<String, dynamic> json) {
 | 
				
			||||||
 | 
					    return Tag(
 | 
				
			||||||
 | 
					      id: json['id'],
 | 
				
			||||||
 | 
					      tag: json['tag'],
 | 
				
			||||||
 | 
					      normalized: json['normalized'],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,205 +0,0 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					 | 
				
			||||||
import 'package:video_player/video_player.dart';
 | 
					 | 
				
			||||||
import 'package:f0ckapp/models/mediaitem_detail.dart';
 | 
					 | 
				
			||||||
import 'package:f0ckapp/services/api.dart';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class DetailView extends StatefulWidget {
 | 
					 | 
				
			||||||
  final MediaItemDetail details;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const DetailView({super.key, required this.details});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  State<DetailView> createState() => _DetailViewState();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _DetailViewState extends State<DetailView> {
 | 
					 | 
				
			||||||
  late VideoPlayerController _controller;
 | 
					 | 
				
			||||||
  late Future<void> _initializeVideoPlayer;
 | 
					 | 
				
			||||||
  bool isAudio = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  void initState() {
 | 
					 | 
				
			||||||
    super.initState();
 | 
					 | 
				
			||||||
    isAudio = widget.details.mime.startsWith("audio/");
 | 
					 | 
				
			||||||
    _controller = VideoPlayerController.networkUrl(
 | 
					 | 
				
			||||||
      Uri.parse(widget.details.mediaUrl),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!widget.details.mime.startsWith("image/")) {
 | 
					 | 
				
			||||||
      _initializeVideoPlayer = _controller
 | 
					 | 
				
			||||||
          .initialize()
 | 
					 | 
				
			||||||
          .then((_) {
 | 
					 | 
				
			||||||
            setState(() {
 | 
					 | 
				
			||||||
              _controller.play();
 | 
					 | 
				
			||||||
              _controller.setLooping(true);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
          })
 | 
					 | 
				
			||||||
          .catchError((error) {
 | 
					 | 
				
			||||||
            ScaffoldMessenger.of(context).showSnackBar(
 | 
					 | 
				
			||||||
              SnackBar(content: Text("Fehler beim Laden des Videos: $error")),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  void dispose() {
 | 
					 | 
				
			||||||
    _controller.dispose();
 | 
					 | 
				
			||||||
    super.dispose();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Future<void> _loadNewMediaItem(int itemId, bool swipeLeft) async {
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      var newDetails = await fetchMediaDetail(itemId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Navigator.of(context).pushReplacement(
 | 
					 | 
				
			||||||
        PageRouteBuilder(
 | 
					 | 
				
			||||||
          pageBuilder: (context, animation, secondaryAnimation) =>
 | 
					 | 
				
			||||||
              DetailView(details: newDetails),
 | 
					 | 
				
			||||||
          transitionsBuilder: (context, animation, secondaryAnimation, child) {
 | 
					 | 
				
			||||||
            Offset begin = swipeLeft
 | 
					 | 
				
			||||||
                ? const Offset(-1.0, 0.0)
 | 
					 | 
				
			||||||
                : const Offset(1.0, 0.0);
 | 
					 | 
				
			||||||
            const Offset end = Offset.zero;
 | 
					 | 
				
			||||||
            const curve = Curves.easeInOut;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var tween = Tween(
 | 
					 | 
				
			||||||
              begin: begin,
 | 
					 | 
				
			||||||
              end: end,
 | 
					 | 
				
			||||||
            ).chain(CurveTween(curve: curve));
 | 
					 | 
				
			||||||
            var offsetAnimation = animation.drive(tween);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return SlideTransition(position: offsetAnimation, child: child);
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    } catch (error) {
 | 
					 | 
				
			||||||
      ScaffoldMessenger.of(context).showSnackBar(
 | 
					 | 
				
			||||||
        SnackBar(content: Text("Fehler beim Laden des Items: $error")),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					 | 
				
			||||||
    return GestureDetector(
 | 
					 | 
				
			||||||
      onHorizontalDragEnd: (details) {
 | 
					 | 
				
			||||||
        if (details.velocity.pixelsPerSecond.dx > 0 && widget.details.next != null) {
 | 
					 | 
				
			||||||
          _loadNewMediaItem(widget.details.next!, true);
 | 
					 | 
				
			||||||
        } else if (details.velocity.pixelsPerSecond.dx < 0 && widget.details.prev != null) {
 | 
					 | 
				
			||||||
          _loadNewMediaItem(widget.details.prev!, false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      child: Scaffold(
 | 
					 | 
				
			||||||
        appBar: AppBar(
 | 
					 | 
				
			||||||
          backgroundColor: const Color(0xFF2B2B2B),
 | 
					 | 
				
			||||||
          foregroundColor: Colors.white,
 | 
					 | 
				
			||||||
          title: Text('f0ck #${widget.details.id}'),
 | 
					 | 
				
			||||||
          centerTitle: true,
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        body: SingleChildScrollView(
 | 
					 | 
				
			||||||
          child: Column(
 | 
					 | 
				
			||||||
            children: [
 | 
					 | 
				
			||||||
              if (widget.details.mime.contains(
 | 
					 | 
				
			||||||
                RegExp(r'image/(jpeg|png|gif|webp)'),
 | 
					 | 
				
			||||||
              )) ...[
 | 
					 | 
				
			||||||
                Image.network(widget.details.mediaUrl),
 | 
					 | 
				
			||||||
              ] else ...[
 | 
					 | 
				
			||||||
                Stack(
 | 
					 | 
				
			||||||
                  alignment: Alignment.center,
 | 
					 | 
				
			||||||
                  children: [
 | 
					 | 
				
			||||||
                    if (isAudio)
 | 
					 | 
				
			||||||
                      Image.network(
 | 
					 | 
				
			||||||
                        'https://f0ck.me/ca/${widget.details.id}.webp',
 | 
					 | 
				
			||||||
                      ),
 | 
					 | 
				
			||||||
                    FutureBuilder(
 | 
					 | 
				
			||||||
                      future: _initializeVideoPlayer,
 | 
					 | 
				
			||||||
                      builder: (context, snapshot) {
 | 
					 | 
				
			||||||
                        if (snapshot.connectionState == ConnectionState.done &&
 | 
					 | 
				
			||||||
                            _controller.value.isInitialized) {
 | 
					 | 
				
			||||||
                          return AspectRatio(
 | 
					 | 
				
			||||||
                            aspectRatio: isAudio
 | 
					 | 
				
			||||||
                                ? 1
 | 
					 | 
				
			||||||
                                : _controller.value.aspectRatio,
 | 
					 | 
				
			||||||
                            child: VideoPlayer(_controller),
 | 
					 | 
				
			||||||
                          );
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                          return const CircularProgressIndicator();
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                      },
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                  ],
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                Padding(
 | 
					 | 
				
			||||||
                  padding: const EdgeInsets.symmetric(vertical: 8.0),
 | 
					 | 
				
			||||||
                  child: VideoProgressIndicator(
 | 
					 | 
				
			||||||
                    _controller,
 | 
					 | 
				
			||||||
                    allowScrubbing: true,
 | 
					 | 
				
			||||||
                  ),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                Row(
 | 
					 | 
				
			||||||
                  mainAxisAlignment: MainAxisAlignment.center,
 | 
					 | 
				
			||||||
                  children: [
 | 
					 | 
				
			||||||
                    IconButton(
 | 
					 | 
				
			||||||
                      icon: const Icon(Icons.replay_10),
 | 
					 | 
				
			||||||
                      onPressed: () {
 | 
					 | 
				
			||||||
                        _controller.seekTo(
 | 
					 | 
				
			||||||
                          _controller.value.position -
 | 
					 | 
				
			||||||
                              const Duration(seconds: 10),
 | 
					 | 
				
			||||||
                        );
 | 
					 | 
				
			||||||
                      },
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    IconButton(
 | 
					 | 
				
			||||||
                      icon: Icon(
 | 
					 | 
				
			||||||
                        _controller.value.isPlaying
 | 
					 | 
				
			||||||
                            ? Icons.pause
 | 
					 | 
				
			||||||
                            : Icons.play_arrow,
 | 
					 | 
				
			||||||
                      ),
 | 
					 | 
				
			||||||
                      onPressed: () {
 | 
					 | 
				
			||||||
                        setState(() {
 | 
					 | 
				
			||||||
                          _controller.value.isPlaying
 | 
					 | 
				
			||||||
                              ? _controller.pause()
 | 
					 | 
				
			||||||
                              : _controller.play();
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                      },
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    IconButton(
 | 
					 | 
				
			||||||
                      icon: const Icon(Icons.forward_10),
 | 
					 | 
				
			||||||
                      onPressed: () {
 | 
					 | 
				
			||||||
                        _controller.seekTo(
 | 
					 | 
				
			||||||
                          _controller.value.position +
 | 
					 | 
				
			||||||
                              const Duration(seconds: 10),
 | 
					 | 
				
			||||||
                        );
 | 
					 | 
				
			||||||
                      },
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                  ],
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
              ],
 | 
					 | 
				
			||||||
              const SizedBox(height: 16),
 | 
					 | 
				
			||||||
              Padding(
 | 
					 | 
				
			||||||
                padding: const EdgeInsets.all(16.0),
 | 
					 | 
				
			||||||
                child: Column(
 | 
					 | 
				
			||||||
                  crossAxisAlignment: CrossAxisAlignment.start,
 | 
					 | 
				
			||||||
                  children: [
 | 
					 | 
				
			||||||
                    Text(
 | 
					 | 
				
			||||||
                      '${widget.details.mime}',
 | 
					 | 
				
			||||||
                      style: const TextStyle(fontSize: 16, color: Colors.white),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    Text(
 | 
					 | 
				
			||||||
                      "Benutzername: ${widget.details.username}",
 | 
					 | 
				
			||||||
                      style: const TextStyle(fontSize: 16, color: Colors.white),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    Text(
 | 
					 | 
				
			||||||
                      "Zeitstempel: ${widget.details.stamp}",
 | 
					 | 
				
			||||||
                      style: const TextStyle(fontSize: 16, color: Colors.white),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                  ],
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
              ),
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										144
									
								
								lib/screens/detailview.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								lib/screens/detailview.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:f0ckapp/models/mediaitem.dart';
 | 
				
			||||||
 | 
					import 'package:f0ckapp/services/api.dart';
 | 
				
			||||||
 | 
					import 'package:f0ckapp/widgets/video_widget.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DetailView extends StatefulWidget {
 | 
				
			||||||
 | 
					  final int initialItemId;
 | 
				
			||||||
 | 
					  final List<MediaItem> mediaItems;
 | 
				
			||||||
 | 
					  final String mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const DetailView({
 | 
				
			||||||
 | 
					    super.key,
 | 
				
			||||||
 | 
					    required this.initialItemId,
 | 
				
			||||||
 | 
					    required this.mediaItems,
 | 
				
			||||||
 | 
					    required this.mode,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  State createState() => _DetailViewState();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _DetailViewState extends State<DetailView> {
 | 
				
			||||||
 | 
					  late PageController _pageController;
 | 
				
			||||||
 | 
					  late List<MediaItem> mediaItems;
 | 
				
			||||||
 | 
					  int currentItemId = 0;
 | 
				
			||||||
 | 
					  bool isLoading = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					    mediaItems = widget.mediaItems;
 | 
				
			||||||
 | 
					    final initialIndex = mediaItems.indexWhere(
 | 
				
			||||||
 | 
					      (item) => item.id == widget.initialItemId,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    _pageController = PageController(initialPage: initialIndex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    currentItemId = mediaItems[initialIndex].id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _pageController.addListener(() {
 | 
				
			||||||
 | 
					      final newIndex = _pageController.page?.round();
 | 
				
			||||||
 | 
					      if (newIndex != null && newIndex < mediaItems.length) {
 | 
				
			||||||
 | 
					        setState(() => currentItemId = mediaItems[newIndex].id);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (_pageController.position.pixels >=
 | 
				
			||||||
 | 
					          _pageController.position.maxScrollExtent - 100) {
 | 
				
			||||||
 | 
					        _loadMoreMedia();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> _loadMoreMedia() async {
 | 
				
			||||||
 | 
					    if (isLoading) return;
 | 
				
			||||||
 | 
					    setState(() => isLoading = true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      final newMedia = await fetchMedia(
 | 
				
			||||||
 | 
					        older: mediaItems.last.id.toString(),
 | 
				
			||||||
 | 
					        mode: widget.mode,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      if (mounted) {
 | 
				
			||||||
 | 
					        setState(() => mediaItems.addAll(newMedia));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      if (mounted) {
 | 
				
			||||||
 | 
					        ScaffoldMessenger.of(context).showSnackBar(
 | 
				
			||||||
 | 
					          SnackBar(content: Text('Fehler beim Laden weiterer Medien: $e')),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      setState(() => isLoading = false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    return Scaffold(
 | 
				
			||||||
 | 
					      backgroundColor: Color(0xFF171717),
 | 
				
			||||||
 | 
					      appBar: AppBar(
 | 
				
			||||||
 | 
					        backgroundColor: const Color(0xFF2B2B2B),
 | 
				
			||||||
 | 
					        foregroundColor: Colors.white,
 | 
				
			||||||
 | 
					        title: Text('f0ck #$currentItemId (${widget.mode})'),
 | 
				
			||||||
 | 
					        centerTitle: true,
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      body: PageView.builder(
 | 
				
			||||||
 | 
					        controller: _pageController,
 | 
				
			||||||
 | 
					        itemCount: mediaItems.length + (isLoading ? 1 : 0),
 | 
				
			||||||
 | 
					        itemBuilder: (context, index) {
 | 
				
			||||||
 | 
					          if (index >= mediaItems.length) {
 | 
				
			||||||
 | 
					            return const Center(child: CircularProgressIndicator());
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          final item = mediaItems[index];
 | 
				
			||||||
 | 
					          return SingleChildScrollView(
 | 
				
			||||||
 | 
					            child: Column(
 | 
				
			||||||
 | 
					              mainAxisAlignment: MainAxisAlignment.start,
 | 
				
			||||||
 | 
					              crossAxisAlignment: CrossAxisAlignment.center,
 | 
				
			||||||
 | 
					              children: [
 | 
				
			||||||
 | 
					                if (item.mime.startsWith('image')) ...[
 | 
				
			||||||
 | 
					                  Image.network(
 | 
				
			||||||
 | 
					                    'https://f0ck.me/b/${item.dest}',
 | 
				
			||||||
 | 
					                    fit: BoxFit.contain,
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                ] else ...[
 | 
				
			||||||
 | 
					                  VideoWidget(details: item),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                const SizedBox(height: 20),
 | 
				
			||||||
 | 
					                Text(
 | 
				
			||||||
 | 
					                  item.mime,
 | 
				
			||||||
 | 
					                  style: const TextStyle(color: Colors.white, fontSize: 18),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                const SizedBox(height: 10),
 | 
				
			||||||
 | 
					                Wrap(
 | 
				
			||||||
 | 
					                  alignment: WrapAlignment.center,
 | 
				
			||||||
 | 
					                  spacing: 5.0,
 | 
				
			||||||
 | 
					                  children: item.tags.map((tag) {
 | 
				
			||||||
 | 
					                    Color tagColor;
 | 
				
			||||||
 | 
					                    switch (tag.id) {
 | 
				
			||||||
 | 
					                      case 1:
 | 
				
			||||||
 | 
					                        tagColor = Colors.green;
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                      case 2:
 | 
				
			||||||
 | 
					                        tagColor = Colors.red;
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                      default:
 | 
				
			||||||
 | 
					                        tagColor = Color(0xFF090909);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return Chip(
 | 
				
			||||||
 | 
					                      label: Text(tag.tag),
 | 
				
			||||||
 | 
					                      backgroundColor: tagColor,
 | 
				
			||||||
 | 
					                      labelStyle: const TextStyle(color: Colors.white),
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                  }).toList(),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                const SizedBox(height: 40),
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,16 +4,16 @@ import 'package:http/http.dart' as http;
 | 
				
			|||||||
import 'package:f0ckapp/models/mediaitem.dart';
 | 
					import 'package:f0ckapp/models/mediaitem.dart';
 | 
				
			||||||
import 'package:f0ckapp/models/mediaitem_detail.dart';
 | 
					import 'package:f0ckapp/models/mediaitem_detail.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Future<List<MediaItem>> fetchMedia({String? older}) async {
 | 
					Future<List<MediaItem>> fetchMedia({String? older, String? mode}) async {
 | 
				
			||||||
  final Uri url = Uri.parse('https://f0ck.me/api/v2/items/get')
 | 
					  final Uri url = Uri.parse('https://api.f0ck.me/items/get')
 | 
				
			||||||
      .replace(queryParameters: {
 | 
					    .replace(queryParameters: {
 | 
				
			||||||
    if (older != null) 'older': older,
 | 
					      'mode': mode ?? 'image',
 | 
				
			||||||
  });
 | 
					      if (older != null) 'older': older,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  final response = await http.get(url);
 | 
					  final response = await http.get(url);
 | 
				
			||||||
  if (response.statusCode == 200) {
 | 
					  if (response.statusCode == 200) {
 | 
				
			||||||
    final Map<String, dynamic> jsonResponse = jsonDecode(response.body);
 | 
					    final List<dynamic> jsonList = jsonDecode(response.body);
 | 
				
			||||||
    final List<dynamic> jsonList = jsonResponse['items'];
 | 
					 | 
				
			||||||
    return jsonList.map((item) => MediaItem.fromJson(item)).toList();
 | 
					    return jsonList.map((item) => MediaItem.fromJson(item)).toList();
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    throw Exception('Fehler beim Abrufen der Medien: ${response.statusCode}');
 | 
					    throw Exception('Fehler beim Abrufen der Medien: ${response.statusCode}');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,13 @@
 | 
				
			|||||||
import 'package:f0ckapp/models/mediaitem_detail.dart';
 | 
					import 'package:f0ckapp/models/mediaitem.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:video_player/video_player.dart';
 | 
					import 'package:video_player/video_player.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VideoWidget extends StatefulWidget {
 | 
					class VideoWidget extends StatefulWidget {
 | 
				
			||||||
  final MediaItemDetail details;
 | 
					  final MediaItem details;
 | 
				
			||||||
  //const VideoWidget({super.key, required this.details}): super(key: key);
 | 
					  const VideoWidget({super.key, required this.details});
 | 
				
			||||||
  const VideoWidget({Key? key, required this.details}) : super(key: key);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  _VideoWidgetState createState() => _VideoWidgetState();
 | 
					  State createState() => _VideoWidgetState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _VideoWidgetState extends State<VideoWidget> {
 | 
					class _VideoWidgetState extends State<VideoWidget> {
 | 
				
			||||||
@@ -18,11 +17,15 @@ class _VideoWidgetState extends State<VideoWidget> {
 | 
				
			|||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _controller = VideoPlayerController.networkUrl(
 | 
					    _controller =
 | 
				
			||||||
      Uri.parse(widget.details.mediaUrl)
 | 
					        VideoPlayerController.networkUrl(
 | 
				
			||||||
    )..initialize().then((_) {
 | 
					            Uri.parse('https://f0ck.me/b/${widget.details.dest}'),
 | 
				
			||||||
      setState(() {});
 | 
					          )
 | 
				
			||||||
    });
 | 
					          ..initialize().then((_) {
 | 
				
			||||||
 | 
					            setState(() {});
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _controller.addListener(() => setState(() {}));
 | 
				
			||||||
    _controller.play();
 | 
					    _controller.play();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,20 +35,137 @@ class _VideoWidgetState extends State<VideoWidget> {
 | 
				
			|||||||
    super.dispose();
 | 
					    super.dispose();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String _formatDuration(Duration duration) {
 | 
				
			||||||
 | 
					    String twoDigits(int n) => n.toString().padLeft(2, '0');
 | 
				
			||||||
 | 
					    final minutes = twoDigits(duration.inMinutes.remainder(60));
 | 
				
			||||||
 | 
					    final seconds = twoDigits(duration.inSeconds.remainder(60));
 | 
				
			||||||
 | 
					    return "$minutes:$seconds";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return Scaffold(
 | 
					    bool isAudio = widget.details.mime.startsWith('audio');
 | 
				
			||||||
      backgroundColor: Colors.white,
 | 
					
 | 
				
			||||||
      body: Center(
 | 
					    return Column(
 | 
				
			||||||
        child: _controller.value.isInitialized
 | 
					      mainAxisSize: MainAxisSize.min,
 | 
				
			||||||
          ? AspectRatio(
 | 
					      children: [
 | 
				
			||||||
              aspectRatio: _controller.value.aspectRatio,
 | 
					        AspectRatio(
 | 
				
			||||||
              child: VideoPlayer(_controller),
 | 
					          aspectRatio: isAudio ? 1.0 : _controller.value.aspectRatio,
 | 
				
			||||||
            )
 | 
					          child: Stack(
 | 
				
			||||||
          : Center(
 | 
					            alignment: Alignment.center,
 | 
				
			||||||
              child: CircularProgressIndicator(),
 | 
					            children: [
 | 
				
			||||||
            ),
 | 
					              GestureDetector(
 | 
				
			||||||
      ),
 | 
					                onTap: () {
 | 
				
			||||||
 | 
					                  setState(() {
 | 
				
			||||||
 | 
					                    _controller.value.isPlaying
 | 
				
			||||||
 | 
					                        ? _controller.pause()
 | 
				
			||||||
 | 
					                        : _controller.play();
 | 
				
			||||||
 | 
					                  });
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                child: isAudio
 | 
				
			||||||
 | 
					                    ? Image.network(
 | 
				
			||||||
 | 
					                        'https://f0ck.me/ca/${widget.details.id}.webp',
 | 
				
			||||||
 | 
					                        fit: BoxFit.cover,
 | 
				
			||||||
 | 
					                      )
 | 
				
			||||||
 | 
					                    : VideoPlayer(_controller),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Align(
 | 
				
			||||||
 | 
					                alignment: Alignment.bottomCenter,
 | 
				
			||||||
 | 
					                child: Stack(
 | 
				
			||||||
 | 
					                  children: [
 | 
				
			||||||
 | 
					                    SizedBox(
 | 
				
			||||||
 | 
					                      height: 10,
 | 
				
			||||||
 | 
					                      child: VideoProgressIndicator(
 | 
				
			||||||
 | 
					                        _controller,
 | 
				
			||||||
 | 
					                        allowScrubbing: true,
 | 
				
			||||||
 | 
					                        padding: EdgeInsets.only(bottom: 0),
 | 
				
			||||||
 | 
					                        colors: VideoProgressColors(
 | 
				
			||||||
 | 
					                          playedColor: Colors.red,
 | 
				
			||||||
 | 
					                          bufferedColor: Colors.grey,
 | 
				
			||||||
 | 
					                          backgroundColor: Colors.black.withValues(alpha: 0.5),
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    Positioned(
 | 
				
			||||||
 | 
					                      left:
 | 
				
			||||||
 | 
					                          (_controller.value.position.inMilliseconds /
 | 
				
			||||||
 | 
					                                  _controller.value.duration.inMilliseconds) *
 | 
				
			||||||
 | 
					                              MediaQuery.of(context).size.width -
 | 
				
			||||||
 | 
					                          6,
 | 
				
			||||||
 | 
					                      bottom: -1,
 | 
				
			||||||
 | 
					                      child: Container(
 | 
				
			||||||
 | 
					                        width: 12,
 | 
				
			||||||
 | 
					                        height: 12,
 | 
				
			||||||
 | 
					                        decoration: BoxDecoration(
 | 
				
			||||||
 | 
					                          shape: BoxShape.circle,
 | 
				
			||||||
 | 
					                          color: Colors.white,
 | 
				
			||||||
 | 
					                          border: Border.all(color: Colors.red, width: 2),
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                  ],
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        SizedBox(
 | 
				
			||||||
 | 
					          child: Row(
 | 
				
			||||||
 | 
					            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
 | 
					            crossAxisAlignment: CrossAxisAlignment.start,
 | 
				
			||||||
 | 
					            children: [
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                _formatDuration(_controller.value.position),
 | 
				
			||||||
 | 
					                style: const TextStyle(color: Colors.white),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Row(
 | 
				
			||||||
 | 
					                mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
 | 
					                children: [
 | 
				
			||||||
 | 
					                  IconButton(
 | 
				
			||||||
 | 
					                    icon: const Icon(Icons.replay_10),
 | 
				
			||||||
 | 
					                    color: Colors.white,
 | 
				
			||||||
 | 
					                    onPressed: () {
 | 
				
			||||||
 | 
					                      _controller.seekTo(
 | 
				
			||||||
 | 
					                        _controller.value.position -
 | 
				
			||||||
 | 
					                            const Duration(seconds: 10),
 | 
				
			||||||
 | 
					                      );
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                  IconButton(
 | 
				
			||||||
 | 
					                    color: Colors.white,
 | 
				
			||||||
 | 
					                    icon: Icon(
 | 
				
			||||||
 | 
					                      _controller.value.isPlaying
 | 
				
			||||||
 | 
					                          ? Icons.pause
 | 
				
			||||||
 | 
					                          : Icons.play_arrow,
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    onPressed: () {
 | 
				
			||||||
 | 
					                      setState(() {
 | 
				
			||||||
 | 
					                        _controller.value.isPlaying
 | 
				
			||||||
 | 
					                            ? _controller.pause()
 | 
				
			||||||
 | 
					                            : _controller.play();
 | 
				
			||||||
 | 
					                      });
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                  IconButton(
 | 
				
			||||||
 | 
					                    color: Colors.white,
 | 
				
			||||||
 | 
					                    icon: const Icon(Icons.forward_10),
 | 
				
			||||||
 | 
					                    onPressed: () {
 | 
				
			||||||
 | 
					                      _controller.seekTo(
 | 
				
			||||||
 | 
					                        _controller.value.position +
 | 
				
			||||||
 | 
					                            const Duration(seconds: 10),
 | 
				
			||||||
 | 
					                      );
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                _formatDuration(_controller.value.duration),
 | 
				
			||||||
 | 
					                style: const TextStyle(color: Colors.white),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,12 +5,8 @@
 | 
				
			|||||||
import FlutterMacOS
 | 
					import FlutterMacOS
 | 
				
			||||||
import Foundation
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import path_provider_foundation
 | 
					 | 
				
			||||||
import sqflite_darwin
 | 
					 | 
				
			||||||
import video_player_avfoundation
 | 
					import video_player_avfoundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 | 
					func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 | 
				
			||||||
  PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
 | 
					 | 
				
			||||||
  SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
 | 
					 | 
				
			||||||
  FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
 | 
					  FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										208
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								pubspec.lock
									
									
									
									
									
								
							@@ -17,30 +17,6 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.1.2"
 | 
					    version: "2.1.2"
 | 
				
			||||||
  cached_network_image:
 | 
					 | 
				
			||||||
    dependency: "direct main"
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: cached_network_image
 | 
					 | 
				
			||||||
      sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "3.4.1"
 | 
					 | 
				
			||||||
  cached_network_image_platform_interface:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: cached_network_image_platform_interface
 | 
					 | 
				
			||||||
      sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "4.1.1"
 | 
					 | 
				
			||||||
  cached_network_image_web:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: cached_network_image_web
 | 
					 | 
				
			||||||
      sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "1.3.1"
 | 
					 | 
				
			||||||
  characters:
 | 
					  characters:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -65,14 +41,6 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.19.1"
 | 
					    version: "1.19.1"
 | 
				
			||||||
  crypto:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: crypto
 | 
					 | 
				
			||||||
      sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "3.0.6"
 | 
					 | 
				
			||||||
  csslib:
 | 
					  csslib:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -97,43 +65,11 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.3.3"
 | 
					    version: "1.3.3"
 | 
				
			||||||
  ffi:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: ffi
 | 
					 | 
				
			||||||
      sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.1.4"
 | 
					 | 
				
			||||||
  file:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: file
 | 
					 | 
				
			||||||
      sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "7.0.1"
 | 
					 | 
				
			||||||
  fixnum:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: fixnum
 | 
					 | 
				
			||||||
      sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "1.1.1"
 | 
					 | 
				
			||||||
  flutter:
 | 
					  flutter:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
    source: sdk
 | 
					    source: sdk
 | 
				
			||||||
    version: "0.0.0"
 | 
					    version: "0.0.0"
 | 
				
			||||||
  flutter_cache_manager:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: flutter_cache_manager
 | 
					 | 
				
			||||||
      sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "3.4.1"
 | 
					 | 
				
			||||||
  flutter_lints:
 | 
					  flutter_lints:
 | 
				
			||||||
    dependency: "direct dev"
 | 
					    dependency: "direct dev"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -232,14 +168,6 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.16.0"
 | 
					    version: "1.16.0"
 | 
				
			||||||
  octo_image:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: octo_image
 | 
					 | 
				
			||||||
      sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.1.0"
 | 
					 | 
				
			||||||
  path:
 | 
					  path:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -248,62 +176,6 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.9.1"
 | 
					    version: "1.9.1"
 | 
				
			||||||
  path_provider:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: path_provider
 | 
					 | 
				
			||||||
      sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.1.5"
 | 
					 | 
				
			||||||
  path_provider_android:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: path_provider_android
 | 
					 | 
				
			||||||
      sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.2.17"
 | 
					 | 
				
			||||||
  path_provider_foundation:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: path_provider_foundation
 | 
					 | 
				
			||||||
      sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.4.1"
 | 
					 | 
				
			||||||
  path_provider_linux:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: path_provider_linux
 | 
					 | 
				
			||||||
      sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.2.1"
 | 
					 | 
				
			||||||
  path_provider_platform_interface:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: path_provider_platform_interface
 | 
					 | 
				
			||||||
      sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.1.2"
 | 
					 | 
				
			||||||
  path_provider_windows:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: path_provider_windows
 | 
					 | 
				
			||||||
      sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.3.0"
 | 
					 | 
				
			||||||
  platform:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: platform
 | 
					 | 
				
			||||||
      sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "3.1.6"
 | 
					 | 
				
			||||||
  plugin_platform_interface:
 | 
					  plugin_platform_interface:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -312,14 +184,6 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.1.8"
 | 
					    version: "2.1.8"
 | 
				
			||||||
  rxdart:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: rxdart
 | 
					 | 
				
			||||||
      sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "0.28.0"
 | 
					 | 
				
			||||||
  sky_engine:
 | 
					  sky_engine:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
@@ -333,54 +197,6 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.10.1"
 | 
					    version: "1.10.1"
 | 
				
			||||||
  sprintf:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: sprintf
 | 
					 | 
				
			||||||
      sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "7.0.0"
 | 
					 | 
				
			||||||
  sqflite:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: sqflite
 | 
					 | 
				
			||||||
      sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.4.2"
 | 
					 | 
				
			||||||
  sqflite_android:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: sqflite_android
 | 
					 | 
				
			||||||
      sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.4.1"
 | 
					 | 
				
			||||||
  sqflite_common:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: sqflite_common
 | 
					 | 
				
			||||||
      sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.5.5"
 | 
					 | 
				
			||||||
  sqflite_darwin:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: sqflite_darwin
 | 
					 | 
				
			||||||
      sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.4.2"
 | 
					 | 
				
			||||||
  sqflite_platform_interface:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: sqflite_platform_interface
 | 
					 | 
				
			||||||
      sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "2.4.0"
 | 
					 | 
				
			||||||
  stack_trace:
 | 
					  stack_trace:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -405,14 +221,6 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.4.1"
 | 
					    version: "1.4.1"
 | 
				
			||||||
  synchronized:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: synchronized
 | 
					 | 
				
			||||||
      sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "3.3.1"
 | 
					 | 
				
			||||||
  term_glyph:
 | 
					  term_glyph:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -437,14 +245,6 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.4.0"
 | 
					    version: "1.4.0"
 | 
				
			||||||
  uuid:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: uuid
 | 
					 | 
				
			||||||
      sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "4.5.1"
 | 
					 | 
				
			||||||
  vector_math:
 | 
					  vector_math:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -509,14 +309,6 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.1.1"
 | 
					    version: "1.1.1"
 | 
				
			||||||
  xdg_directories:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: xdg_directories
 | 
					 | 
				
			||||||
      sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "1.1.0"
 | 
					 | 
				
			||||||
sdks:
 | 
					sdks:
 | 
				
			||||||
  dart: ">=3.9.0-172.0.dev <4.0.0"
 | 
					  dart: ">=3.9.0-172.0.dev <4.0.0"
 | 
				
			||||||
  flutter: ">=3.29.0"
 | 
					  flutter: ">=3.29.0"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
 | 
				
			|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 | 
					# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 | 
				
			||||||
# In Windows, build-name is used as the major, minor, and patch parts
 | 
					# In Windows, build-name is used as the major, minor, and patch parts
 | 
				
			||||||
# of the product and file versions while build-number is used as the build suffix.
 | 
					# of the product and file versions while build-number is used as the build suffix.
 | 
				
			||||||
version: 1.0.5
 | 
					version: 1.0.12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
environment:
 | 
					environment:
 | 
				
			||||||
  sdk: ^3.9.0-172.0.dev
 | 
					  sdk: ^3.9.0-172.0.dev
 | 
				
			||||||
@@ -31,7 +31,6 @@ dependencies:
 | 
				
			|||||||
  flutter:
 | 
					  flutter:
 | 
				
			||||||
    sdk: flutter
 | 
					    sdk: flutter
 | 
				
			||||||
  http: ^1.4.0  # API-Anfragen
 | 
					  http: ^1.4.0  # API-Anfragen
 | 
				
			||||||
  cached_network_image: ^3.2.3  # Bilder effizient laden
 | 
					 | 
				
			||||||
  video_player: ^2.2.10
 | 
					  video_player: ^2.2.10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # The following adds the Cupertino Icons font to your application.
 | 
					  # The following adds the Cupertino Icons font to your application.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user