detailview lol
This commit is contained in:
		@@ -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),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user