diff --git a/lib/controller/media_controller.dart b/lib/controller/media_controller.dart new file mode 100644 index 0000000..3eae3e3 --- /dev/null +++ b/lib/controller/media_controller.dart @@ -0,0 +1,110 @@ +import 'package:encrypt_shared_preferences/provider.dart'; +import 'package:f0ckapp/utils/animatedtransition.dart'; +import 'package:get/get.dart'; + +import 'package:f0ckapp/service/media_service.dart'; +import 'package:f0ckapp/models/media_item.dart'; + +class MediaController extends GetxController { + final EncryptedSharedPreferencesAsync storage = + EncryptedSharedPreferencesAsync.getInstance(); + + final RxList mediaItems = [].obs; + final RxBool isLoading = false.obs; + final RxString errorMessage = ''.obs; + final MediaService _mediaService = MediaService(); + + RxnString tag = RxnString(); + RxInt type = 0.obs; + RxInt mode = 0.obs; + RxBool random = false.obs; + late RxBool muted = false.obs; + late RxInt crossAxisCount = 0.obs; + late RxBool drawerSwipeEnabled = true.obs; + final Rx transitionType = PageTransition.opacity.obs; + + @override + void onInit() async { + super.onInit(); + await loadSettings(); + } + + Future loadSettings() async { + muted.value = await storage.getBoolean('muted') ?? false; + crossAxisCount.value = await storage.getInt('crossAxisCount') ?? 0; + drawerSwipeEnabled.value = + await storage.getBoolean('drawerSwipeEnabled') ?? true; + transitionType.value = + PageTransition.values[await storage.getInt('transitionType') ?? 0]; + } + + Future saveSettings() async { + await storage.setBoolean('muted', muted.value); + await storage.setInt('crossAxisCount', crossAxisCount.value); + await storage.setBoolean('drawerSwipeEnabled', drawerSwipeEnabled.value); + await storage.setInt('transitionType', transitionType.value.index); + } + + Future setTag(String? newTag) async { + tag.value = newTag; + await loadMediaItems(); + } + + Future setType(int newType) async { + type.value = newType; + await loadMediaItems(); + } + + Future setMode(int newMode) async { + mode.value = newMode; + await loadMediaItems(); + } + + Future toggleRandom() async { + random.value = !random.value; + await loadMediaItems(); + } + + Future toggleMuted() async { + muted.value = !muted.value; + await saveSettings(); + } + + Future setCrossAxisCount(int newCrossAxisCount) async { + crossAxisCount.value = newCrossAxisCount; + await saveSettings(); + } + + Future setDrawerSwipeEnabled(bool newValue) async { + drawerSwipeEnabled.value = newValue; + await saveSettings(); + } + + Future setTransitionType(PageTransition newType) async { + transitionType.value = newType; + await saveSettings(); + } + + Future loadMediaItems({int? older, bool append = false}) async { + if (isLoading.value) return; + try { + isLoading.value = true; + final List items = await _mediaService.fetchMediaItems( + type: type.value, + mode: mode.value, + random: random.value ? 1 : 0, + tag: tag.value, + older: older, + ); + + append ? mediaItems.addAll(items) : mediaItems.assignAll(items); + + errorMessage.value = ''; + } catch (e) { + errorMessage.value = 'Fehler beim Laden der Daten: ${e.toString()}'; + Get.snackbar('Error', e.toString()); + } finally { + isLoading.value = false; + } + } +} diff --git a/lib/providers/theme_provider.dart b/lib/controller/theme_controller.dart similarity index 94% rename from lib/providers/theme_provider.dart rename to lib/controller/theme_controller.dart index 9d75310..cdb78f3 100644 --- a/lib/providers/theme_provider.dart +++ b/lib/controller/theme_controller.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; + import 'package:get/get.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:encrypt_shared_preferences/provider.dart'; final ThemeData f0ckTheme = ThemeData( brightness: Brightness.dark, @@ -236,9 +237,8 @@ final ThemeData f0ck95dTheme = ThemeData( ); class ThemeController extends GetxController { - final FlutterSecureStorage secureStorage = const FlutterSecureStorage( - aOptions: AndroidOptions(encryptedSharedPreferences: true), - ); + final EncryptedSharedPreferencesAsync storage = + EncryptedSharedPreferencesAsync.getInstance(); final Rx currentTheme = f0ckTheme.obs; @@ -260,13 +260,13 @@ class ThemeController extends GetxController { Future _loadTheme() async { try { - final String? savedThemeName = await secureStorage.read(key: 'theme'); + final String? savedThemeName = await storage.getString('theme'); if (savedThemeName != null && themeMap.containsKey(savedThemeName)) { currentTheme.value = themeMap[savedThemeName]!; Get.changeTheme(currentTheme.value); } } catch (error) { - debugPrint('Fehler beim Laden des Themes: $error'); + Get.snackbar('', 'Fehler beim Laden des Themes: $error'); currentTheme.value = f0ckTheme; Get.changeTheme(f0ckTheme); } @@ -274,7 +274,7 @@ class ThemeController extends GetxController { Future updateTheme(String themeName) async { try { - await secureStorage.write(key: 'theme', value: themeName); + await storage.setString('theme', themeName); if (themeMap.containsKey(themeName)) { currentTheme.value = themeMap[themeName]!; Get.changeTheme(currentTheme.value); @@ -283,7 +283,7 @@ class ThemeController extends GetxController { Get.changeTheme(f0ckTheme); } } catch (error) { - debugPrint('Fehler beim Aktualisieren des Themes: $error'); + Get.snackbar('', 'Fehler beim Aktualisieren des Themes: $error'); } } } diff --git a/lib/main.dart b/lib/main.dart index 83e18ff..4e9c516 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,46 +1,44 @@ -import 'package:f0ckapp/providers/theme_provider.dart'; +import 'package:f0ckapp/screens/settings_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; +import 'package:encrypt_shared_preferences/provider.dart'; -import 'package:f0ckapp/utils/appversion_util.dart'; -import 'package:f0ckapp/services/api_service.dart'; +import 'package:f0ckapp/utils/appversion.dart'; +import 'package:f0ckapp/controller/theme_controller.dart'; +import 'package:f0ckapp/controller/media_controller.dart'; +import 'package:f0ckapp/screens/detail_view.dart'; import 'package:f0ckapp/screens/media_grid.dart'; -import 'package:f0ckapp/screens/settings_screen.dart'; -import 'package:f0ckapp/screens/detailview_screen.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + await EncryptedSharedPreferencesAsync.initialize('VokTnbAbemBUa2j9'); await AppVersion.init(); + Get.put(MediaController()); final ThemeController themeController = Get.put(ThemeController()); - final api = ApiService(); - await api.fetchMedia(); - Get.put(api); - runApp( - GetMaterialApp( - theme: themeController.currentTheme.value, - initialRoute: '/', - getPages: [ - GetPage(name: '/', page: () => MediaGrid()), - GetPage(name: '/settings', page: () => SettingsPage()), - GetPage( - name: '/:itemId', - page: () { - int? test = int.tryParse(Get.parameters['itemId']!); - if (test == null) { - return Scaffold(body: Center(child: Text('oof'))); - } - return DetailView(initialItemId: test); - }, - ), - ], - unknownRoute: GetPage( - name: '/notfound', - page: () => Center(child: Text('oof')), + Obx( + () => MaterialApp( + navigatorKey: Get.key, + theme: themeController.currentTheme.value, + debugShowCheckedModeBanner: false, + onGenerateRoute: (RouteSettings settings) { + final uri = Uri.parse(settings.name ?? '/'); + + if (uri.path == '/' || uri.pathSegments.isEmpty) { + return MaterialPageRoute(builder: (_) => MediaGrid()); + } + + if (uri.pathSegments.length == 1) { + final int id = int.parse(uri.pathSegments.first); + return MaterialPageRoute(builder: (_) => DetailView(initialId: id)); + } + + return MaterialPageRoute(builder: (_) => MediaGrid()); + }, ), ), ); diff --git a/lib/models/mediaitem_model.dart b/lib/models/media_item.dart similarity index 80% rename from lib/models/mediaitem_model.dart rename to lib/models/media_item.dart index e37463c..788fcb9 100644 --- a/lib/models/mediaitem_model.dart +++ b/lib/models/media_item.dart @@ -18,6 +18,15 @@ class MediaItem { }); factory MediaItem.fromJson(Map json) { + List parsedTags = []; + if (json['tags'] is List) { + parsedTags = (json['tags'] as List) + .map((tagJson) => Tag.fromJson(tagJson as Map)) + .toList(); + } else { + parsedTags = []; + } + return MediaItem( id: json['id'], mime: json['mime'], @@ -25,9 +34,7 @@ class MediaItem { stamp: json['stamp'], dest: json['dest'], mode: json['mode'], - tags: (json['tags'] as List) - .map((tagJson) => Tag.fromJson(tagJson)) - .toList(), + tags: parsedTags, ); } diff --git a/lib/screens/detailview_screen.dart b/lib/screens/detail_view.dart similarity index 50% rename from lib/screens/detailview_screen.dart rename to lib/screens/detail_view.dart index f94aadd..a53b54a 100644 --- a/lib/screens/detailview_screen.dart +++ b/lib/screens/detail_view.dart @@ -1,91 +1,70 @@ import 'dart:io'; +import 'package:f0ckapp/utils/animatedtransition.dart'; +import 'package:f0ckapp/utils/smartrefreshindicator.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:get/get.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:f0ckapp/models/mediaitem_model.dart'; -import 'package:f0ckapp/services/api_service.dart'; -import 'package:f0ckapp/widgets/end_drawer.dart'; +import 'package:f0ckapp/controller/media_controller.dart'; +import 'package:f0ckapp/models/media_item.dart'; +import 'package:f0ckapp/screens/media_grid.dart'; import 'package:f0ckapp/screens/fullscreen_screen.dart'; +import 'package:f0ckapp/widgets/end_drawer.dart'; import 'package:f0ckapp/widgets/video_widget.dart'; class DetailView extends StatefulWidget { - final int initialItemId; - const DetailView({super.key, required this.initialItemId}); + final int initialId; + const DetailView({super.key, required this.initialId}); @override State createState() => _DetailViewState(); } class _DetailViewState extends State { - final ApiService apiService = Get.find(); + final MediaController controller = Get.find(); + MediaItem? item; + bool isLoading = false; PageController? _pageController; - Future? _loadingFuture; int _currentPage = 0; @override void initState() { super.initState(); - if (!_mediaItemExists(widget.initialItemId)) { - _loadingFuture = _fetchAndPreloadMedia(widget.initialItemId); - } else { - _initializePageController(); - } + _setupInitialView(); + + ever(controller.drawerSwipeEnabled, (_) { + setState(() {}); + }); } - bool _mediaItemExists(int id) { - return apiService.mediaItems.any((media) => media.id == id); - } + Future _setupInitialView() async { + bool itemExists = controller.mediaItems.any( + (media) => media.id == widget.initialId, + ); - Future _fetchAndPreloadMedia(int targetId) async { - try { - WidgetsBinding.instance.addPostFrameCallback((_) async { - await apiService.setTag(null); - }); - await apiService.fetchMedia(id: targetId + 50, reset: false); - _initializePageController(); - } catch (e) { - _showMsg("Medien konnten nicht geladen werden"); + if (!itemExists) { + await _initializeDetail(widget.initialId); } + _initializePageController(); } void _initializePageController() { - _currentPage = apiService.mediaItems.indexWhere( - (media) => media.id == widget.initialItemId, + _currentPage = controller.mediaItems.indexWhere( + (media) => media.id == widget.initialId, ); - if (_currentPage < 0) { - _currentPage = 0; - } + if (_currentPage < 0) _currentPage = 0; _pageController = PageController(initialPage: _currentPage) ..addListener(() { - setState(() { - _currentPage = _pageController!.page!.round(); - }); + setState(() => _currentPage = _pageController!.page!.round()); }); setState(() {}); } - @override - void dispose() { - _pageController?.dispose(); - super.dispose(); - } - - MediaItem? _findMediaItem() { - try { - return apiService.mediaItems.firstWhere( - (media) => media.id == widget.initialItemId, - ); - } catch (e) { - return null; - } - } - Future _downloadMedia(MediaItem item) async { final File file = await DefaultCacheManager().getSingleFile(item.mediaUrl); final MethodChannel methodChannel = const MethodChannel('MediaShit'); @@ -102,59 +81,60 @@ class _DetailViewState extends State { void _showMsg(String message) { if (!mounted) return; - ScaffoldMessenger.of(context) - ..removeCurrentSnackBar() - ..showSnackBar(SnackBar(content: Text(message))); + Get + ..closeAllSnackbars() + ..snackbar('hehe', message, snackPosition: SnackPosition.BOTTOM); + } + + Future _initializeDetail(int deepLinkId) async { + item = controller.mediaItems.firstWhereOrNull( + (element) => element.id == deepLinkId, + ); + + if (item == null) { + setState(() => isLoading = true); + await controller.loadMediaItems(older: deepLinkId + 50); + item = controller.mediaItems.firstWhereOrNull( + (element) => element.id == deepLinkId, + ); + if (item == null) { + Get.offAll(() => const MediaGrid()); + } + setState(() => isLoading = false); + } } @override Widget build(BuildContext context) { - if (_loadingFuture != null) { - return FutureBuilder( - future: _loadingFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return Scaffold( - appBar: AppBar(title: const Text("Detail")), - body: const Center(child: CircularProgressIndicator()), - ); - } else { - MediaItem? item = _findMediaItem(); - if (item == null) { - return Scaffold( - appBar: AppBar(title: const Text("Detail")), - body: const Center(child: Text("f0ck nicht gefunden")), - ); - } - return _buildDetail(); - } - }, - ); - } - - MediaItem? existingItem = _findMediaItem(); - if (existingItem == null) { + if (isLoading) { return Scaffold( - appBar: AppBar(title: const Text("Detail")), - body: const Center(child: Text("f0ck nicht gefunden")), + appBar: AppBar( + title: const Text("f0ck"), + leading: Navigator.canPop(context) + ? null + : IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () { + Get.offAll(() => const MediaGrid()); + }, + ), + ), + body: const Center(child: CircularProgressIndicator()), ); } - return _buildDetail(); - } - Widget _buildDetail() { - final MediaItem currentItem = apiService.mediaItems[_currentPage]; + final MediaItem currentItem = controller.mediaItems[_currentPage]; return Scaffold( endDrawer: const EndDrawer(), - endDrawerEnableOpenDragGesture: false, - persistentFooterButtons: apiService.tag.value != null + endDrawerEnableOpenDragGesture: controller.drawerSwipeEnabled.value, + persistentFooterButtons: controller.tag.value != null ? [ Center( child: InputChip( - label: Text(apiService.tag.value!), + label: Text(controller.tag.value!), onDeleted: () { - apiService.setTag(null); + controller.setTag(null); Get.offAllNamed('/'); }, ), @@ -254,69 +234,72 @@ class _DetailViewState extends State { SliverFillRemaining( child: PageView.builder( controller: _pageController, - itemCount: apiService.mediaItems.length, + itemCount: controller.mediaItems.length, itemBuilder: (context, index) { - final MediaItem pageItem = apiService.mediaItems[index]; + final MediaItem pageItem = controller.mediaItems[index]; return AnimatedBuilder( animation: _pageController!, builder: (context, child) { - double value = 0; - if (_pageController!.position.haveDimensions) { - value = (_pageController!.page! - index).abs(); - } - double factor = Curves.easeOut.transform( - 1 - value.clamp(0.0, 1.0), + return buildAnimatedTransition( + context: context, + child: child!, + pageController: _pageController!, + index: index, + controller: controller, ); - double scale = 0.8 + factor * 0.2; - return Transform.scale(scale: scale, child: child); }, - child: SafeArea( - top: false, - child: SingleChildScrollView( - child: Column( - children: [ - if (pageItem.mime.startsWith('image')) - CachedNetworkImage( - imageUrl: pageItem.mediaUrl, - fit: BoxFit.contain, - placeholder: (context, url) => const Center( - child: CircularProgressIndicator(), - ), - errorWidget: (context, url, error) => - const Center(child: Icon(Icons.error)), - ) - else - VideoWidget( - details: pageItem, - isActive: index == _currentPage, - ), - const SizedBox(height: 10, width: double.infinity), - Wrap( - alignment: WrapAlignment.center, - spacing: 5.0, - children: pageItem.tags.map((tag) { - return ActionChip( - onPressed: () { - if (tag.tag == 'sfw' || tag.tag == 'nsfw') { - return; - } - apiService.setTag(tag.tag); - Get.offAllNamed('/'); - }, - label: Text(tag.tag), - backgroundColor: switch (tag.id) { - 1 => Colors.green, - 2 => Colors.red, - _ => const Color(0xFF090909), - }, - labelStyle: const TextStyle( - color: Colors.white, + child: SmartRefreshIndicator( + onRefresh: () async { + _showMsg('not hehe'); + }, + child: SafeArea( + top: false, + child: SingleChildScrollView( + child: Column( + children: [ + if (pageItem.mime.startsWith('image')) + CachedNetworkImage( + imageUrl: pageItem.mediaUrl, + fit: BoxFit.contain, + placeholder: (context, url) => const Center( + child: CircularProgressIndicator(), ), - ); - }).toList(), - ), - const SizedBox(height: 20), - ], + errorWidget: (context, url, error) => + const Center(child: Icon(Icons.error)), + ) + else + VideoWidget( + details: pageItem, + isActive: index == _currentPage, + ), + const SizedBox(height: 10, width: double.infinity), + Wrap( + alignment: WrapAlignment.center, + spacing: 5.0, + children: pageItem.tags.map((tag) { + return ActionChip( + onPressed: () { + if (tag.tag == 'sfw' || tag.tag == 'nsfw') { + return; + } + controller.setTag(tag.tag); + Get.offAllNamed('/'); + }, + label: Text(tag.tag), + backgroundColor: switch (tag.id) { + 1 => Colors.green, + 2 => Colors.red, + _ => const Color(0xFF090909), + }, + labelStyle: const TextStyle( + color: Colors.white, + ), + ); + }).toList(), + ), + const SizedBox(height: 20), + ], + ), ), ), ), diff --git a/lib/screens/fullscreen_screen.dart b/lib/screens/fullscreen_screen.dart index 6356319..8a271ec 100644 --- a/lib/screens/fullscreen_screen.dart +++ b/lib/screens/fullscreen_screen.dart @@ -3,7 +3,7 @@ import 'package:flutter/services.dart'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:f0ckapp/models/mediaitem_model.dart'; +import 'package:f0ckapp/models/media_item.dart'; import 'package:f0ckapp/widgets/video_widget.dart'; class FullScreenMediaView extends StatefulWidget { diff --git a/lib/screens/media_grid.dart b/lib/screens/media_grid.dart index b7d3bf8..ea6b633 100644 --- a/lib/screens/media_grid.dart +++ b/lib/screens/media_grid.dart @@ -1,31 +1,43 @@ +import 'package:f0ckapp/utils/smartrefreshindicator.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:f0ckapp/services/api_service.dart'; -import 'package:f0ckapp/widgets/media_tile.dart'; -import 'package:f0ckapp/widgets/end_drawer.dart'; +import 'package:f0ckapp/utils/customsearchdelegate.dart'; import 'package:f0ckapp/widgets/filter_bar.dart'; -import 'package:f0ckapp/utils/customsearchdelegate_util.dart'; +import 'package:f0ckapp/widgets/end_drawer.dart'; +import 'package:f0ckapp/controller/media_controller.dart'; +import 'package:f0ckapp/widgets/media_tile.dart'; class MediaGrid extends StatefulWidget { const MediaGrid({super.key}); @override - State createState() => _MediaGrid(); + State createState() => _MediaGridState(); } -class _MediaGrid extends State { - final ApiService apiService = Get.find(); +class _MediaGridState extends State { + final MediaController controller = Get.find(); final ScrollController _scrollController = ScrollController(); @override void initState() { super.initState(); - _scrollController.addListener(() async { - if (_scrollController.position.pixels >= - _scrollController.position.maxScrollExtent - 300) { - await apiService.fetchMedia(); + controller.loadMediaItems(); + + ever(controller.drawerSwipeEnabled, (_) { + setState(() {}); + }); + + _scrollController.addListener(() { + if (_scrollController.position.extentAfter < 200 && + !controller.isLoading.value) { + controller.loadMediaItems( + older: controller.mediaItems.isNotEmpty + ? controller.mediaItems.last.id + : null, + append: true, + ); } }); } @@ -34,14 +46,102 @@ class _MediaGrid extends State { Widget build(BuildContext context) { return Scaffold( endDrawer: EndDrawer(), + endDrawerEnableOpenDragGesture: controller.drawerSwipeEnabled.value, + body: RefreshIndicator( + edgeOffset: 100, + onRefresh: () async { + await controller.loadMediaItems(); + }, + child: CustomScrollView( + controller: _scrollController, + slivers: [ + SliverAppBar( + floating: true, + snap: true, + title: GestureDetector( + child: Row( + children: [ + Image.asset( + 'assets/images/f0ck_small.webp', + fit: BoxFit.fitHeight, + ), + const SizedBox(width: 10), + const Text('fApp', style: TextStyle(fontSize: 24)), + ], + ), + onTap: () { + controller.setTag(null); + }, + ), + actions: [ + IconButton( + icon: const Icon(Icons.search), + onPressed: () async { + await showSearch( + context: context, + delegate: CustomSearchDelegate(), + ); + }, + ), + Obx( + () => IconButton( + icon: Icon( + controller.random.value + ? Icons.shuffle_on_outlined + : Icons.shuffle, + ), + onPressed: () async { + await controller.toggleRandom(); + }, + ), + ), + + Builder( + builder: (context) { + return IconButton( + icon: const Icon(Icons.menu), + onPressed: () { + Scaffold.of(context).openEndDrawer(); + }, + ); + }, + ), + ], + ), + SliverPadding( + padding: EdgeInsets.zero, + sliver: Obx( + () => SliverGrid( + delegate: SliverChildBuilderDelegate((context, index) { + return MediaTile(item: controller.mediaItems[index]); + }, childCount: controller.mediaItems.length), + gridDelegate: controller.crossAxisCount.value == 0 + ? const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 150, + crossAxisSpacing: 5, + mainAxisSpacing: 5, + childAspectRatio: 1, + ) + : SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: controller.crossAxisCount.value, + crossAxisSpacing: 5, + mainAxisSpacing: 5, + childAspectRatio: 1, + ), + ), + ), + ), + ], + ), + ), persistentFooterButtons: [ Obx(() { - if (apiService.tag.value != null) { + if (controller.tag.value != null) { return Center( child: InputChip( - label: Text(apiService.tag.value!), - onDeleted: () { - apiService.setTag(null); + label: Text(controller.tag.value!), + onDeleted: () async { + await controller.setTag(null); Get.offAllNamed('/'); }, ), @@ -51,79 +151,6 @@ class _MediaGrid extends State { } }), ], - body: CustomScrollView( - controller: _scrollController, - slivers: [ - SliverAppBar( - floating: true, - snap: true, - title: GestureDetector( - onTap: () async { - apiService.setTag(null); - }, - child: Row( - children: [ - Image.asset( - 'assets/images/f0ck_small.webp', - fit: BoxFit.fitHeight, - ), - const SizedBox(width: 10), - const Text('fApp', style: TextStyle(fontSize: 24)), - ], - ), - ), - actions: [ - IconButton( - icon: const Icon(Icons.search), - onPressed: () async { - await showSearch( - context: context, - delegate: CustomSearchDelegate(), - ); - }, - ), - Obx( - () => IconButton( - icon: Icon( - apiService.random.value - ? Icons.shuffle_on_outlined - : Icons.shuffle, - ), - onPressed: () { - apiService.toggleRandom(); - }, - ), - ), - Builder( - builder: (context) { - return IconButton( - icon: const Icon(Icons.menu), - onPressed: () { - Scaffold.of(context).openEndDrawer(); - }, - ); - }, - ), - ], - ), - SliverPadding( - padding: EdgeInsets.zero, - sliver: Obx( - () => SliverGrid( - delegate: SliverChildBuilderDelegate((context, index) { - return MediaTile(item: apiService.mediaItems[index]); - }, childCount: apiService.mediaItems.length), - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 150, - crossAxisSpacing: 5, - mainAxisSpacing: 5, - childAspectRatio: 1, - ), - ), - ), - ), - ], - ), bottomNavigationBar: FilterBar(scrollController: _scrollController), ); } diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 43a36bb..8ed0750 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -1,10 +1,11 @@ +import 'package:f0ckapp/utils/animatedtransition.dart'; import 'package:flutter/material.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; - -import 'package:f0ckapp/widgets/end_drawer.dart'; import 'package:get/get.dart'; +import 'package:f0ckapp/controller/media_controller.dart'; + class SettingsPage extends StatefulWidget { const SettingsPage({super.key}); @@ -13,75 +14,94 @@ class SettingsPage extends StatefulWidget { } class _SettingsPageState extends State { - int _columns = 3; - bool _drawerSwipeEnabled = true; - - void _showMsg(String message, BuildContext context) { - ScaffoldMessenger.of(context) - ..removeCurrentSnackBar() - ..showSnackBar(SnackBar(content: Text(message))); - } + final MediaController controller = Get.find(); @override Widget build(BuildContext context) { return Scaffold( - endDrawerEnableOpenDragGesture: _drawerSwipeEnabled, - endDrawer: EndDrawer(), body: CustomScrollView( slivers: [ SliverAppBar( floating: false, pinned: true, title: const Text('Settings'), - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - Get.back(); - }, - ), ), SliverList( delegate: SliverChildListDelegate([ - /*Padding( - padding: const EdgeInsets.all(16.0), - child: Text( - "Anzahl der Spalten", - style: Theme.of(context).textTheme.titleLarge, - ), - ), ListTile( title: const Text("Spaltenanzahl"), - trailing: DropdownButton( - value: mediaState.crossAxisCount, - dropdownColor: const Color.fromARGB(255, 43, 43, 43), - iconEnabledColor: Colors.white, - items: [0, 3, 4, 5].map((int value) { - return DropdownMenuItem( - value: value, - child: Text(value == 0 ? 'auto' : '$value Spalten'), - ); - }).toList(), - onChanged: (int? newValue) { - if (newValue != null) { - setState(() { - _columns = newValue; - mediaNotifier.setCrossAxisCount(newValue); - }); - } - }, + trailing: Obx( + () => DropdownButton( + value: controller.crossAxisCount.value, + dropdownColor: const Color.fromARGB(255, 43, 43, 43), + iconEnabledColor: Colors.white, + items: [0, 3, 4, 5].map((int value) { + return DropdownMenuItem( + value: value, + child: Text(value == 0 ? 'auto' : '$value Spalten'), + ); + }).toList(), + onChanged: (int? newValue) async { + if (newValue != null) { + await controller.setCrossAxisCount(newValue); + setState(() {}); + } + }, + ), ), - ),*/ + ), + const Divider(), + ListTile( + title: const Text("Seitenwechselanimation"), + trailing: Obx( + () => DropdownButton( + value: controller.transitionType.value, + dropdownColor: const Color.fromARGB(255, 43, 43, 43), + iconEnabledColor: Colors.white, + items: PageTransition.values.map((PageTransition type) { + String label; + switch (type) { + case PageTransition.opacity: + label = 'Opacity'; + break; + case PageTransition.scale: + label = 'Scale'; + break; + case PageTransition.slide: + label = 'Slide'; + break; + case PageTransition.rotate: + label = 'Rotate'; + break; + case PageTransition.flip: + label = 'Flip'; + break; + } + return DropdownMenuItem( + value: type, + child: Text(label), + ); + }).toList(), + onChanged: (PageTransition? newValue) async { + if (newValue != null) { + await controller.setTransitionType(newValue); + setState(() {}); + } + }, + ), + ), + ), + const Divider(), SwitchListTile( title: const Text("Drawer per Geste öffnen"), subtitle: const Text( "Wähle, ob der Drawer mit einer Wischgeste geschlossen/ geöffnet werden kann.", ), - value: _drawerSwipeEnabled, - onChanged: (bool value) { - setState(() { - _drawerSwipeEnabled = value; - }); + value: controller.drawerSwipeEnabled.value, + onChanged: (bool value) async { + await controller.setDrawerSwipeEnabled(value); + setState(() {}); }, ), const Divider(), @@ -91,7 +111,7 @@ class _SettingsPageState extends State { onPressed: () async { await DefaultCacheManager().emptyCache(); if (!mounted) return; - _showMsg('Cache wurde geleert.', context); + Get.snackbar('', 'der Cache wurde geleert.'); }, child: const Text("Löschen"), ), diff --git a/lib/service/media_service.dart b/lib/service/media_service.dart new file mode 100644 index 0000000..014e40f --- /dev/null +++ b/lib/service/media_service.dart @@ -0,0 +1,39 @@ +import 'package:get/get.dart'; + +import 'package:f0ckapp/models/media_item.dart'; + +const List mediaTypes = ["alles", "image", "video", "audio"]; +const List mediaModes = ["sfw", "nsfw", "untagged", "all"]; + +class MediaService extends GetConnect { + Future> fetchMediaItems({ + required int type, + required int mode, + required int random, + String? tag, + int? older, + }) async { + final queryParameters = { + 'type': type.toString(), + 'mode': mode.toString(), + 'random': random.toString(), + if (tag != null) 'tag': tag, + if (older != null) 'older': older.toString(), + }; + + try { + final response = await get( + 'https://api.f0ck.me/items/get', + query: queryParameters, + ); + if (response.status.code == 200 && response.body is List) { + final data = response.body as List; + return data.map((json) => MediaItem.fromJson(json)).toList(); + } else { + return Future.error('Fehler beim Laden der Daten: ${response.body}'); + } + } catch (e) { + return Future.error('Netzwerkfehler: ${e.toString()}'); + } + } +} diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart deleted file mode 100644 index 025b6e4..0000000 --- a/lib/services/api_service.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:get/get.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; - -import 'package:f0ckapp/models/mediaitem_model.dart'; - -const List mediaTypes = ["alles", "image", "video", "audio"]; -const List mediaModes = ["sfw", "nsfw", "untagged", "all"]; - -class ApiService extends GetConnect { - final _storage = const FlutterSecureStorage( - aOptions: AndroidOptions(encryptedSharedPreferences: true), - ); - RxList mediaItems = [].obs; - RxnString tag = RxnString(); - RxInt type = 0.obs; - RxInt mode = 0.obs; - RxBool random = false.obs; - RxBool muted = false.obs; - - bool _isFetching = false; - DateTime? _lastFetchTime; - final Duration _minFetchInterval = Duration(milliseconds: 500); - - @override - void onInit() { - super.onInit(); - loadMutedState(); - everAll([tag, type, mode, random], (_) => fetchMedia(reset: true)); - } - - Future loadMutedState() async { - String? value = await _storage.read(key: 'muted'); - muted.value = value == 'true'; - } - - Future toggleMuted() async { - muted.value = !muted.value; - await _storage.write(key: 'muted', value: muted.value.toString()); - } - - Future setTag(String? newTag) async { - tag.value = newTag; - return await fetchMedia(reset: true); - } - - Future setType(int newType) async { - type.value = newType >= 0 && newType < mediaTypes.length ? newType : 0; - return await fetchMedia(reset: true); - } - - Future setMode(int newMode) async { - mode.value = newMode >= 0 && newMode < mediaModes.length ? newMode : 0; - return await fetchMedia(reset: true); - } - - Future toggleRandom() async { - random.value = !random.value; - return await fetchMedia(reset: true); - } - - Future fetchMedia({int? id, bool reset = false}) async { - if (!reset) { - if (_isFetching) return; - if (_lastFetchTime != null && - DateTime.now().difference(_lastFetchTime!) < _minFetchInterval) { - return; - } - } - _lastFetchTime = DateTime.now(); - _isFetching = true; - - if (reset) mediaItems.clear(); - - final int? older = - id ?? (mediaItems.isNotEmpty ? mediaItems.last.id : null); - final Uri url = Uri.parse('https://api.f0ck.me/items/get').replace( - queryParameters: { - 'type': mediaTypes[type.value], - 'mode': mode.value.toString(), - 'random': (random.value ? 1 : 0).toString(), - if (tag.value != null) 'tag': tag.value, - if (older != null) 'older': older.toString(), - }, - ); - - try { - final Response response = await get(url.toString()); - - if (response.isOk && response.body is List) { - List newMedia = (response.body as List) - .map((json) => MediaItem.fromJson(json)) - .toList(); - - if (reset) { - mediaItems.assignAll(newMedia); - } else { - mediaItems.addAll(newMedia); - } - } else { - debugPrint('Fehler beim Laden der MediaItems: ${response.statusText}'); - } - } catch (e) { - debugPrint('Exception beim Laden der MediaItems: $e'); - } finally { - _isFetching = false; - } - } -} diff --git a/lib/utils/animatedtransition.dart b/lib/utils/animatedtransition.dart new file mode 100644 index 0000000..10aca63 --- /dev/null +++ b/lib/utils/animatedtransition.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; + +import 'package:f0ckapp/controller/media_controller.dart'; + +enum PageTransition { opacity, scale, slide, rotate, flip } + +Widget buildAnimatedTransition({ + required BuildContext context, + required Widget child, + required PageController pageController, + required int index, + required MediaController controller, + }) { + final double value = pageController.position.haveDimensions + ? pageController.page! - index + : 0; + + switch (controller.transitionType.value) { + case PageTransition.opacity: + return Opacity( + opacity: Curves.easeOut.transform(1 - value.abs().clamp(0.0, 500.0)), + child: Transform(transform: Matrix4.identity(), child: child), + ); + case PageTransition.scale: + return Transform.scale( + scale: + 0.8 + + Curves.easeOut.transform(1 - value.abs().clamp(0.0, 1.0)) * 0.2, + child: child, + ); + case PageTransition.slide: + return Transform.translate( + offset: Offset(300 * value.abs(), 0), + child: child, + ); + case PageTransition.rotate: + return Opacity( + opacity: (1 - value.abs()).clamp(0.0, 1.0), + child: Transform.rotate(angle: value.abs() * 0.5, child: child), + ); + case PageTransition.flip: + return Transform( + transform: Matrix4.identity() + ..setEntry(3, 2, 0.001) + ..rotateY(value.abs()), + alignment: Alignment.center, + child: child, + ); + } + /*int blah = 1; + + if (blah == 0) { + return Opacity( + opacity: Curves.easeOut.transform(1 - value.abs().clamp(0.0, 500.0)), + child: Transform(transform: Matrix4.identity(), child: child), + ); + } else { + return Transform.scale( + scale: + 0.8 + + Curves.easeOut.transform(1 - value.abs().clamp(0.0, 1.0)) * 0.2, + child: child, + ); + }*/ + } \ No newline at end of file diff --git a/lib/utils/appversion.dart b/lib/utils/appversion.dart new file mode 100644 index 0000000..8611007 --- /dev/null +++ b/lib/utils/appversion.dart @@ -0,0 +1,12 @@ +import 'package:flutter/services.dart'; + +class AppVersion { + static late String version; + + static Future init() async { + final String yaml = await rootBundle.loadString('pubspec.yaml'); + final RegExpMatch? match = RegExp(r'^version:\s*(.*)$', multiLine: true).firstMatch(yaml); + final String? v = match?.group(1)!.replaceAll('"', '').replaceAll("'", '').trim(); + version = v ?? ""; + } +} diff --git a/lib/utils/appversion_util.dart b/lib/utils/appversion_util.dart deleted file mode 100644 index 8e3f8fc..0000000 --- a/lib/utils/appversion_util.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:package_info_plus/package_info_plus.dart'; - -class AppVersion { - static String version = ""; - - static Future init() async { - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - version = '${packageInfo.version}+${packageInfo.buildNumber}'; - } -} diff --git a/lib/utils/customsearchdelegate_util.dart b/lib/utils/customsearchdelegate.dart similarity index 96% rename from lib/utils/customsearchdelegate_util.dart rename to lib/utils/customsearchdelegate.dart index 40e5f37..c2021b3 100644 --- a/lib/utils/customsearchdelegate_util.dart +++ b/lib/utils/customsearchdelegate.dart @@ -1,16 +1,16 @@ import 'dart:async'; import 'dart:convert'; +import 'package:f0ckapp/controller/media_controller.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; -import 'package:f0ckapp/services/api_service.dart'; import 'package:f0ckapp/models/suggestion_model.dart'; class CustomSearchDelegate extends SearchDelegate { - final ApiService apiService = Get.find(); + final MediaController controller = Get.find(); Timer? _debounceTimer; List? _suggestions; bool _isLoading = false; @@ -145,7 +145,7 @@ class CustomSearchDelegate extends SearchDelegate { style: TextStyle(fontSize: 12), ), onTap: () async { - await apiService.setTag(suggestion.tag); + await controller.setTag(suggestion.tag); close(context, suggestion.tag); }, ); diff --git a/lib/utils/smartrefreshindicator_util.dart b/lib/utils/smartrefreshindicator.dart similarity index 100% rename from lib/utils/smartrefreshindicator_util.dart rename to lib/utils/smartrefreshindicator.dart diff --git a/lib/widgets/end_drawer.dart b/lib/widgets/end_drawer.dart index ac71292..07b7a49 100644 --- a/lib/widgets/end_drawer.dart +++ b/lib/widgets/end_drawer.dart @@ -1,10 +1,10 @@ +import 'package:f0ckapp/screens/settings_screen.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:f0ckapp/providers/theme_provider.dart'; -//import 'package:f0ckapp/services/api_service.dart'; -import 'package:f0ckapp/utils/appversion_util.dart'; +import 'package:f0ckapp/controller/theme_controller.dart'; +import 'package:f0ckapp/utils/appversion.dart'; class EndDrawer extends StatelessWidget { const EndDrawer({super.key}); @@ -17,7 +17,6 @@ class EndDrawer extends StatelessWidget { @override Widget build(BuildContext context) { - //final ApiService c = Get.find(); final ThemeController themeController = Get.find(); return Drawer( @@ -115,7 +114,7 @@ class EndDrawer extends StatelessWidget { title: const Text('Settings'), onTap: () { Navigator.pop(context); - Get.toNamed('/settings'); + Get.bottomSheet(SettingsPage()); }, ), ListTile( diff --git a/lib/widgets/filter_bar.dart b/lib/widgets/filter_bar.dart index 89761e7..18594a5 100644 --- a/lib/widgets/filter_bar.dart +++ b/lib/widgets/filter_bar.dart @@ -2,7 +2,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:f0ckapp/services/api_service.dart'; +import 'package:f0ckapp/controller/media_controller.dart'; +import 'package:f0ckapp/service/media_service.dart'; class FilterBar extends StatelessWidget { final ScrollController scrollController; @@ -14,7 +15,7 @@ class FilterBar extends StatelessWidget { @override Widget build(BuildContext context) { - final ApiService c = Get.find(); + final MediaController c = Get.find(); return BottomAppBar( height: 50, diff --git a/lib/widgets/media_tile.dart b/lib/widgets/media_tile.dart index 6a7f15e..8526985 100644 --- a/lib/widgets/media_tile.dart +++ b/lib/widgets/media_tile.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:get/get.dart'; -import 'package:f0ckapp/models/mediaitem_model.dart'; +import 'package:f0ckapp/models/media_item.dart'; class MediaTile extends StatelessWidget { final MediaItem item; @@ -14,7 +14,6 @@ class MediaTile extends StatelessWidget { Widget build(BuildContext context) { return InkWell( onTap: () { - //Navigator.pushNamed(context, '/${item.id}'); Get.toNamed('/${item.id}'); }, child: Stack( diff --git a/lib/widgets/video_widget.dart b/lib/widgets/video_widget.dart index 3d46953..bc471c2 100644 --- a/lib/widgets/video_widget.dart +++ b/lib/widgets/video_widget.dart @@ -1,12 +1,12 @@ import 'dart:async'; -import 'package:f0ckapp/services/api_service.dart'; +import 'package:f0ckapp/controller/media_controller.dart'; +import 'package:f0ckapp/models/media_item.dart'; import 'package:flutter/material.dart'; import 'package:cached_video_player_plus/cached_video_player_plus.dart'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:f0ckapp/models/mediaitem_model.dart'; import 'package:f0ckapp/widgets/videooverlay_widget.dart'; import 'package:get/get.dart'; @@ -27,7 +27,7 @@ class VideoWidget extends StatefulWidget { } class _VideoWidgetState extends State { - final ApiService apiService = Get.find(); + final MediaController controller = Get.find(); late CachedVideoPlayerPlusController _controller; bool _showControls = false; Timer? _hideControlsTimer; @@ -52,7 +52,7 @@ class _VideoWidgetState extends State { } _controller.setLooping(true); - _controller.setVolume(apiService.muted.value ? 0.0 : 1.0); + _controller.setVolume(controller.muted.value ? 0.0 : 1.0); } @override @@ -88,7 +88,7 @@ class _VideoWidgetState extends State { @override Widget build(BuildContext context) { - final bool muted = apiService.muted.value; + final bool muted = controller.muted.value; if (_controller.value.isInitialized && _controller.value.volume != (muted ? 0.0 : 1.0)) { _controller.setVolume(muted ? 0.0 : 1.0); diff --git a/lib/widgets/videooverlay_widget.dart b/lib/widgets/videooverlay_widget.dart index 240c7db..99eed49 100644 --- a/lib/widgets/videooverlay_widget.dart +++ b/lib/widgets/videooverlay_widget.dart @@ -1,9 +1,10 @@ -import 'package:f0ckapp/services/api_service.dart'; import 'package:flutter/material.dart'; import 'package:cached_video_player_plus/cached_video_player_plus.dart'; import 'package:get/get.dart'; +import 'package:f0ckapp/controller/media_controller.dart'; + class VideoControlsOverlay extends StatelessWidget { final CachedVideoPlayerPlusController controller; final VoidCallback button; @@ -16,7 +17,7 @@ class VideoControlsOverlay extends StatelessWidget { @override Widget build(BuildContext context) { - final ApiService apiService = Get.find(); + final MediaController c = Get.find(); return Stack( alignment: Alignment.center, @@ -26,10 +27,10 @@ class VideoControlsOverlay extends StatelessWidget { bottom: 12, child: Obx( () => _ControlButton( - apiService.muted.value ? Icons.volume_off : Icons.volume_up, + c.muted.value ? Icons.volume_off : Icons.volume_up, () async { button(); - await apiService.toggleMuted(); + await c.toggleMuted(); }, size: 16, ), diff --git a/pubspec.lock b/pubspec.lock index 8131a21..380d18c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + asn1lib: + dependency: transitive + description: + name: asn1lib + sha256: "0511d6be23b007e95105ae023db599aea731df604608978dada7f9faf2637623" + url: "https://pub.dev" + source: hosted + version: "1.6.4" async: dependency: transitive description: @@ -73,6 +89,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" cross_file: dependency: transitive description: @@ -97,14 +121,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" - cupertino_icons: - dependency: "direct main" + devtools_shared: + dependency: transitive description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + name: devtools_shared + sha256: "659e2d65aa5ef5c3551163811c5c6fa1b973b3df80d8cac6f618035edcdc1096" url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "11.2.1" + dtd: + dependency: transitive + description: + name: dtd + sha256: "14a0360d898ded87c3d99591fc386b8a6ea5d432927bee709b22130cd25b993a" + url: "https://pub.dev" + source: hosted + version: "2.5.1" + encrypt: + dependency: transitive + description: + name: encrypt + sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" + url: "https://pub.dev" + source: hosted + version: "5.0.3" + encrypt_shared_preferences: + dependency: "direct main" + description: + name: encrypt_shared_preferences + sha256: ab8a957db7ae645c8b0341e8aee85c1cd046a5cb9a0529459ea417ebd6040ba2 + url: "https://pub.dev" + source: hosted + version: "0.9.9" + extension_discovery: + dependency: transitive + description: + name: extension_discovery + sha256: de1fce715ab013cdfb00befc3bdf0914bea5e409c3a567b7f8f144bc061611a7 + url: "https://pub.dev" + source: hosted + version: "2.1.0" fake_async: dependency: transitive description: @@ -158,54 +214,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" - flutter_secure_storage: - dependency: "direct main" - description: - name: flutter_secure_storage - sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" - url: "https://pub.dev" - source: hosted - version: "9.2.4" - flutter_secure_storage_linux: - dependency: transitive - description: - name: flutter_secure_storage_linux - sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 - url: "https://pub.dev" - source: hosted - version: "1.2.3" - flutter_secure_storage_macos: - dependency: transitive - description: - name: flutter_secure_storage_macos - sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" - url: "https://pub.dev" - source: hosted - version: "3.1.3" - flutter_secure_storage_platform_interface: - dependency: transitive - description: - name: flutter_secure_storage_platform_interface - sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 - url: "https://pub.dev" - source: hosted - version: "1.1.2" - flutter_secure_storage_web: - dependency: transitive - description: - name: flutter_secure_storage_web - sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - flutter_secure_storage_windows: - dependency: transitive - description: - name: flutter_secure_storage_windows - sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 - url: "https://pub.dev" - source: hosted - version: "3.1.2" flutter_test: dependency: "direct dev" description: flutter @@ -264,6 +272,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json_rpc_2: + dependency: transitive + description: + name: json_rpc_2 + sha256: "246b321532f0e8e2ba474b4d757eaa558ae4fdd0688fdbc1e1ca9705f9b8ca0e" + url: "https://pub.dev" + source: hosted + version: "3.0.3" leak_tracker: dependency: transitive description: @@ -296,6 +312,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -336,22 +360,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" - package_info_plus: - dependency: "direct main" - description: - name: package_info_plus - sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" - url: "https://pub.dev" - source: hosted - version: "8.3.0" - package_info_plus_platform_interface: - dependency: transitive - description: - name: package_info_plus_platform_interface - sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" - url: "https://pub.dev" - source: hosted - version: "3.2.0" path: dependency: transitive description: @@ -424,6 +432,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" rxdart: dependency: transitive description: @@ -448,6 +472,70 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" sky_engine: dependency: transitive description: flutter @@ -509,6 +597,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.0" + sse: + dependency: transitive + description: + name: sse + sha256: fcc97470240bb37377f298e2bd816f09fd7216c07928641c0560719f50603643 + url: "https://pub.dev" + source: hosted + version: "4.1.8" stack_trace: dependency: transitive description: @@ -565,6 +661,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + unified_analytics: + dependency: transitive + description: + name: unified_analytics + sha256: c8abdcad84b55b78f860358aae90077b8f54f98169a75e16d97796a1b3c95590 + url: "https://pub.dev" + source: hosted + version: "8.0.1" url_launcher_linux: dependency: transitive description: @@ -661,6 +765,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" win32: dependency: transitive description: @@ -677,6 +805,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: fb38626579fb345ad00e674e2af3a5c9b0cc4b9bfb8fd7f7ff322c7c9e62aef5 + url: "https://pub.dev" + source: hosted + version: "2.2.2" sdks: dart: ">=3.9.0-100.2.beta <4.0.0" flutter: ">=3.29.0" diff --git a/pubspec.yaml b/pubspec.yaml index ee66571..4c2f142 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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 # 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. -version: 1.2.1+55 +version: 1.3.0+56 environment: sdk: ^3.9.0-100.2.beta @@ -32,14 +32,10 @@ dependencies: sdk: flutter http: ^1.4.0 - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.8 cached_network_image: ^3.4.1 cached_video_player_plus: ^3.0.3 - package_info_plus: ^8.3.0 share_plus: ^11.0.0 - flutter_secure_storage: ^9.2.4 + encrypt_shared_preferences: ^0.9.9 get: ^4.7.2 dev_dependencies: @@ -67,6 +63,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - assets/images/ + - pubspec.yaml # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg