- preload adjacent media
This commit is contained in:
		@@ -5,6 +5,7 @@ import 'package:f0ckapp/services/Api.dart';
 | 
				
			|||||||
import 'package:f0ckapp/widgets/VideoWidget.dart';
 | 
					import 'package:f0ckapp/widgets/VideoWidget.dart';
 | 
				
			||||||
import 'package:f0ckapp/utils/SmartRefreshIndicator.dart';
 | 
					import 'package:f0ckapp/utils/SmartRefreshIndicator.dart';
 | 
				
			||||||
import 'package:f0ckapp/utils/PageTransformer.dart';
 | 
					import 'package:f0ckapp/utils/PageTransformer.dart';
 | 
				
			||||||
 | 
					import 'package:flutter_cache_manager/flutter_cache_manager.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DetailView extends StatefulWidget {
 | 
					class DetailView extends StatefulWidget {
 | 
				
			||||||
  final int initialItemId;
 | 
					  final int initialItemId;
 | 
				
			||||||
@@ -46,18 +47,42 @@ class _DetailViewState extends State<DetailView> {
 | 
				
			|||||||
    _pageController = PageController(initialPage: initialIndex);
 | 
					    _pageController = PageController(initialPage: initialIndex);
 | 
				
			||||||
    currentItemId = mediaItems[initialIndex].id;
 | 
					    currentItemId = mediaItems[initialIndex].id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _pageController.addListener(_onPageScroll);
 | 
					    int? lastLoadedIndex;
 | 
				
			||||||
 | 
					    _pageController.addListener(() async {
 | 
				
			||||||
 | 
					      final newIndex = _pageController.page?.round();
 | 
				
			||||||
 | 
					      if (newIndex != null &&
 | 
				
			||||||
 | 
					          newIndex < mediaItems.length &&
 | 
				
			||||||
 | 
					          newIndex != lastLoadedIndex) {
 | 
				
			||||||
 | 
					        setState(() => currentItemId = mediaItems[newIndex].id);
 | 
				
			||||||
 | 
					        lastLoadedIndex = newIndex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _preloadAdjacentMedia(newIndex);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (_pageController.position.pixels >=
 | 
				
			||||||
 | 
					          _pageController.position.maxScrollExtent - 100) {
 | 
				
			||||||
 | 
					        _loadMoreMedia();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _preloadAdjacentMedia(initialIndex);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _onPageScroll() {
 | 
					  void _preloadAdjacentMedia(int index) async {
 | 
				
			||||||
    final newIndex = _pageController.page?.round();
 | 
					    if (index + 1 < mediaItems.length) {
 | 
				
			||||||
    if (newIndex != null && newIndex < mediaItems.length) {
 | 
					      final nextUrl = mediaItems[index + 1].mediaUrl;
 | 
				
			||||||
      setState(() => currentItemId = mediaItems[newIndex].id);
 | 
					      if (await DefaultCacheManager().getFileFromCache(nextUrl) == null) {
 | 
				
			||||||
 | 
					        await DefaultCacheManager().downloadFile(nextUrl);
 | 
				
			||||||
 | 
					        print('preload ${mediaItems[index + 1].id}');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (_pageController.position.pixels >=
 | 
					    if (index - 1 >= 0) {
 | 
				
			||||||
        _pageController.position.maxScrollExtent - 100) {
 | 
					      final prevUrl = mediaItems[index - 1].mediaUrl;
 | 
				
			||||||
      _loadMoreMedia();
 | 
					      if (await DefaultCacheManager().getFileFromCache(prevUrl) == null) {
 | 
				
			||||||
 | 
					        await DefaultCacheManager().downloadFile(prevUrl);
 | 
				
			||||||
 | 
					        print('preload ${mediaItems[index - 1].id}');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -120,11 +145,12 @@ class _DetailViewState extends State<DetailView> {
 | 
				
			|||||||
          PageTransformer(
 | 
					          PageTransformer(
 | 
				
			||||||
            controller: _pageController,
 | 
					            controller: _pageController,
 | 
				
			||||||
            pages: mediaItems.map((item) {
 | 
					            pages: mediaItems.map((item) {
 | 
				
			||||||
 | 
					              final isActive = item.id == currentItemId;
 | 
				
			||||||
              return Scaffold(
 | 
					              return Scaffold(
 | 
				
			||||||
                body: SafeArea(
 | 
					                body: SafeArea(
 | 
				
			||||||
                  child: SmartRefreshIndicator(
 | 
					                  child: SmartRefreshIndicator(
 | 
				
			||||||
                    onRefresh: _refreshMediaItem,
 | 
					                    onRefresh: _refreshMediaItem,
 | 
				
			||||||
                    child: _buildMediaItem(item),
 | 
					                    child: _buildMediaItem(item, isActive),
 | 
				
			||||||
                  ),
 | 
					                  ),
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
              );
 | 
					              );
 | 
				
			||||||
@@ -169,7 +195,7 @@ class _DetailViewState extends State<DetailView> {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget _buildMediaItem(MediaItem item) {
 | 
					  Widget _buildMediaItem(MediaItem item, bool isActive) {
 | 
				
			||||||
    return SingleChildScrollView(
 | 
					    return SingleChildScrollView(
 | 
				
			||||||
      child: Column(
 | 
					      child: Column(
 | 
				
			||||||
        mainAxisAlignment: MainAxisAlignment.center,
 | 
					        mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
@@ -182,7 +208,7 @@ class _DetailViewState extends State<DetailView> {
 | 
				
			|||||||
              errorWidget: (context, url, error) => Icon(Icons.error),
 | 
					              errorWidget: (context, url, error) => Icon(Icons.error),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
          else
 | 
					          else
 | 
				
			||||||
            VideoWidget(details: item),
 | 
					            VideoWidget(details: item, isActive: isActive),
 | 
				
			||||||
          const SizedBox(height: 20),
 | 
					          const SizedBox(height: 20),
 | 
				
			||||||
          Text(
 | 
					          Text(
 | 
				
			||||||
            item.mime,
 | 
					            item.mime,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,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.24+24';
 | 
					  final String _version = '1.0.25+25';
 | 
				
			||||||
  List<MediaItem> mediaItems = [];
 | 
					  List<MediaItem> mediaItems = [];
 | 
				
			||||||
  bool isLoading = false;
 | 
					  bool isLoading = false;
 | 
				
			||||||
  Timer? _debounceTimer;
 | 
					  Timer? _debounceTimer;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,9 @@ import 'dart:async';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class VideoWidget extends StatefulWidget {
 | 
					class VideoWidget extends StatefulWidget {
 | 
				
			||||||
  final MediaItem details;
 | 
					  final MediaItem details;
 | 
				
			||||||
  const VideoWidget({super.key, required this.details});
 | 
					  final bool isActive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const VideoWidget({super.key, required this.details, required this.isActive});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  State createState() => _VideoWidgetState();
 | 
					  State createState() => _VideoWidgetState();
 | 
				
			||||||
@@ -32,7 +34,9 @@ class _VideoWidgetState extends State<VideoWidget> {
 | 
				
			|||||||
    setState(() {});
 | 
					    setState(() {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _controller.addListener(() => setState(() {}));
 | 
					    _controller.addListener(() => setState(() {}));
 | 
				
			||||||
    _controller.play();
 | 
					    if (widget.isActive) {
 | 
				
			||||||
 | 
					      _controller.play();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    _controller.setLooping(true);
 | 
					    _controller.setLooping(true);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,6 +47,16 @@ class _VideoWidgetState extends State<VideoWidget> {
 | 
				
			|||||||
    super.dispose();
 | 
					    super.dispose();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void didUpdateWidget(VideoWidget oldWidget) {
 | 
				
			||||||
 | 
					    super.didUpdateWidget(oldWidget);
 | 
				
			||||||
 | 
					    if (widget.isActive) {
 | 
				
			||||||
 | 
					      _controller.play();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      _controller.pause();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _onTap({bool ctrlButton = false}) {
 | 
					  void _onTap({bool ctrlButton = false}) {
 | 
				
			||||||
    if (!ctrlButton) {
 | 
					    if (!ctrlButton) {
 | 
				
			||||||
      setState(() => _showControls = !_showControls);
 | 
					      setState(() => _showControls = !_showControls);
 | 
				
			||||||
@@ -81,7 +95,7 @@ class _VideoWidgetState extends State<VideoWidget> {
 | 
				
			|||||||
                        errorWidget: (context, url, error) => Image.asset(
 | 
					                        errorWidget: (context, url, error) => Image.asset(
 | 
				
			||||||
                          'assets/images/music.webp',
 | 
					                          'assets/images/music.webp',
 | 
				
			||||||
                          fit: BoxFit.contain,
 | 
					                          fit: BoxFit.contain,
 | 
				
			||||||
                          width: double.infinity
 | 
					                          width: double.infinity,
 | 
				
			||||||
                        ),
 | 
					                        ),
 | 
				
			||||||
                      )
 | 
					                      )
 | 
				
			||||||
                    : _controller.value.isInitialized
 | 
					                    : _controller.value.isInitialized
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.24+24
 | 
					version: 1.0.25+25
 | 
				
			||||||
 | 
					
 | 
				
			||||||
environment:
 | 
					environment:
 | 
				
			||||||
  sdk: ^3.9.0-100.2.beta
 | 
					  sdk: ^3.9.0-100.2.beta
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user