Compare commits
3 Commits
v1.0.27+27
...
v1.0.29+29
Author | SHA1 | Date | |
---|---|---|---|
c7d996a402 | |||
ee93ef576b | |||
78ff1953ad |
@ -4,12 +4,14 @@ 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/utils/AppVersion.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
await AppVersion.init();
|
||||
|
||||
runApp(
|
||||
MultiProvider(
|
||||
|
@ -22,25 +22,26 @@ class MediaProvider extends ChangeNotifier {
|
||||
int get crossAxisCount => _crossAxisCount;
|
||||
List<MediaItem> get mediaItems => _mediaItems;
|
||||
bool get isLoading => _isLoading;
|
||||
Function get resetMedia => _resetMedia;
|
||||
|
||||
void setType(String type) {
|
||||
_typeid = types.indexOf(type);
|
||||
loadMedia(reload: true);
|
||||
_resetMedia();
|
||||
}
|
||||
|
||||
void setMode(int mode) {
|
||||
_mode = mode;
|
||||
loadMedia(reload: true);
|
||||
_resetMedia();
|
||||
}
|
||||
|
||||
void toggleRandom() {
|
||||
_random = !_random;
|
||||
loadMedia(reload: true);
|
||||
_resetMedia();
|
||||
}
|
||||
|
||||
void setTag(String? tag) {
|
||||
_tag = tag;
|
||||
loadMedia(reload: true);
|
||||
_resetMedia();
|
||||
}
|
||||
|
||||
void setCrossAxisCount(int crossAxisCount) {
|
||||
@ -49,8 +50,11 @@ class MediaProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void setMediaItems(List<MediaItem> mediaItems) {
|
||||
_mediaItems = mediaItems;
|
||||
notifyListeners();
|
||||
if (_mediaItems != mediaItems) {
|
||||
_mediaItems.clear();
|
||||
_mediaItems.addAll(mediaItems);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void addMediaItems(List<MediaItem> newItems) {
|
||||
@ -58,30 +62,32 @@ class MediaProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadMedia({bool reload = false}) async {
|
||||
void _resetMedia() {
|
||||
_mediaItems.clear();
|
||||
notifyListeners();
|
||||
loadMedia();
|
||||
}
|
||||
|
||||
Future<void> loadMedia({bool notify = true}) async {
|
||||
if (_isLoading) return;
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
if (notify) notifyListeners();
|
||||
|
||||
try {
|
||||
final newMedia = await fetchMedia(
|
||||
older: reload
|
||||
? null
|
||||
: _mediaItems.isNotEmpty
|
||||
? _mediaItems.last.id
|
||||
: null,
|
||||
older: _mediaItems.isNotEmpty ? _mediaItems.last.id : null,
|
||||
type: type,
|
||||
mode: mode,
|
||||
random: random,
|
||||
tag: tag,
|
||||
);
|
||||
|
||||
reload ? setMediaItems(newMedia) : addMediaItems(newMedia);
|
||||
addMediaItems(newMedia);
|
||||
} catch (e) {
|
||||
debugPrint('Fehler beim Laden der Medien: $e');
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
if (notify) notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ class DetailView extends StatefulWidget {
|
||||
class _DetailViewState extends State<DetailView> {
|
||||
late PageController _pageController;
|
||||
bool isLoading = false;
|
||||
int _currentIndex = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -30,8 +31,14 @@ class _DetailViewState extends State<DetailView> {
|
||||
final initialIndex = provider.mediaItems.indexWhere(
|
||||
(item) => item.id == widget.initialItemId,
|
||||
);
|
||||
|
||||
_pageController = PageController(initialPage: initialIndex);
|
||||
|
||||
_currentIndex = initialIndex;
|
||||
_pageController.addListener(() {
|
||||
setState(() => _currentIndex = _pageController.page?.round() ?? 0);
|
||||
});
|
||||
|
||||
_preloadAdjacentMedia(initialIndex);
|
||||
}
|
||||
|
||||
@ -88,19 +95,18 @@ class _DetailViewState extends State<DetailView> {
|
||||
final provider = Provider.of<MediaProvider>(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text('f0ck #${widget.initialItemId} (${provider.type})'),
|
||||
),
|
||||
appBar: AppBar(centerTitle: true, title: const Text('f0ck')),
|
||||
body: Stack(
|
||||
children: [
|
||||
PageTransformer(
|
||||
controller: _pageController,
|
||||
pages: provider.mediaItems.map((item) {
|
||||
int itemIndex = provider.mediaItems.indexOf(item);
|
||||
|
||||
return SafeArea(
|
||||
child: SmartRefreshIndicator(
|
||||
onRefresh: _loadMoreMedia,
|
||||
child: _buildMediaItem(item),
|
||||
child: _buildMediaItem(item, _currentIndex == itemIndex),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
@ -129,7 +135,7 @@ class _DetailViewState extends State<DetailView> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMediaItem(MediaItem item) {
|
||||
Widget _buildMediaItem(MediaItem item, bool isActive) {
|
||||
final provider = Provider.of<MediaProvider>(context);
|
||||
|
||||
return SingleChildScrollView(
|
||||
@ -143,10 +149,10 @@ class _DetailViewState extends State<DetailView> {
|
||||
errorWidget: (context, url, error) => Icon(Icons.error),
|
||||
)
|
||||
else
|
||||
VideoWidget(details: item, isActive: true),
|
||||
VideoWidget(details: item, isActive: isActive),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
item.mime,
|
||||
'f0ck #${item.id.toString()}',
|
||||
style: const TextStyle(color: Colors.white, fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 10, width: double.infinity),
|
||||
@ -159,7 +165,7 @@ class _DetailViewState extends State<DetailView> {
|
||||
if (tag.tag == 'sfw' || tag.tag == 'nsfw') return;
|
||||
setState(() {
|
||||
provider.setTag(tag.tag);
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context, true);
|
||||
});
|
||||
},
|
||||
label: Text(tag.tag),
|
||||
|
@ -3,6 +3,7 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:f0ckapp/screens/DetailView.dart';
|
||||
import 'package:f0ckapp/providers/MediaProvider.dart';
|
||||
import 'package:f0ckapp/utils/AppVersion.dart';
|
||||
|
||||
class MediaGrid extends StatefulWidget {
|
||||
const MediaGrid({super.key});
|
||||
@ -26,12 +27,16 @@ class _MediaGridState extends State<MediaGrid> {
|
||||
|
||||
_scrollController.addListener(() {
|
||||
if (_scrollController.position.pixels >=
|
||||
_scrollController.position.maxScrollExtent - 100) {
|
||||
provider.loadMedia();
|
||||
_scrollController.position.maxScrollExtent - 200) {
|
||||
provider.loadMedia(notify: false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void scrollToTop() {
|
||||
_scrollController.jumpTo(0);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final provider = Provider.of<MediaProvider>(context);
|
||||
@ -41,8 +46,17 @@ class _MediaGridState extends State<MediaGrid> {
|
||||
key: scaffoldKey,
|
||||
appBar: AppBar(
|
||||
//centerTitle: true,
|
||||
title: Text('f0ck v1.0.27+27'),
|
||||
title: Text('fApp v${AppVersion.version}'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
provider.random ? Icons.shuffle_on_outlined : Icons.shuffle,
|
||||
),
|
||||
onPressed: () {
|
||||
provider.toggleRandom();
|
||||
_scrollController.jumpTo(0);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.menu),
|
||||
onPressed: () {
|
||||
@ -71,6 +85,7 @@ class _MediaGridState extends State<MediaGrid> {
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
provider.setType(newValue);
|
||||
_scrollController.jumpTo(0);
|
||||
}
|
||||
},
|
||||
),
|
||||
@ -89,6 +104,7 @@ class _MediaGridState extends State<MediaGrid> {
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
provider.setMode(provider.modes.indexOf(newValue));
|
||||
_scrollController.jumpTo(0);
|
||||
}
|
||||
},
|
||||
),
|
||||
@ -103,62 +119,6 @@ class _MediaGridState extends State<MediaGrid> {
|
||||
padding: EdgeInsets.all(0),
|
||||
child: Image.asset('assets/images/menu.webp', fit: BoxFit.cover),
|
||||
),
|
||||
/*ListTile(
|
||||
title: Text(
|
||||
'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');
|
||||
},
|
||||
),*/
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -174,6 +134,7 @@ class _MediaGridState extends State<MediaGrid> {
|
||||
labelStyle: const TextStyle(color: Colors.white),
|
||||
onDeleted: () {
|
||||
provider.setTag(null);
|
||||
_scrollController.jumpTo(0);
|
||||
},
|
||||
),
|
||||
],
|
||||
@ -182,11 +143,13 @@ class _MediaGridState extends State<MediaGrid> {
|
||||
: null,
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await provider.loadMedia(reload: true);
|
||||
await provider.resetMedia();
|
||||
_scrollController.jumpTo(0);
|
||||
},
|
||||
child: Consumer<MediaProvider>(
|
||||
builder: (context, mediaProvider, child) {
|
||||
return GridView.builder(
|
||||
key: PageStorageKey('mediaGrid'),
|
||||
controller: _scrollController,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: mediaProvider.crossAxisCount == 0
|
||||
@ -206,12 +169,17 @@ class _MediaGridState extends State<MediaGrid> {
|
||||
final item = provider.mediaItems[index];
|
||||
|
||||
return InkWell(
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DetailView(initialItemId: item.id),
|
||||
),
|
||||
),
|
||||
onTap: () async {
|
||||
bool test = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DetailView(initialItemId: item.id),
|
||||
),
|
||||
);
|
||||
if (test) {
|
||||
scrollToTop();
|
||||
}
|
||||
},
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
|
10
lib/utils/AppVersion.dart
Normal file
10
lib/utils/AppVersion.dart
Normal file
@ -0,0 +1,10 @@
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
class AppVersion {
|
||||
static String version = "";
|
||||
|
||||
static Future<void> init() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
version = '${packageInfo.version}+${packageInfo.buildNumber}';
|
||||
}
|
||||
}
|
@ -5,11 +5,13 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import sqflite_darwin
|
||||
import video_player_avfoundation
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
||||
|
24
pubspec.lock
24
pubspec.lock
@ -272,6 +272,22 @@ 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:
|
||||
@ -541,6 +557,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.13.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
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
|
||||
# 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.0.27+27
|
||||
version: 1.0.29+29
|
||||
|
||||
environment:
|
||||
sdk: ^3.9.0-100.2.beta
|
||||
@ -38,6 +38,7 @@ dependencies:
|
||||
cached_network_image: ^3.4.1
|
||||
cached_video_player_plus: ^3.0.3
|
||||
provider: ^6.1.5
|
||||
package_info_plus: ^8.3.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user