mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-22 14:38:06 +02:00
feat: add HeroCarousel widget for displaying interactive, auto-playing content.
This commit is contained in:
1 parent
e0cdf69674
commit
20f8904e78
3 files changed
+26
-20
No files matched your search
@@ -169,14 +169,6 @@ class _HeroCarouselState extends State<HeroCarousel> {
|
|||||||
onPressed: () => widget.onTap(item),
|
onPressed: () => widget.onTap(item),
|
||||||
isPrimary: true,
|
isPrimary: true,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
|
||||||
if (item.onMoreInfo != null)
|
|
||||||
_HeroButton(
|
|
||||||
icon: Icons.info_outline,
|
|
||||||
label: 'Plus d\'infos',
|
|
||||||
onPressed: item.onMoreInfo!,
|
|
||||||
isPrimary: false,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ class _MobileMoviesTabState extends ConsumerState<MobileMoviesTab> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onScroll() {
|
void _onScroll() {
|
||||||
if (_scrollController.position.pixels >=
|
// Only trigger load when near bottom and not already loading
|
||||||
|
if (!_isLoading && _hasMore &&
|
||||||
|
_scrollController.position.pixels >=
|
||||||
_scrollController.position.maxScrollExtent * 0.8) {
|
_scrollController.position.maxScrollExtent * 0.8) {
|
||||||
_loadMoreMovies();
|
_loadMoreMovies();
|
||||||
}
|
}
|
||||||
@@ -92,9 +94,12 @@ class _MobileMoviesTabState extends ConsumerState<MobileMoviesTab> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadMoreMovies() async {
|
Future<void> _loadMoreMovies() async {
|
||||||
if (_isLoading || !_hasMore) return;
|
// Double-check conditions to prevent race conditions
|
||||||
|
if (_isLoading || !_hasMore || !mounted) return;
|
||||||
|
|
||||||
setState(() => _isLoading = true);
|
// Set loading flag immediately before any async operation
|
||||||
|
_isLoading = true;
|
||||||
|
if (mounted) setState(() {});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final service = ref.read(xtreamServiceProvider(widget.playlist));
|
final service = ref.read(xtreamServiceProvider(widget.playlist));
|
||||||
@@ -112,7 +117,9 @@ class _MobileMoviesTabState extends ConsumerState<MobileMoviesTab> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) setState(() => _isLoading = false);
|
if (mounted) {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,8 +172,8 @@ class _MobileMoviesTabState extends ConsumerState<MobileMoviesTab> {
|
|||||||
: _movies.where((m) => settings.matchesMoviesFilter(m.categoryName)).toList();
|
: _movies.where((m) => settings.matchesMoviesFilter(m.categoryName)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hero Items
|
// Hero Items (use filtered movies)
|
||||||
final heroItems = _movies.take(3).map((m) => HeroItem(
|
final heroItems = displayMovies.take(3).map((m) => HeroItem(
|
||||||
id: m.streamId,
|
id: m.streamId,
|
||||||
title: m.name,
|
title: m.name,
|
||||||
imageUrl: _getProxiedImageUrl(m.streamIcon),
|
imageUrl: _getProxiedImageUrl(m.streamIcon),
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ class _MobileSeriesTabState extends ConsumerState<MobileSeriesTab> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onScroll() {
|
void _onScroll() {
|
||||||
if (_scrollController.position.pixels >=
|
// Only trigger load when near bottom and not already loading
|
||||||
|
if (!_isLoading && _hasMore &&
|
||||||
|
_scrollController.position.pixels >=
|
||||||
_scrollController.position.maxScrollExtent * 0.8) {
|
_scrollController.position.maxScrollExtent * 0.8) {
|
||||||
_loadMoreSeries();
|
_loadMoreSeries();
|
||||||
}
|
}
|
||||||
@@ -91,9 +93,12 @@ class _MobileSeriesTabState extends ConsumerState<MobileSeriesTab> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadMoreSeries() async {
|
Future<void> _loadMoreSeries() async {
|
||||||
if (_isLoading || !_hasMore) return;
|
// Double-check conditions to prevent race conditions
|
||||||
|
if (_isLoading || !_hasMore || !mounted) return;
|
||||||
|
|
||||||
setState(() => _isLoading = true);
|
// Set loading flag immediately before any async operation
|
||||||
|
_isLoading = true;
|
||||||
|
if (mounted) setState(() {});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final service = ref.read(xtreamServiceProvider(widget.playlist));
|
final service = ref.read(xtreamServiceProvider(widget.playlist));
|
||||||
@@ -111,7 +116,9 @@ class _MobileSeriesTabState extends ConsumerState<MobileSeriesTab> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) setState(() => _isLoading = false);
|
if (mounted) {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,8 +165,8 @@ class _MobileSeriesTabState extends ConsumerState<MobileSeriesTab> {
|
|||||||
: _series.where((s) => settings.matchesSeriesFilter(s.categoryName)).toList();
|
: _series.where((s) => settings.matchesSeriesFilter(s.categoryName)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hero Items
|
// Hero Items (use filtered series)
|
||||||
final heroItems = _series.take(3).map((s) => HeroItem(
|
final heroItems = displaySeries.take(3).map((s) => HeroItem(
|
||||||
id: s.seriesId.toString(),
|
id: s.seriesId.toString(),
|
||||||
title: s.name,
|
title: s.name,
|
||||||
imageUrl: _getProxiedImageUrl(s.cover),
|
imageUrl: _getProxiedImageUrl(s.cover),
|
||||||
|
|||||||
Reference in new issue
Block a user