This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								assets/images/menu.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/images/menu.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 16 KiB  | 
@@ -1,12 +1,25 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:flutter/services.dart';
 | 
					import 'package:flutter/services.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:f0ckapp/providers/MediaProvider.dart';
 | 
				
			||||||
 | 
					import 'package:f0ckapp/providers/ThemeProvider.dart';
 | 
				
			||||||
import 'package:f0ckapp/screens/MediaGrid.dart';
 | 
					import 'package:f0ckapp/screens/MediaGrid.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:provider/provider.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void main() async {
 | 
					void main() async {
 | 
				
			||||||
  WidgetsFlutterBinding.ensureInitialized();
 | 
					  WidgetsFlutterBinding.ensureInitialized();
 | 
				
			||||||
  await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
 | 
					  await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
 | 
				
			||||||
  runApp(const F0ckApp());
 | 
					
 | 
				
			||||||
 | 
					  runApp(
 | 
				
			||||||
 | 
					    MultiProvider(
 | 
				
			||||||
 | 
					      providers: [
 | 
				
			||||||
 | 
					        ChangeNotifierProvider(create: (context) => ThemeProvider()),
 | 
				
			||||||
 | 
					        ChangeNotifierProvider(create: (context) => MediaProvider())
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      child: F0ckApp()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class F0ckApp extends StatelessWidget {
 | 
					class F0ckApp extends StatelessWidget {
 | 
				
			||||||
@@ -14,11 +27,11 @@ class F0ckApp extends StatelessWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    final themeProvider = Provider.of<ThemeProvider>(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return MaterialApp(
 | 
					    return MaterialApp(
 | 
				
			||||||
      debugShowCheckedModeBanner: false,
 | 
					      debugShowCheckedModeBanner: false,
 | 
				
			||||||
      theme: ThemeData(
 | 
					      theme: themeProvider.themeData,
 | 
				
			||||||
        scaffoldBackgroundColor: const Color.fromARGB(255, 23, 23, 23),
 | 
					 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
      home: Scaffold(
 | 
					      home: Scaffold(
 | 
				
			||||||
        body: MediaGrid(),
 | 
					        body: MediaGrid(),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										88
									
								
								lib/providers/MediaProvider.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								lib/providers/MediaProvider.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					import 'package:f0ckapp/services/Api.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:f0ckapp/models/MediaItem.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MediaProvider extends ChangeNotifier {
 | 
				
			||||||
 | 
					  int _typeid = 0;
 | 
				
			||||||
 | 
					  int _mode = 0;
 | 
				
			||||||
 | 
					  bool _random = false;
 | 
				
			||||||
 | 
					  String? _tag;
 | 
				
			||||||
 | 
					  int _crossAxisCount = 0;
 | 
				
			||||||
 | 
					  final List<MediaItem> _mediaItems = [];
 | 
				
			||||||
 | 
					  bool _isLoading = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  List<String> types = ["alles", "image", "video", "audio"];
 | 
				
			||||||
 | 
					  List<String> modes = ["sfw", "nsfw", "untagged", "all"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String get type => types[_typeid];
 | 
				
			||||||
 | 
					  int get typeid => _typeid;
 | 
				
			||||||
 | 
					  int get mode => _mode;
 | 
				
			||||||
 | 
					  bool get random => _random;
 | 
				
			||||||
 | 
					  String? get tag => _tag;
 | 
				
			||||||
 | 
					  int get crossAxisCount  => _crossAxisCount;
 | 
				
			||||||
 | 
					  List<MediaItem> get mediaItems => _mediaItems;
 | 
				
			||||||
 | 
					  bool get isLoading => _isLoading;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void setType(String type) {
 | 
				
			||||||
 | 
					    _typeid = types.indexOf(type);
 | 
				
			||||||
 | 
					    loadMedia(reload: true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void setMode(int mode) {
 | 
				
			||||||
 | 
					    _mode = mode;
 | 
				
			||||||
 | 
					    loadMedia(reload: true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void toggleRandom() {
 | 
				
			||||||
 | 
					    _random = !_random;
 | 
				
			||||||
 | 
					    loadMedia(reload: true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void setTag(String? tag) {
 | 
				
			||||||
 | 
					    _tag = tag;
 | 
				
			||||||
 | 
					    loadMedia(reload: true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void setCrossAxisCount(int crossAxisCount) {
 | 
				
			||||||
 | 
					    _crossAxisCount = crossAxisCount;
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void setMediaItems(List<MediaItem> mediaItems) {
 | 
				
			||||||
 | 
					    _mediaItems.clear();
 | 
				
			||||||
 | 
					    addMediaItems(mediaItems);
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void addMediaItems(List<MediaItem> newItems) {
 | 
				
			||||||
 | 
					    _mediaItems.addAll(newItems);
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> loadMedia({bool reload = false}) async {
 | 
				
			||||||
 | 
					    if (_isLoading) return;
 | 
				
			||||||
 | 
					    _isLoading = true;
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      final newMedia = await fetchMedia(
 | 
				
			||||||
 | 
					        older: reload
 | 
				
			||||||
 | 
					            ? null
 | 
				
			||||||
 | 
					            : _mediaItems.isNotEmpty
 | 
				
			||||||
 | 
					            ? _mediaItems.last.id
 | 
				
			||||||
 | 
					            : null,
 | 
				
			||||||
 | 
					        type: type,
 | 
				
			||||||
 | 
					        mode: mode,
 | 
				
			||||||
 | 
					        random: random,
 | 
				
			||||||
 | 
					        tag: tag,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      reload ? setMediaItems(newMedia) : addMediaItems(newMedia);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      debugPrint('Fehler beim Laden der Medien: $e');
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      _isLoading = false;
 | 
				
			||||||
 | 
					      notifyListeners();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										132
									
								
								lib/providers/ThemeProvider.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								lib/providers/ThemeProvider.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final ThemeData f0ckTheme = ThemeData(
 | 
				
			||||||
 | 
					  brightness: Brightness.dark,
 | 
				
			||||||
 | 
					  primaryColor: Color(0xFF9FFF00),
 | 
				
			||||||
 | 
					  scaffoldBackgroundColor: Color(0xFF000000),
 | 
				
			||||||
 | 
					  colorScheme: ColorScheme.dark(
 | 
				
			||||||
 | 
					    primary: Color(0xFF9FFF00),
 | 
				
			||||||
 | 
					    secondary: Color(0xFF262626),
 | 
				
			||||||
 | 
					    surface: Color(0xFF232323),
 | 
				
			||||||
 | 
					    onPrimary: Color(0xFF000000),
 | 
				
			||||||
 | 
					    onSecondary: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					    onSurface: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  appBarTheme: AppBarTheme(
 | 
				
			||||||
 | 
					    backgroundColor: Color(0xFF2B2B2B),
 | 
				
			||||||
 | 
					    foregroundColor: Color(0xFF9FFF00),
 | 
				
			||||||
 | 
					    elevation: 2,
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  textTheme: TextTheme(
 | 
				
			||||||
 | 
					    bodyLarge: TextStyle(fontFamily: 'VCR', color: Color(0xFFFFFFFF)),
 | 
				
			||||||
 | 
					    bodyMedium: TextStyle(fontFamily: 'monospace', color: Color(0xFFFFFFFF)),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  buttonTheme: ButtonThemeData(
 | 
				
			||||||
 | 
					    buttonColor: Color(0xFF9FFF00),
 | 
				
			||||||
 | 
					    textTheme: ButtonTextTheme.primary,
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  scrollbarTheme: ScrollbarThemeData(
 | 
				
			||||||
 | 
					    thumbColor: WidgetStateProperty.all(Color(0xFF2B2B2B)),
 | 
				
			||||||
 | 
					    trackColor: WidgetStateProperty.all(Color(0xFF424242)),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final ThemeData paperTheme = ThemeData(
 | 
				
			||||||
 | 
					  brightness: Brightness.light,
 | 
				
			||||||
 | 
					  primaryColor: Color(0xFF000000),
 | 
				
			||||||
 | 
					  scaffoldBackgroundColor: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					  colorScheme: ColorScheme.light(
 | 
				
			||||||
 | 
					    primary: Color(0xFF000000),
 | 
				
			||||||
 | 
					    secondary: Color(0xFF262626),
 | 
				
			||||||
 | 
					    surface: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					    onPrimary: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					    onSecondary: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					    onSurface: Color(0xFF000000),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  appBarTheme: AppBarTheme(
 | 
				
			||||||
 | 
					    backgroundColor: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					    foregroundColor: Color(0xFF000000),
 | 
				
			||||||
 | 
					    elevation: 0,
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  textTheme: TextTheme(
 | 
				
			||||||
 | 
					    bodyLarge: TextStyle(fontFamily: 'VCR', color: Color(0xFF000000)),
 | 
				
			||||||
 | 
					    bodyMedium: TextStyle(fontFamily: 'monospace', color: Color(0xFF000000)),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  buttonTheme: ButtonThemeData(
 | 
				
			||||||
 | 
					    buttonColor: Color(0xFF000000),
 | 
				
			||||||
 | 
					    textTheme: ButtonTextTheme.primary,
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final ThemeData f0ck95Theme = ThemeData(
 | 
				
			||||||
 | 
					  brightness: Brightness.light,
 | 
				
			||||||
 | 
					  primaryColor: Color(0xFFC0C0C0),
 | 
				
			||||||
 | 
					  scaffoldBackgroundColor: Color(0xFF008080),
 | 
				
			||||||
 | 
					  colorScheme: ColorScheme.light(
 | 
				
			||||||
 | 
					    primary: Color(0xFFC0C0C0),
 | 
				
			||||||
 | 
					    secondary: Color(0xFF808080),
 | 
				
			||||||
 | 
					    surface: Color(0xFFC0C0C0),
 | 
				
			||||||
 | 
					    onPrimary: Color(0xFF000000),
 | 
				
			||||||
 | 
					    onSecondary: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  appBarTheme: AppBarTheme(
 | 
				
			||||||
 | 
					    backgroundColor: Color(0xFFC0C0C0),
 | 
				
			||||||
 | 
					    foregroundColor: Color(0xFF000000),
 | 
				
			||||||
 | 
					    elevation: 2,
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  textTheme: TextTheme(
 | 
				
			||||||
 | 
					    bodyLarge: TextStyle(fontFamily: 'VCR', color: Color(0xFF000000)),
 | 
				
			||||||
 | 
					    bodyMedium: TextStyle(fontFamily: 'monospace', color: Color(0xFF000000)),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  buttonTheme: ButtonThemeData(
 | 
				
			||||||
 | 
					    buttonColor: Color(0xFF000000),
 | 
				
			||||||
 | 
					    textTheme: ButtonTextTheme.primary,
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  scrollbarTheme: ScrollbarThemeData(
 | 
				
			||||||
 | 
					    thumbColor: WidgetStateProperty.all(Color(0xFF2B2B2B)),
 | 
				
			||||||
 | 
					    trackColor: WidgetStateProperty.all(Color(0xFF424242)),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final ThemeData f0ck95dTheme = ThemeData(
 | 
				
			||||||
 | 
					  brightness: Brightness.dark,
 | 
				
			||||||
 | 
					  primaryColor: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					  scaffoldBackgroundColor: Color(0xFF0E0F0F),
 | 
				
			||||||
 | 
					  colorScheme: ColorScheme.dark(
 | 
				
			||||||
 | 
					    primary: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					    secondary: Color(0xFFC0C0C0),
 | 
				
			||||||
 | 
					    surface: Color(0xFF333131),
 | 
				
			||||||
 | 
					    onPrimary: Color(0xFF000000),
 | 
				
			||||||
 | 
					    onSecondary: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  appBarTheme: AppBarTheme(
 | 
				
			||||||
 | 
					    backgroundColor: Color(0xFF0B0A0A),
 | 
				
			||||||
 | 
					    foregroundColor: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					    elevation: 2,
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  textTheme: TextTheme(
 | 
				
			||||||
 | 
					    bodyLarge: TextStyle(fontFamily: 'VCR', color: Color(0xFFFFFFFF)),
 | 
				
			||||||
 | 
					    bodyMedium: TextStyle(fontFamily: 'monospace', color: Color(0xFFFFFFFF)),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  buttonTheme: ButtonThemeData(
 | 
				
			||||||
 | 
					    buttonColor: Color(0xFFFFFFFF),
 | 
				
			||||||
 | 
					    textTheme: ButtonTextTheme.primary,
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  scrollbarTheme: ScrollbarThemeData(
 | 
				
			||||||
 | 
					    thumbColor: WidgetStateProperty.all(Color(0xFF2B2B2B)),
 | 
				
			||||||
 | 
					    trackColor: WidgetStateProperty.all(Color(0xFF424242)),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ThemeProvider extends ChangeNotifier {
 | 
				
			||||||
 | 
					  ThemeData _themeData = f0ck95dTheme;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ThemeData get themeData => _themeData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*void toggleTheme() {
 | 
				
			||||||
 | 
					    _themeData = _themeData == lightTheme ? darkTheme : lightTheme;
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,29 +1,18 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:cached_network_image/cached_network_image.dart';
 | 
					import 'package:cached_network_image/cached_network_image.dart';
 | 
				
			||||||
 | 
					import 'package:provider/provider.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/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:f0ckapp/providers/MediaProvider.dart';
 | 
				
			||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
 | 
					import 'package:flutter_cache_manager/flutter_cache_manager.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DetailView extends StatefulWidget {
 | 
					class DetailView extends StatefulWidget {
 | 
				
			||||||
  final int initialItemId;
 | 
					  final int initialItemId;
 | 
				
			||||||
  final List<MediaItem> mediaItems;
 | 
					 | 
				
			||||||
  final String type;
 | 
					 | 
				
			||||||
  final int mode;
 | 
					 | 
				
			||||||
  final bool random;
 | 
					 | 
				
			||||||
  final String? tagname;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const DetailView({
 | 
					  const DetailView({super.key, required this.initialItemId});
 | 
				
			||||||
    super.key,
 | 
					 | 
				
			||||||
    required this.initialItemId,
 | 
					 | 
				
			||||||
    required this.mediaItems,
 | 
					 | 
				
			||||||
    required this.type,
 | 
					 | 
				
			||||||
    required this.mode,
 | 
					 | 
				
			||||||
    required this.random,
 | 
					 | 
				
			||||||
    required this.tagname,
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  State createState() => _DetailViewState();
 | 
					  State createState() => _DetailViewState();
 | 
				
			||||||
@@ -31,57 +20,34 @@ class DetailView extends StatefulWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class _DetailViewState extends State<DetailView> {
 | 
					class _DetailViewState extends State<DetailView> {
 | 
				
			||||||
  late PageController _pageController;
 | 
					  late PageController _pageController;
 | 
				
			||||||
  late List<MediaItem> mediaItems;
 | 
					 | 
				
			||||||
  String? _tagname;
 | 
					 | 
				
			||||||
  int currentItemId = 0;
 | 
					 | 
				
			||||||
  bool isLoading = false;
 | 
					  bool isLoading = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
    mediaItems = widget.mediaItems;
 | 
					    final provider = Provider.of<MediaProvider>(context, listen: false);
 | 
				
			||||||
    _tagname = widget.tagname;
 | 
					
 | 
				
			||||||
    final initialIndex = mediaItems.indexWhere(
 | 
					    final initialIndex = provider.mediaItems.indexWhere(
 | 
				
			||||||
      (item) => item.id == widget.initialItemId,
 | 
					      (item) => item.id == widget.initialItemId,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    _pageController = PageController(initialPage: initialIndex);
 | 
					    _pageController = PageController(initialPage: initialIndex);
 | 
				
			||||||
    currentItemId = mediaItems[initialIndex].id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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);
 | 
					    _preloadAdjacentMedia(initialIndex);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _preloadAdjacentMedia(int index) async {
 | 
					  void _preloadAdjacentMedia(int index) async {
 | 
				
			||||||
    if (index + 1 < mediaItems.length) {
 | 
					    final provider = Provider.of<MediaProvider>(context, listen: false);
 | 
				
			||||||
      final nextUrl = mediaItems[index + 1].mediaUrl;
 | 
					    if (index + 1 < provider.mediaItems.length) {
 | 
				
			||||||
 | 
					      final nextUrl = provider.mediaItems[index + 1].mediaUrl;
 | 
				
			||||||
      if (await DefaultCacheManager().getFileFromCache(nextUrl) == null) {
 | 
					      if (await DefaultCacheManager().getFileFromCache(nextUrl) == null) {
 | 
				
			||||||
        await DefaultCacheManager().downloadFile(nextUrl);
 | 
					        await DefaultCacheManager().downloadFile(nextUrl);
 | 
				
			||||||
        print('preload ${mediaItems[index + 1].id}');
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (index - 1 >= 0) {
 | 
					    if (index - 1 >= 0) {
 | 
				
			||||||
      final prevUrl = mediaItems[index - 1].mediaUrl;
 | 
					      final prevUrl = provider.mediaItems[index - 1].mediaUrl;
 | 
				
			||||||
      if (await DefaultCacheManager().getFileFromCache(prevUrl) == null) {
 | 
					      if (await DefaultCacheManager().getFileFromCache(prevUrl) == null) {
 | 
				
			||||||
        await DefaultCacheManager().downloadFile(prevUrl);
 | 
					        await DefaultCacheManager().downloadFile(prevUrl);
 | 
				
			||||||
        print('preload ${mediaItems[index - 1].id}');
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -90,116 +56,84 @@ class _DetailViewState extends State<DetailView> {
 | 
				
			|||||||
    if (isLoading) return;
 | 
					    if (isLoading) return;
 | 
				
			||||||
    setState(() => isLoading = true);
 | 
					    setState(() => isLoading = true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final provider = Provider.of<MediaProvider>(context, listen: false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final newMedia = await fetchMedia(
 | 
					      final newMedia = await fetchMedia(
 | 
				
			||||||
        older: mediaItems.last.id.toString(),
 | 
					        older: provider.mediaItems.last.id,
 | 
				
			||||||
        type: widget.type,
 | 
					        type: provider.type,
 | 
				
			||||||
        mode: widget.mode,
 | 
					        mode: provider.mode,
 | 
				
			||||||
        random: widget.random,
 | 
					        random: provider.random,
 | 
				
			||||||
        tag: _tagname,
 | 
					        tag: provider.tag,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      if (mounted && newMedia.isNotEmpty) {
 | 
					      if (mounted && newMedia.isNotEmpty) {
 | 
				
			||||||
        setState(() {
 | 
					        setState(() => provider.mediaItems.addAll(newMedia));
 | 
				
			||||||
          mediaItems.addAll(newMedia);
 | 
					 | 
				
			||||||
          isLoading = false;
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _showError("Ein unerwarteter Fehler ist aufgetreten: $e");
 | 
					      _showError("Fehler beim Laden der Medien: $e");
 | 
				
			||||||
    }
 | 
					    } finally {
 | 
				
			||||||
  }
 | 
					      setState(() => isLoading = false);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  Future<void> _refreshMediaItem() async {
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      final updatedItem = await fetchMediaDetail(currentItemId);
 | 
					 | 
				
			||||||
      if (mounted) {
 | 
					 | 
				
			||||||
        final index = mediaItems.indexWhere((item) => item.id == currentItemId);
 | 
					 | 
				
			||||||
        if (index != -1) {
 | 
					 | 
				
			||||||
          setState(() => mediaItems[index] = updatedItem);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      _showError("Fehler beim Aktualisieren des Items: $e");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _showError(String message) {
 | 
					  void _showError(String message) {
 | 
				
			||||||
    if (!mounted) return;
 | 
					    if (!mounted) return;
 | 
				
			||||||
 | 
					    ScaffoldMessenger.of(
 | 
				
			||||||
    final messenger = ScaffoldMessenger.of(context);
 | 
					      context,
 | 
				
			||||||
    messenger.hideCurrentSnackBar();
 | 
					    ).showSnackBar(SnackBar(content: Text(message)));
 | 
				
			||||||
    messenger.showSnackBar(SnackBar(content: Text(message)));
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    final provider = Provider.of<MediaProvider>(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Scaffold(
 | 
					    return Scaffold(
 | 
				
			||||||
      backgroundColor: const Color(0xFF171717),
 | 
					 | 
				
			||||||
      appBar: AppBar(
 | 
					      appBar: AppBar(
 | 
				
			||||||
        backgroundColor: const Color(0xFF2B2B2B),
 | 
					 | 
				
			||||||
        foregroundColor: Colors.white,
 | 
					 | 
				
			||||||
        title: Text('f0ck #$currentItemId (${widget.type})'),
 | 
					 | 
				
			||||||
        centerTitle: true,
 | 
					        centerTitle: true,
 | 
				
			||||||
 | 
					        title: Text('f0ck #${widget.initialItemId} (${provider.type})'),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
      body: Stack(
 | 
					      body: Stack(
 | 
				
			||||||
        children: [
 | 
					        children: [
 | 
				
			||||||
          PageTransformer(
 | 
					          PageTransformer(
 | 
				
			||||||
            controller: _pageController,
 | 
					            controller: _pageController,
 | 
				
			||||||
            pages: mediaItems.map((item) {
 | 
					            pages: provider.mediaItems.map((item) {
 | 
				
			||||||
              final isActive = item.id == currentItemId;
 | 
					              return SafeArea(
 | 
				
			||||||
              return Scaffold(
 | 
					 | 
				
			||||||
                body: SafeArea(
 | 
					 | 
				
			||||||
                child: SmartRefreshIndicator(
 | 
					                child: SmartRefreshIndicator(
 | 
				
			||||||
                    onRefresh: _refreshMediaItem,
 | 
					                  onRefresh: _loadMoreMedia,
 | 
				
			||||||
                    child: _buildMediaItem(item, isActive),
 | 
					                  child: _buildMediaItem(item),
 | 
				
			||||||
                  ),
 | 
					 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
              );
 | 
					              );
 | 
				
			||||||
            }).toList(),
 | 
					            }).toList(),
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
          if (_tagname != null)
 | 
					        ],
 | 
				
			||||||
            Positioned(
 | 
					 | 
				
			||||||
              bottom: 60,
 | 
					 | 
				
			||||||
              left: MediaQuery.of(context).size.width * 0.2,
 | 
					 | 
				
			||||||
              right: MediaQuery.of(context).size.width * 0.2,
 | 
					 | 
				
			||||||
              child: Container(
 | 
					 | 
				
			||||||
                padding: const EdgeInsets.symmetric(
 | 
					 | 
				
			||||||
                  vertical: 10,
 | 
					 | 
				
			||||||
                  horizontal: 20,
 | 
					 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
                decoration: BoxDecoration(
 | 
					      persistentFooterButtons: provider.tag != null
 | 
				
			||||||
                  color: Colors.black.withValues(alpha: 0.8),
 | 
					          ? [
 | 
				
			||||||
                  borderRadius: BorderRadius.circular(12),
 | 
					              Row(
 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                child: Row(
 | 
					 | 
				
			||||||
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
					                mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
                children: [
 | 
					                children: [
 | 
				
			||||||
                    Text(
 | 
					                  Text('tag: '),
 | 
				
			||||||
                      'Tag: $_tagname',
 | 
					                  InputChip(
 | 
				
			||||||
                      style: const TextStyle(color: Colors.white),
 | 
					                    label: Text(provider.tag!),
 | 
				
			||||||
                    ),
 | 
					                    backgroundColor: const Color(0xFF090909),
 | 
				
			||||||
                    IconButton(
 | 
					                    labelStyle: const TextStyle(color: Colors.white),
 | 
				
			||||||
                      icon: const Icon(Icons.close, color: Colors.white),
 | 
					                    onDeleted: () {
 | 
				
			||||||
                      onPressed: () {
 | 
					                      provider.setTag(null);
 | 
				
			||||||
                        setState(() {
 | 
					                      Navigator.pop(context);
 | 
				
			||||||
                          _tagname = '___empty___';
 | 
					 | 
				
			||||||
                          Navigator.pop(context, _tagname);
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                  ),
 | 
					                  ),
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
              ),
 | 
					            ]
 | 
				
			||||||
            ),
 | 
					          : null,
 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget _buildMediaItem(MediaItem item, bool isActive) {
 | 
					  Widget _buildMediaItem(MediaItem item) {
 | 
				
			||||||
 | 
					    final provider = Provider.of<MediaProvider>(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return SingleChildScrollView(
 | 
					    return SingleChildScrollView(
 | 
				
			||||||
      child: Column(
 | 
					      child: Column(
 | 
				
			||||||
        mainAxisAlignment: MainAxisAlignment.center,
 | 
					 | 
				
			||||||
        children: [
 | 
					        children: [
 | 
				
			||||||
          if (item.mime.startsWith('image'))
 | 
					          if (item.mime.startsWith('image'))
 | 
				
			||||||
            CachedNetworkImage(
 | 
					            CachedNetworkImage(
 | 
				
			||||||
@@ -209,7 +143,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, isActive: isActive),
 | 
					            VideoWidget(details: item, isActive: true),
 | 
				
			||||||
          const SizedBox(height: 20),
 | 
					          const SizedBox(height: 20),
 | 
				
			||||||
          Text(
 | 
					          Text(
 | 
				
			||||||
            item.mime,
 | 
					            item.mime,
 | 
				
			||||||
@@ -221,26 +155,20 @@ class _DetailViewState extends State<DetailView> {
 | 
				
			|||||||
            spacing: 5.0,
 | 
					            spacing: 5.0,
 | 
				
			||||||
            children: item.tags.map((tag) {
 | 
					            children: item.tags.map((tag) {
 | 
				
			||||||
              return ActionChip(
 | 
					              return ActionChip(
 | 
				
			||||||
                label: Text(
 | 
					                onPressed: () {
 | 
				
			||||||
                  tag.tag,
 | 
					                  if (tag.tag == 'sfw' || tag.tag == 'nsfw') return;
 | 
				
			||||||
                  style: const TextStyle(
 | 
					                  setState(() {
 | 
				
			||||||
                    color: Colors.white,
 | 
					                    provider.setTag(tag.tag);
 | 
				
			||||||
                    fontWeight: FontWeight.bold,
 | 
					                    Navigator.pop(context);
 | 
				
			||||||
                  ),
 | 
					                  });
 | 
				
			||||||
                ),
 | 
					                },
 | 
				
			||||||
 | 
					                label: Text(tag.tag),
 | 
				
			||||||
                backgroundColor: switch (tag.id) {
 | 
					                backgroundColor: switch (tag.id) {
 | 
				
			||||||
                  1 => Colors.green,
 | 
					                  1 => Colors.green,
 | 
				
			||||||
                  2 => Colors.red,
 | 
					                  2 => Colors.red,
 | 
				
			||||||
                  _ => const Color(0xFF090909),
 | 
					                  _ => const Color(0xFF090909),
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
 | 
					                labelStyle: const TextStyle(color: Colors.white),
 | 
				
			||||||
                shape: RoundedRectangleBorder(
 | 
					 | 
				
			||||||
                  borderRadius: BorderRadius.circular(12),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                onPressed: () async {
 | 
					 | 
				
			||||||
                  if (tag.tag == 'sfw' || tag.tag == 'nsfw') return;
 | 
					 | 
				
			||||||
                  setState(() => Navigator.pop(context, tag.tag));
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              );
 | 
					              );
 | 
				
			||||||
            }).toList(),
 | 
					            }).toList(),
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
import 'package:cached_network_image/cached_network_image.dart';
 | 
					 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:f0ckapp/services/Api.dart';
 | 
					import 'package:cached_network_image/cached_network_image.dart';
 | 
				
			||||||
import 'package:f0ckapp/models/MediaItem.dart';
 | 
					import 'package:provider/provider.dart';
 | 
				
			||||||
import 'package:f0ckapp/screens/DetailView.dart';
 | 
					import 'package:f0ckapp/screens/DetailView.dart';
 | 
				
			||||||
import 'dart:async';
 | 
					import 'package:f0ckapp/services/Api.dart';
 | 
				
			||||||
 | 
					import 'package:f0ckapp/providers/MediaProvider.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MediaGrid extends StatefulWidget {
 | 
					class MediaGrid extends StatefulWidget {
 | 
				
			||||||
  const MediaGrid({super.key});
 | 
					  const MediaGrid({super.key});
 | 
				
			||||||
@@ -14,256 +14,175 @@ 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.25+25';
 | 
					 | 
				
			||||||
  List<MediaItem> mediaItems = [];
 | 
					 | 
				
			||||||
  bool isLoading = false;
 | 
					 | 
				
			||||||
  Timer? _debounceTimer;
 | 
					 | 
				
			||||||
  Completer<void>? _navigationCompleter;
 | 
					 | 
				
			||||||
  int _crossAxisCount = 0;
 | 
					 | 
				
			||||||
  String _selectedType = 'alles';
 | 
					 | 
				
			||||||
  int _selectedMode = 0;
 | 
					 | 
				
			||||||
  bool _random = false;
 | 
					 | 
				
			||||||
  final List<String> _modes = ["sfw", "nsfw", "untagged", "all"];
 | 
					 | 
				
			||||||
  String? _selectedTag;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
    _loadMedia();
 | 
					
 | 
				
			||||||
 | 
					    final provider = Provider.of<MediaProvider>(context, listen: false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Future.microtask(() {
 | 
				
			||||||
 | 
					      provider.loadMedia();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _scrollController.addListener(() {
 | 
					    _scrollController.addListener(() {
 | 
				
			||||||
      if (_scrollController.position.pixels >=
 | 
					      if (_scrollController.position.pixels >=
 | 
				
			||||||
          _scrollController.position.maxScrollExtent - 100) {
 | 
					          _scrollController.position.maxScrollExtent - 100) {
 | 
				
			||||||
        _debounceLoadMedia();
 | 
					        provider.loadMedia();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _debounceLoadMedia() {
 | 
					 | 
				
			||||||
    _debounceTimer?.cancel();
 | 
					 | 
				
			||||||
    _debounceTimer = Timer(const Duration(milliseconds: 500), _loadMedia);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  int _calculateCrossAxisCount(BuildContext context) {
 | 
					 | 
				
			||||||
    if (_crossAxisCount != 0) {
 | 
					 | 
				
			||||||
      return _crossAxisCount;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    double screenWidth = MediaQuery.of(context).size.width;
 | 
					 | 
				
			||||||
    int columnCount = (screenWidth / 110).clamp(3, 5).toInt();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return columnCount;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Future<void> _loadMedia() async {
 | 
					 | 
				
			||||||
    if (isLoading) return;
 | 
					 | 
				
			||||||
    setState(() => isLoading = true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      final newMedia = await fetchMedia(
 | 
					 | 
				
			||||||
        older: mediaItems.isNotEmpty ? mediaItems.last.id.toString() : null,
 | 
					 | 
				
			||||||
        type: _selectedType,
 | 
					 | 
				
			||||||
        mode: _selectedMode,
 | 
					 | 
				
			||||||
        random: _random,
 | 
					 | 
				
			||||||
        tag: _selectedTag,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      if (mounted) {
 | 
					 | 
				
			||||||
        setState(() => mediaItems.addAll(newMedia));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      if (mounted) {
 | 
					 | 
				
			||||||
        ScaffoldMessenger.of(context).showSnackBar(
 | 
					 | 
				
			||||||
          SnackBar(content: Text('Fehler beim Laden der Medien: $e')),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      if (mounted) setState(() => isLoading = false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Future<void> _refreshMedia() async {
 | 
					 | 
				
			||||||
    setState(() => isLoading = true);
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      final freshMedia = await fetchMedia(
 | 
					 | 
				
			||||||
        older: null,
 | 
					 | 
				
			||||||
        type: _selectedType,
 | 
					 | 
				
			||||||
        mode: _selectedMode,
 | 
					 | 
				
			||||||
        random: _random,
 | 
					 | 
				
			||||||
        tag: _selectedTag,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      if (mounted) {
 | 
					 | 
				
			||||||
        setState(() {
 | 
					 | 
				
			||||||
          mediaItems.clear();
 | 
					 | 
				
			||||||
          mediaItems.addAll(freshMedia);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      if (mounted) {
 | 
					 | 
				
			||||||
        ScaffoldMessenger.of(context).showSnackBar(
 | 
					 | 
				
			||||||
          SnackBar(content: Text('Fehler beim Aktualisieren: $e')),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      if (mounted) setState(() => isLoading = false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Future<void> _navigateToDetail(MediaItem item) async {
 | 
					 | 
				
			||||||
    if (_navigationCompleter?.isCompleted == false) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    _navigationCompleter = Completer();
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      if (mounted) {
 | 
					 | 
				
			||||||
        final String? newTag = await Navigator.push(
 | 
					 | 
				
			||||||
          context,
 | 
					 | 
				
			||||||
          MaterialPageRoute(
 | 
					 | 
				
			||||||
            builder: (context) => DetailView(
 | 
					 | 
				
			||||||
              initialItemId: item.id,
 | 
					 | 
				
			||||||
              mediaItems: mediaItems,
 | 
					 | 
				
			||||||
              type: _selectedType,
 | 
					 | 
				
			||||||
              mode: _selectedMode,
 | 
					 | 
				
			||||||
              random: _random,
 | 
					 | 
				
			||||||
              tagname: _selectedTag,
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (newTag != null) {
 | 
					 | 
				
			||||||
          setState(() {
 | 
					 | 
				
			||||||
            if (newTag == '___empty___') {
 | 
					 | 
				
			||||||
              _selectedTag = null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
              _selectedTag = newTag;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            _refreshMedia();
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      if (mounted) {
 | 
					 | 
				
			||||||
        ScaffoldMessenger.of(context).showSnackBar(
 | 
					 | 
				
			||||||
          SnackBar(content: Text('Fehler beim Laden der Details: $e')),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      _navigationCompleter?.complete();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    final provider = Provider.of<MediaProvider>(context);
 | 
				
			||||||
 | 
					    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Scaffold(
 | 
					    return Scaffold(
 | 
				
			||||||
 | 
					      key: scaffoldKey,
 | 
				
			||||||
      appBar: AppBar(
 | 
					      appBar: AppBar(
 | 
				
			||||||
        centerTitle: true,
 | 
					        //centerTitle: true,
 | 
				
			||||||
        backgroundColor: const Color.fromARGB(255, 43, 43, 43),
 | 
					        title: Text('f0ck v1.0.26+26'),
 | 
				
			||||||
        foregroundColor: const Color.fromARGB(255, 255, 255, 255),
 | 
					        actions: [
 | 
				
			||||||
        title: Row(
 | 
					          DropdownButton<String>(
 | 
				
			||||||
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
					            // mode
 | 
				
			||||||
 | 
					            value: provider.modes[provider.mode],
 | 
				
			||||||
 | 
					            isDense: true,
 | 
				
			||||||
 | 
					            icon: SizedBox.shrink(),
 | 
				
			||||||
 | 
					            items: provider.modes.map((String value) {
 | 
				
			||||||
 | 
					              return DropdownMenuItem<String>(value: value, child: Text(value));
 | 
				
			||||||
 | 
					            }).toList(),
 | 
				
			||||||
 | 
					            onChanged: (String? newValue) {
 | 
				
			||||||
 | 
					              if (newValue != null) {
 | 
				
			||||||
 | 
					                provider.setMode(provider.modes.indexOf(newValue));
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          IconButton(
 | 
				
			||||||
 | 
					            icon: const Icon(Icons.menu),
 | 
				
			||||||
 | 
					            onPressed: () {
 | 
				
			||||||
 | 
					              scaffoldKey.currentState?.openEndDrawer();
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      endDrawer: Drawer(
 | 
				
			||||||
 | 
					        child: ListView(
 | 
				
			||||||
 | 
					          padding: EdgeInsets.zero,
 | 
				
			||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            Text('f0ck v$_version'),
 | 
					            DrawerHeader(
 | 
				
			||||||
            Checkbox(
 | 
					              padding: EdgeInsets.all(0),
 | 
				
			||||||
              value: _random,
 | 
					              child: Image.asset('assets/images/menu.webp', fit: BoxFit.cover),
 | 
				
			||||||
              onChanged: (bool? value) {
 | 
					            ),
 | 
				
			||||||
                setState(() {
 | 
					            ListTile(
 | 
				
			||||||
                  _random = !_random;
 | 
					              title: Text(
 | 
				
			||||||
                  _refreshMedia();
 | 
					                'All',
 | 
				
			||||||
                });
 | 
					                style: TextStyle(
 | 
				
			||||||
 | 
					                  fontWeight: provider.type == 'alles'
 | 
				
			||||||
 | 
					                      ? FontWeight.bold
 | 
				
			||||||
 | 
					                      : FontWeight.normal,
 | 
				
			||||||
 | 
					                  color: provider.type == 'alles' ? Colors.blue : Colors.white,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              onTap: () {
 | 
				
			||||||
 | 
					                provider.setType('all');
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              title: Text(
 | 
				
			||||||
 | 
					                'Images',
 | 
				
			||||||
 | 
					                style: TextStyle(
 | 
				
			||||||
 | 
					                  fontWeight: provider.type == 'image'
 | 
				
			||||||
 | 
					                      ? FontWeight.bold
 | 
				
			||||||
 | 
					                      : FontWeight.normal,
 | 
				
			||||||
 | 
					                  color: provider.type == 'image' ? Colors.blue : Colors.white,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              onTap: () {
 | 
				
			||||||
 | 
					                provider.setType('image');
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              title: Text(
 | 
				
			||||||
 | 
					                'Videos',
 | 
				
			||||||
 | 
					                style: TextStyle(
 | 
				
			||||||
 | 
					                  fontWeight: provider.type == 'video'
 | 
				
			||||||
 | 
					                      ? FontWeight.bold
 | 
				
			||||||
 | 
					                      : FontWeight.normal,
 | 
				
			||||||
 | 
					                  color: provider.type == 'video' ? Colors.blue : Colors.white,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              onTap: () {
 | 
				
			||||||
 | 
					                provider.setType('video');
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            ListTile(
 | 
				
			||||||
 | 
					              title: Text(
 | 
				
			||||||
 | 
					                'Audio',
 | 
				
			||||||
 | 
					                style: TextStyle(
 | 
				
			||||||
 | 
					                  fontWeight: provider.type == 'audio'
 | 
				
			||||||
 | 
					                      ? FontWeight.bold
 | 
				
			||||||
 | 
					                      : FontWeight.normal,
 | 
				
			||||||
 | 
					                  color: provider.type == 'audio' ? Colors.blue : Colors.white,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              onTap: () {
 | 
				
			||||||
 | 
					                provider.setType('audio');
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
      bottomNavigationBar: BottomAppBar(
 | 
					      persistentFooterButtons: provider.tag != null
 | 
				
			||||||
        color: const Color.fromARGB(255, 43, 43, 43),
 | 
					          ? [
 | 
				
			||||||
        child: Padding(
 | 
					              Row(
 | 
				
			||||||
          padding: const EdgeInsets.symmetric(horizontal: 16.0),
 | 
					 | 
				
			||||||
          child: Row(
 | 
					 | 
				
			||||||
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
					                mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
            crossAxisAlignment: CrossAxisAlignment.end,
 | 
					 | 
				
			||||||
                children: [
 | 
					                children: [
 | 
				
			||||||
              DropdownButton<String>(
 | 
					                  Text('tag: '),
 | 
				
			||||||
                value: _selectedType,
 | 
					                  InputChip(
 | 
				
			||||||
                dropdownColor: const Color.fromARGB(255, 43, 43, 43),
 | 
					                    label: Text(provider.tag!),
 | 
				
			||||||
                iconEnabledColor: Colors.white,
 | 
					                    backgroundColor: const Color(0xFF090909),
 | 
				
			||||||
                items: ["alles", "image", "video", "audio"].map((String value) {
 | 
					                    labelStyle: const TextStyle(color: Colors.white),
 | 
				
			||||||
                  return DropdownMenuItem<String>(
 | 
					                    onDeleted: () {
 | 
				
			||||||
                    value: value,
 | 
					                      provider.setTag(null);
 | 
				
			||||||
                    child: Text(value, style: TextStyle(color: Colors.white)),
 | 
					 | 
				
			||||||
                  );
 | 
					 | 
				
			||||||
                }).toList(),
 | 
					 | 
				
			||||||
                onChanged: (String? newValue) {
 | 
					 | 
				
			||||||
                  if (newValue != null) {
 | 
					 | 
				
			||||||
                    setState(() {
 | 
					 | 
				
			||||||
                      _selectedType = newValue;
 | 
					 | 
				
			||||||
                      _refreshMedia();
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              ),
 | 
					 | 
				
			||||||
              DropdownButton<String>(
 | 
					 | 
				
			||||||
                value: _modes[_selectedMode],
 | 
					 | 
				
			||||||
                dropdownColor: const Color.fromARGB(255, 43, 43, 43),
 | 
					 | 
				
			||||||
                iconEnabledColor: Colors.white,
 | 
					 | 
				
			||||||
                items: _modes.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 = _modes.indexOf(newValue);
 | 
					 | 
				
			||||||
                      _refreshMedia();
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              ),
 | 
					 | 
				
			||||||
              DropdownButton<int>(
 | 
					 | 
				
			||||||
                value: _crossAxisCount,
 | 
					 | 
				
			||||||
                dropdownColor: const Color.fromARGB(255, 43, 43, 43),
 | 
					 | 
				
			||||||
                iconEnabledColor: Colors.white,
 | 
					 | 
				
			||||||
                items: [0, 3, 4].map((int value) {
 | 
					 | 
				
			||||||
                  return DropdownMenuItem<int>(
 | 
					 | 
				
			||||||
                    value: value,
 | 
					 | 
				
			||||||
                    child: Text(
 | 
					 | 
				
			||||||
                      value == 0 ? 'auto' : '$value Spalten',
 | 
					 | 
				
			||||||
                      style: TextStyle(color: Colors.white),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                  );
 | 
					 | 
				
			||||||
                }).toList(),
 | 
					 | 
				
			||||||
                onChanged: (int? newValue) {
 | 
					 | 
				
			||||||
                  if (newValue != null) {
 | 
					 | 
				
			||||||
                    setState(() {
 | 
					 | 
				
			||||||
                      _crossAxisCount = newValue;
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                  ),
 | 
					                  ),
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
        ),
 | 
					            ]
 | 
				
			||||||
      ),
 | 
					          : null,
 | 
				
			||||||
      body: Stack(
 | 
					      body: RefreshIndicator(
 | 
				
			||||||
        children: [
 | 
					        onRefresh: () async {
 | 
				
			||||||
          RefreshIndicator(
 | 
					          await provider.loadMedia(reload: true);
 | 
				
			||||||
            onRefresh: _refreshMedia,
 | 
					        },
 | 
				
			||||||
            child: GridView.builder(
 | 
					        child: Consumer<MediaProvider>(
 | 
				
			||||||
 | 
					          builder: (context, mediaProvider, child) {
 | 
				
			||||||
 | 
					            return GridView.builder(
 | 
				
			||||||
              controller: _scrollController,
 | 
					              controller: _scrollController,
 | 
				
			||||||
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
 | 
					              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
 | 
				
			||||||
                crossAxisCount: _calculateCrossAxisCount(context),
 | 
					                crossAxisCount: mediaProvider.crossAxisCount == 0
 | 
				
			||||||
 | 
					                    ? (MediaQuery.of(context).size.width / 110)
 | 
				
			||||||
 | 
					                          .clamp(3, 5)
 | 
				
			||||||
 | 
					                          .toInt()
 | 
				
			||||||
 | 
					                    : mediaProvider.crossAxisCount,
 | 
				
			||||||
                crossAxisSpacing: 5.0,
 | 
					                crossAxisSpacing: 5.0,
 | 
				
			||||||
                mainAxisSpacing: 5.0,
 | 
					                mainAxisSpacing: 5.0,
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
              itemCount: mediaItems.length + (isLoading ? 1 : 0),
 | 
					              itemCount:
 | 
				
			||||||
 | 
					                  provider.mediaItems.length + (provider.isLoading ? 1 : 0),
 | 
				
			||||||
              itemBuilder: (context, index) {
 | 
					              itemBuilder: (context, index) {
 | 
				
			||||||
                if (index >= mediaItems.length) {
 | 
					                if (index >= provider.mediaItems.length) {
 | 
				
			||||||
                  return const Center(child: CircularProgressIndicator());
 | 
					                  return const Center(child: CircularProgressIndicator());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                final item = mediaItems[index];
 | 
					                final item = provider.mediaItems[index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return InkWell(
 | 
					                return InkWell(
 | 
				
			||||||
                  onTap: () => _navigateToDetail(item),
 | 
					                  onTap: () => Navigator.push(
 | 
				
			||||||
 | 
					                    context,
 | 
				
			||||||
 | 
					                    MaterialPageRoute(
 | 
				
			||||||
 | 
					                      builder: (context) => DetailView(initialItemId: item.id),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
                  child: Stack(
 | 
					                  child: Stack(
 | 
				
			||||||
                    fit: StackFit.expand,
 | 
					                    fit: StackFit.expand,
 | 
				
			||||||
                    children: <Widget>[
 | 
					                    children: <Widget>[
 | 
				
			||||||
@@ -289,67 +208,10 @@ class _MediaGridState extends State<MediaGrid> {
 | 
				
			|||||||
                  ),
 | 
					                  ),
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
          if (_selectedTag != null)
 | 
					 | 
				
			||||||
            Positioned(
 | 
					 | 
				
			||||||
              bottom: 20,
 | 
					 | 
				
			||||||
              left: MediaQuery.of(context).size.width * 0.2,
 | 
					 | 
				
			||||||
              right: MediaQuery.of(context).size.width * 0.2,
 | 
					 | 
				
			||||||
              child: Container(
 | 
					 | 
				
			||||||
                padding: const EdgeInsets.symmetric(
 | 
					 | 
				
			||||||
                  vertical: 10,
 | 
					 | 
				
			||||||
                  horizontal: 20,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                decoration: BoxDecoration(
 | 
					 | 
				
			||||||
                  color: Colors.black.withValues(alpha: 0.8),
 | 
					 | 
				
			||||||
                  borderRadius: BorderRadius.circular(12),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                child: Row(
 | 
					 | 
				
			||||||
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
					 | 
				
			||||||
                  children: [
 | 
					 | 
				
			||||||
                    Text(
 | 
					 | 
				
			||||||
                      'Tag: $_selectedTag',
 | 
					 | 
				
			||||||
                      style: const TextStyle(color: Colors.white),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    IconButton(
 | 
					 | 
				
			||||||
                      icon: const Icon(Icons.close, color: Colors.white),
 | 
					 | 
				
			||||||
                      onPressed: () {
 | 
					 | 
				
			||||||
                        setState(() {
 | 
					 | 
				
			||||||
                          _selectedTag = null;
 | 
					 | 
				
			||||||
                          _refreshMedia();
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                        _scrollController.animateTo(
 | 
					 | 
				
			||||||
                          0.0,
 | 
					 | 
				
			||||||
                          duration: const Duration(milliseconds: 500),
 | 
					 | 
				
			||||||
                          curve: Curves.easeOut,
 | 
					 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
                  ],
 | 
					 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
              ),
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
      /*floatingActionButton: FloatingActionButton(
 | 
					 | 
				
			||||||
        backgroundColor: Colors.black.withValues(alpha: 0.8),
 | 
					 | 
				
			||||||
        child: const Icon(Icons.arrow_upward, color: Colors.white),
 | 
					 | 
				
			||||||
        onPressed: () {
 | 
					 | 
				
			||||||
          _scrollController.animateTo(
 | 
					 | 
				
			||||||
            0.0,
 | 
					 | 
				
			||||||
            duration: const Duration(milliseconds: 500),
 | 
					 | 
				
			||||||
            curve: Curves.easeOut,
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ),*/
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  void dispose() {
 | 
					 | 
				
			||||||
    _scrollController.dispose();
 | 
					 | 
				
			||||||
    _debounceTimer?.cancel();
 | 
					 | 
				
			||||||
    super.dispose();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import 'package:http/http.dart' as http;
 | 
				
			|||||||
import 'package:f0ckapp/models/MediaItem.dart';
 | 
					import 'package:f0ckapp/models/MediaItem.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Future<List<MediaItem>> fetchMedia({
 | 
					Future<List<MediaItem>> fetchMedia({
 | 
				
			||||||
  String? older,
 | 
					  int? older,
 | 
				
			||||||
  String? type,
 | 
					  String? type,
 | 
				
			||||||
  int? mode,
 | 
					  int? mode,
 | 
				
			||||||
  bool? random,
 | 
					  bool? random,
 | 
				
			||||||
@@ -16,7 +16,7 @@ Future<List<MediaItem>> fetchMedia({
 | 
				
			|||||||
      'mode': (mode ?? 0).toString(),
 | 
					      'mode': (mode ?? 0).toString(),
 | 
				
			||||||
      'random': (random! ? 1 : 0).toString(),
 | 
					      'random': (random! ? 1 : 0).toString(),
 | 
				
			||||||
      if (tag != null) 'tag': tag,
 | 
					      if (tag != null) 'tag': tag,
 | 
				
			||||||
      if (older != null) 'older': older,
 | 
					      if (older != null) 'older': older.toString(),
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								pubspec.lock
									
									
									
									
									
								
							@@ -256,6 +256,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.16.0"
 | 
					    version: "1.16.0"
 | 
				
			||||||
 | 
					  nested:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: nested
 | 
				
			||||||
 | 
					      sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.0.0"
 | 
				
			||||||
  octo_image:
 | 
					  octo_image:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -336,6 +344,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.1.8"
 | 
					    version: "2.1.8"
 | 
				
			||||||
 | 
					  provider:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: provider
 | 
				
			||||||
 | 
					      sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "6.1.5"
 | 
				
			||||||
  rxdart:
 | 
					  rxdart:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.25+25
 | 
					version: 1.0.26+26
 | 
				
			||||||
 | 
					
 | 
				
			||||||
environment:
 | 
					environment:
 | 
				
			||||||
  sdk: ^3.9.0-100.2.beta
 | 
					  sdk: ^3.9.0-100.2.beta
 | 
				
			||||||
@@ -37,6 +37,7 @@ dependencies:
 | 
				
			|||||||
  cupertino_icons: ^1.0.8
 | 
					  cupertino_icons: ^1.0.8
 | 
				
			||||||
  cached_network_image: ^3.4.1
 | 
					  cached_network_image: ^3.4.1
 | 
				
			||||||
  cached_video_player_plus: ^3.0.3
 | 
					  cached_video_player_plus: ^3.0.3
 | 
				
			||||||
 | 
					  provider: ^6.1.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user