1.0.17
- new appicon - smartRefreshIndicator (https://github.com/flutter/flutter/issues/65356#issuecomment-2410727567)
| 
		 Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 3.5 KiB  | 
| 
		 Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.9 KiB  | 
| 
		 Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 5.2 KiB  | 
| 
		 Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 9.6 KiB  | 
| 
		 Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 15 KiB  | 
| 
		 Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 16 KiB  | 
| 
		 Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 16 KiB  | 
| 
		 Before Width: | Height: | Size: 680 KiB After Width: | Height: | Size: 447 KiB  | 
| 
		 Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 18 KiB  | 
| 
		 Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 20 KiB  | 
| 
		 Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 21 KiB  | 
| 
		 Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 23 KiB  | 
| 
		 Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 28 KiB  | 
| 
		 Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 31 KiB  | 
| 
		 Before Width: | Height: | Size: 906 B After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 35 KiB  | 
| 
		 Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 38 KiB  | 
| 
		 Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 40 KiB  | 
| 
		 Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 47 KiB  | 
| 
		 Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.8 KiB  | 
| 
		 Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 53 KiB  | 
| 
		 Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 62 KiB  | 
| 
		 Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 68 KiB  | 
| 
		 Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 72 KiB  | 
| 
		 Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.8 KiB  | 
| 
		 Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.8 KiB  | 
| 
		 Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.0 KiB  | 
| 
		 Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 5.7 KiB  | 
| 
		 Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 192 KiB  | 
| 
		 Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 6.1 KiB  | 
| 
		 Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 6.6 KiB  | 
| 
		 Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 6.9 KiB  | 
| 
		 Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 7.3 KiB  | 
| 
		 Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 7.7 KiB  | 
| 
		 Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 8.7 KiB  | 
| 
		 Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.0 KiB  | 
| 
		 Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB  | 
| 
		 Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 10 KiB  | 
| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB  | 
| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB  | 
| 
		 Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 14 KiB  | 
@@ -15,6 +15,7 @@ class F0ckApp extends StatelessWidget {
 | 
				
			|||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return MaterialApp(
 | 
					    return MaterialApp(
 | 
				
			||||||
 | 
					      debugShowCheckedModeBanner: false,
 | 
				
			||||||
      theme: ThemeData(
 | 
					      theme: ThemeData(
 | 
				
			||||||
        scaffoldBackgroundColor: const Color.fromARGB(255, 23, 23, 23),
 | 
					        scaffoldBackgroundColor: const Color.fromARGB(255, 23, 23, 23),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ class MediaGrid extends StatefulWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class _MediaGridState extends State<MediaGrid> {
 | 
					class _MediaGridState extends State<MediaGrid> {
 | 
				
			||||||
  final ScrollController _scrollController = ScrollController();
 | 
					  final ScrollController _scrollController = ScrollController();
 | 
				
			||||||
  final String _version = '1.0.15';
 | 
					  final String _version = '1.0.17';
 | 
				
			||||||
  List<MediaItem> mediaItems = [];
 | 
					  List<MediaItem> mediaItems = [];
 | 
				
			||||||
  bool isLoading = false;
 | 
					  bool isLoading = false;
 | 
				
			||||||
  Timer? _debounceTimer;
 | 
					  Timer? _debounceTimer;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 | 
				
			|||||||
import 'package:f0ckapp/models/mediaitem.dart';
 | 
					import 'package:f0ckapp/models/mediaitem.dart';
 | 
				
			||||||
import 'package:f0ckapp/services/api.dart';
 | 
					import 'package:f0ckapp/services/api.dart';
 | 
				
			||||||
import 'package:f0ckapp/widgets/video_widget.dart';
 | 
					import 'package:f0ckapp/widgets/video_widget.dart';
 | 
				
			||||||
 | 
					import 'package:f0ckapp/utils/SmartRefreshIndicator.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DetailView extends StatefulWidget {
 | 
					class DetailView extends StatefulWidget {
 | 
				
			||||||
  final int initialItemId;
 | 
					  final int initialItemId;
 | 
				
			||||||
@@ -26,33 +27,32 @@ class DetailView extends StatefulWidget {
 | 
				
			|||||||
class _DetailViewState extends State<DetailView> {
 | 
					class _DetailViewState extends State<DetailView> {
 | 
				
			||||||
  late PageController _pageController;
 | 
					  late PageController _pageController;
 | 
				
			||||||
  late List<MediaItem> mediaItems;
 | 
					  late List<MediaItem> mediaItems;
 | 
				
			||||||
  final List<String> _modes = ["sfw", "nsfw", "untagged", "all"];
 | 
					 | 
				
			||||||
  int currentItemId = 0;
 | 
					  int currentItemId = 0;
 | 
				
			||||||
  bool isLoading = false;
 | 
					  bool isLoading = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    mediaItems = widget.mediaItems;
 | 
					    mediaItems = widget.mediaItems;
 | 
				
			||||||
    final initialIndex = mediaItems.indexWhere(
 | 
					    final initialIndex = mediaItems.indexWhere(
 | 
				
			||||||
      (item) => item.id == widget.initialItemId,
 | 
					      (item) => item.id == widget.initialItemId,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    _pageController = PageController(initialPage: initialIndex);
 | 
					    _pageController = PageController(initialPage: initialIndex);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    currentItemId = mediaItems[initialIndex].id;
 | 
					    currentItemId = mediaItems[initialIndex].id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _pageController.addListener(() {
 | 
					    _pageController.addListener(_onPageScroll);
 | 
				
			||||||
      final newIndex = _pageController.page?.round();
 | 
					  }
 | 
				
			||||||
      if (newIndex != null && newIndex < mediaItems.length) {
 | 
					 | 
				
			||||||
        setState(() => currentItemId = mediaItems[newIndex].id);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (_pageController.position.pixels >=
 | 
					  void _onPageScroll() {
 | 
				
			||||||
          _pageController.position.maxScrollExtent - 100) {
 | 
					    final newIndex = _pageController.page?.round();
 | 
				
			||||||
        _loadMoreMedia();
 | 
					    if (newIndex != null && newIndex < mediaItems.length) {
 | 
				
			||||||
      }
 | 
					      setState(() => currentItemId = mediaItems[newIndex].id);
 | 
				
			||||||
    });
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (_pageController.position.pixels >=
 | 
				
			||||||
 | 
					        _pageController.position.maxScrollExtent - 100) {
 | 
				
			||||||
 | 
					      _loadMoreMedia();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<void> _loadMoreMedia() async {
 | 
					  Future<void> _loadMoreMedia() async {
 | 
				
			||||||
@@ -66,15 +66,11 @@ class _DetailViewState extends State<DetailView> {
 | 
				
			|||||||
        mode: widget.mode,
 | 
					        mode: widget.mode,
 | 
				
			||||||
        random: widget.random,
 | 
					        random: widget.random,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      if (mounted) {
 | 
					      if (mounted && newMedia.isNotEmpty) {
 | 
				
			||||||
        setState(() => mediaItems.addAll(newMedia));
 | 
					        setState(() => mediaItems.addAll(newMedia));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      if (mounted) {
 | 
					      _showError("Fehler beim Laden weiterer Medien: $e");
 | 
				
			||||||
        ScaffoldMessenger.of(context).showSnackBar(
 | 
					 | 
				
			||||||
          SnackBar(content: Text('Fehler beim Laden weiterer Medien: $e')),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
      setState(() => isLoading = false);
 | 
					      setState(() => isLoading = false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -84,21 +80,21 @@ class _DetailViewState extends State<DetailView> {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final updatedItem = await fetchMediaDetail(currentItemId);
 | 
					      final updatedItem = await fetchMediaDetail(currentItemId);
 | 
				
			||||||
      if (mounted) {
 | 
					      if (mounted) {
 | 
				
			||||||
        setState(() {
 | 
					        final index = mediaItems.indexWhere((item) => item.id == currentItemId);
 | 
				
			||||||
          final index = mediaItems.indexWhere(
 | 
					        if (index != -1) {
 | 
				
			||||||
            (item) => item.id == currentItemId,
 | 
					          setState(() => mediaItems[index] = updatedItem);
 | 
				
			||||||
          );
 | 
					        }
 | 
				
			||||||
          if (index != -1) {
 | 
					 | 
				
			||||||
            mediaItems[index] = updatedItem;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      if (mounted) {
 | 
					      _showError("Fehler beim Aktualisieren des Items: $e");
 | 
				
			||||||
        ScaffoldMessenger.of(context).showSnackBar(
 | 
					    }
 | 
				
			||||||
          SnackBar(content: Text('Fehler beim Aktualisieren des Items: $e')),
 | 
					  }
 | 
				
			||||||
        );
 | 
					
 | 
				
			||||||
      }
 | 
					  void _showError(String message) {
 | 
				
			||||||
 | 
					    if (mounted) {
 | 
				
			||||||
 | 
					      ScaffoldMessenger.of(
 | 
				
			||||||
 | 
					        context,
 | 
				
			||||||
 | 
					      ).showSnackBar(SnackBar(content: Text(message)));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -109,70 +105,60 @@ class _DetailViewState extends State<DetailView> {
 | 
				
			|||||||
      appBar: AppBar(
 | 
					      appBar: AppBar(
 | 
				
			||||||
        backgroundColor: const Color(0xFF2B2B2B),
 | 
					        backgroundColor: const Color(0xFF2B2B2B),
 | 
				
			||||||
        foregroundColor: Colors.white,
 | 
					        foregroundColor: Colors.white,
 | 
				
			||||||
        title: Text(
 | 
					        title: Text('f0ck #$currentItemId (${widget.type})'),
 | 
				
			||||||
          'f0ck #$currentItemId (${widget.type}, ${_modes[widget.mode]})',
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        centerTitle: true,
 | 
					        centerTitle: true,
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
      body: RefreshIndicator(
 | 
					      body: PageView.builder(
 | 
				
			||||||
        onRefresh: _refreshMediaItem,
 | 
					        controller: _pageController,
 | 
				
			||||||
        child: SingleChildScrollView(
 | 
					        itemCount: mediaItems.length,
 | 
				
			||||||
          child: Column(
 | 
					        itemBuilder: (context, index) {
 | 
				
			||||||
            children: [
 | 
					          final MediaItem item = mediaItems[index];
 | 
				
			||||||
              SizedBox(
 | 
					          return Scaffold(
 | 
				
			||||||
                height: MediaQuery.of(context).size.height,
 | 
					            body: SafeArea(
 | 
				
			||||||
                child: PageView.builder(
 | 
					              child: SmartRefreshIndicator(
 | 
				
			||||||
                  controller: _pageController,
 | 
					                onRefresh: _refreshMediaItem,
 | 
				
			||||||
                  itemCount: mediaItems.length,
 | 
					                child: _buildMediaItem(item)
 | 
				
			||||||
                  itemBuilder: (context, index) {
 | 
					 | 
				
			||||||
                    final item = mediaItems[index];
 | 
					 | 
				
			||||||
                    return Column(
 | 
					 | 
				
			||||||
                      children: [
 | 
					 | 
				
			||||||
                        if (item.mime.startsWith('image'))
 | 
					 | 
				
			||||||
                          Image.network(item.mediaUrl, 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 = const Color(0xFF090909);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            return Chip(
 | 
					 | 
				
			||||||
                              label: Text(tag.tag),
 | 
					 | 
				
			||||||
                              backgroundColor: tagColor,
 | 
					 | 
				
			||||||
                              labelStyle: const TextStyle(color: Colors.white),
 | 
					 | 
				
			||||||
                            );
 | 
					 | 
				
			||||||
                          }).toList(),
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                        const SizedBox(height: 40),
 | 
					 | 
				
			||||||
                      ],
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                  },
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
            ],
 | 
					            ),
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Widget _buildMediaItem(MediaItem item) {
 | 
				
			||||||
 | 
					    return SingleChildScrollView(
 | 
				
			||||||
 | 
					      child: Column(
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          if (item.mime.startsWith('image'))
 | 
				
			||||||
 | 
					            Image.network(
 | 
				
			||||||
 | 
					              item.mediaUrl,
 | 
				
			||||||
 | 
					              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) {
 | 
				
			||||||
 | 
					              return Chip(
 | 
				
			||||||
 | 
					                label: Text(tag.tag),
 | 
				
			||||||
 | 
					                backgroundColor: {
 | 
				
			||||||
 | 
					                  1: Colors.green,
 | 
				
			||||||
 | 
					                  2: Colors.red
 | 
				
			||||||
 | 
					                }[tag.id] ?? const Color(0xFF090909),
 | 
				
			||||||
 | 
					                labelStyle: const TextStyle(color: Colors.white),
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            }).toList(),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          const SizedBox(height: 20),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								lib/utils/SmartRefreshIndicator.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SmartRefreshIndicator extends StatelessWidget {
 | 
				
			||||||
 | 
					  final Future<void> Function() onRefresh;
 | 
				
			||||||
 | 
					  final Widget child;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const SmartRefreshIndicator({
 | 
				
			||||||
 | 
					    super.key,
 | 
				
			||||||
 | 
					    required this.onRefresh,
 | 
				
			||||||
 | 
					    required this.child,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(context) {
 | 
				
			||||||
 | 
					    return LayoutBuilder(
 | 
				
			||||||
 | 
					      builder: (context, constraints) => RefreshIndicator(
 | 
				
			||||||
 | 
					        onRefresh: onRefresh,
 | 
				
			||||||
 | 
					        child: SingleChildScrollView(
 | 
				
			||||||
 | 
					          physics: const AlwaysScrollableScrollPhysics(),
 | 
				
			||||||
 | 
					          child: ConstrainedBox(
 | 
				
			||||||
 | 
					            constraints: BoxConstraints(minHeight: constraints.maxHeight),
 | 
				
			||||||
 | 
					            child: child,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -52,7 +52,9 @@ class _VideoWidgetState extends State<VideoWidget> {
 | 
				
			|||||||
      mainAxisSize: MainAxisSize.min,
 | 
					      mainAxisSize: MainAxisSize.min,
 | 
				
			||||||
      children: [
 | 
					      children: [
 | 
				
			||||||
        AspectRatio(
 | 
					        AspectRatio(
 | 
				
			||||||
          aspectRatio: isAudio ? 1.0 : _controller.value.aspectRatio,
 | 
					          aspectRatio: _controller.value.isInitialized
 | 
				
			||||||
 | 
					              ? _controller.value.aspectRatio
 | 
				
			||||||
 | 
					              : 9 / 16,
 | 
				
			||||||
          child: Stack(
 | 
					          child: Stack(
 | 
				
			||||||
            alignment: Alignment.center,
 | 
					            alignment: Alignment.center,
 | 
				
			||||||
            children: [
 | 
					            children: [
 | 
				
			||||||
@@ -66,104 +68,108 @@ class _VideoWidgetState extends State<VideoWidget> {
 | 
				
			|||||||
                },
 | 
					                },
 | 
				
			||||||
                child: isAudio
 | 
					                child: isAudio
 | 
				
			||||||
                    ? Image.network(widget.details.coverUrl, fit: BoxFit.cover)
 | 
					                    ? Image.network(widget.details.coverUrl, fit: BoxFit.cover)
 | 
				
			||||||
                    : VideoPlayer(_controller),
 | 
					                    : _controller.value.isInitialized
 | 
				
			||||||
 | 
					                    ? VideoPlayer(_controller)
 | 
				
			||||||
 | 
					                    : Center(child: CircularProgressIndicator()),
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
              Align(
 | 
					              if (_controller.value.isInitialized)
 | 
				
			||||||
                alignment: Alignment.bottomCenter,
 | 
					                Align(
 | 
				
			||||||
                child: Stack(
 | 
					                  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.withAlpha(128),
 | 
				
			||||||
 | 
					                          ),
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                      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),
 | 
				
			||||||
 | 
					                          ),
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        if (_controller.value.isInitialized)
 | 
				
			||||||
 | 
					          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: [
 | 
					                  children: [
 | 
				
			||||||
                    SizedBox(
 | 
					                    IconButton(
 | 
				
			||||||
                      height: 10,
 | 
					                      icon: const Icon(Icons.replay_10),
 | 
				
			||||||
                      child: VideoProgressIndicator(
 | 
					                      color: Colors.white,
 | 
				
			||||||
                        _controller,
 | 
					                      onPressed: () {
 | 
				
			||||||
                        allowScrubbing: true,
 | 
					                        _controller.seekTo(
 | 
				
			||||||
                        padding: EdgeInsets.only(bottom: 0),
 | 
					                          _controller.value.position -
 | 
				
			||||||
                        colors: VideoProgressColors(
 | 
					                              const Duration(seconds: 10),
 | 
				
			||||||
                          playedColor: Colors.red,
 | 
					                        );
 | 
				
			||||||
                          bufferedColor: Colors.grey,
 | 
					                      },
 | 
				
			||||||
                          backgroundColor: Colors.black.withValues(alpha: 0.5),
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                      ),
 | 
					 | 
				
			||||||
                    ),
 | 
					                    ),
 | 
				
			||||||
                    Positioned(
 | 
					                    IconButton(
 | 
				
			||||||
                      left:
 | 
					                      color: Colors.white,
 | 
				
			||||||
                          (_controller.value.position.inMilliseconds /
 | 
					                      icon: Icon(
 | 
				
			||||||
                                  _controller.value.duration.inMilliseconds) *
 | 
					                        _controller.value.isPlaying
 | 
				
			||||||
                              MediaQuery.of(context).size.width -
 | 
					                            ? Icons.pause
 | 
				
			||||||
                          6,
 | 
					                            : Icons.play_arrow,
 | 
				
			||||||
                      bottom: -1,
 | 
					 | 
				
			||||||
                      child: Container(
 | 
					 | 
				
			||||||
                        width: 12,
 | 
					 | 
				
			||||||
                        height: 12,
 | 
					 | 
				
			||||||
                        decoration: BoxDecoration(
 | 
					 | 
				
			||||||
                          shape: BoxShape.circle,
 | 
					 | 
				
			||||||
                          color: Colors.white,
 | 
					 | 
				
			||||||
                          border: Border.all(color: Colors.red, width: 2),
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                      ),
 | 
					                      ),
 | 
				
			||||||
 | 
					                      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),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        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),
 | 
					 | 
				
			||||||
              ),
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.15
 | 
					version: 1.0.17
 | 
				
			||||||
 | 
					
 | 
				
			||||||
environment:
 | 
					environment:
 | 
				
			||||||
  sdk: ^3.9.0-172.0.dev
 | 
					  sdk: ^3.9.0-172.0.dev
 | 
				
			||||||
 
 | 
				
			|||||||