v1.3.3+59
All checks were successful
Flutter Schmutter / build (push) Successful in 3m47s

This commit is contained in:
2025-06-17 19:03:40 +02:00
parent 089fe1f8df
commit ee2db04a36
19 changed files with 667 additions and 136 deletions

View File

@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:f0ckapp/models/media_item.dart';
class ActionTag extends StatelessWidget {
final Tag tag;
final void Function(String tag) onTagTap;
const ActionTag(this.tag, this.onTagTap, {super.key});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => onTagTap(tag.tag),
child:
[
'german',
'dutch',
'ukraine',
'russia',
'belgium',
].contains(tag.tag)
? Stack(
alignment: Alignment.center,
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.white, width: 1),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
'assets/images/tags/${tag.tag}.webp',
height: 27,
width: 60,
repeat: ImageRepeat.repeat,
fit: BoxFit.fill,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 9,
vertical: 6,
),
child: Text(
tag.tag,
style: const TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
],
)
: Container(
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.white, width: 1),
color: switch (tag.id) {
1 => Colors.green,
2 => Colors.red,
_ => const Color(0xFF090909),
},
),
child: Text(
tag.tag,
style: const TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
);
}
}

View File

@ -0,0 +1,121 @@
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:get/get.dart';
import 'package:f0ckapp/controller/media_controller.dart';
import 'package:f0ckapp/service/media_service.dart';
import 'package:f0ckapp/controller/auth_controller.dart';
import 'package:f0ckapp/widgets/actiontag.dart';
import 'package:f0ckapp/widgets/favorite_avatars.dart';
import 'package:f0ckapp/widgets/video_widget.dart';
import 'package:f0ckapp/models/media_item.dart';
class DetailMediaContent extends StatelessWidget {
final int currentPage;
final int index;
final void Function(String tag) onTagTap;
const DetailMediaContent({
super.key,
required this.currentPage,
required this.index,
required this.onTagTap,
});
@override
Widget build(BuildContext context) {
final MediaService mediaService = Get.find();
final MediaController controller = Get.find();
final AuthController authController = Get.find();
return SafeArea(
top: false,
child: SingleChildScrollView(
child: Obx(() {
final MediaItem currentItem = controller.mediaItems[index];
final bool isFavorite =
currentItem.favorites?.any(
(f) => f.userId == authController.userId.value,
) ??
false;
return Column(
children: [
_buildMedia(currentItem, index == currentPage),
const SizedBox(height: 10, width: double.infinity),
_buildTags(currentItem),
if (currentItem.favorites != null &&
authController.token.value != null) ...[
const SizedBox(height: 20),
_buildFavoritesRow(context, currentItem, isFavorite, () async {
final List<Favorite>? newFavorites = await mediaService
.toggleFavorite(currentItem, isFavorite);
if (newFavorites != null) {
controller.mediaItems[index] = currentItem.copyWith(
favorites: newFavorites,
);
controller.mediaItems.refresh();
}
}),
],
const SizedBox(height: 20),
],
);
}),
),
);
}
Widget _buildMedia(MediaItem item, bool isActive) {
if (item.mime.startsWith('image')) {
return CachedNetworkImage(
imageUrl: item.mediaUrl,
fit: BoxFit.contain,
placeholder: (context, url) =>
const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
const Center(child: Icon(Icons.error)),
);
} else {
return VideoWidget(details: item, isActive: isActive);
}
}
Widget _buildTags(MediaItem item) {
return Wrap(
alignment: WrapAlignment.center,
spacing: 5.0,
children: item.tags
.map<Widget>((Tag tag) => ActionTag(tag, onTagTap))
.toList(),
);
}
Widget _buildFavoritesRow(
BuildContext context,
MediaItem item,
bool isFavorite,
VoidCallback onFavoritePressed,
) {
return Row(
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: FavoriteAvatars(
favorites: item.favorites ?? [],
brightness: Theme.of(context).brightness,
),
),
),
IconButton(
icon: isFavorite
? const Icon(Icons.favorite)
: const Icon(Icons.favorite_outline),
color: Colors.red,
onPressed: onFavoritePressed,
),
],
);
}
}

View File

@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:f0ckapp/screens/login_screen.dart';
import 'package:f0ckapp/controller/auth_controller.dart';
import 'package:f0ckapp/screens/settings_screen.dart';
import 'package:f0ckapp/controller/theme_controller.dart';
import 'package:f0ckapp/utils/appversion.dart';
@ -9,81 +11,93 @@ import 'package:f0ckapp/utils/appversion.dart';
class EndDrawer extends StatelessWidget {
const EndDrawer({super.key});
void _showMsg(String message, BuildContext context) {
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(message)));
void _showMsg(String message, {String title = ''}) {
Get
..closeAllSnackbars()
..snackbar(message, title, snackPosition: SnackPosition.BOTTOM);
}
@override
Widget build(BuildContext context) {
final ThemeController themeController = Get.find();
final AuthController authController = Get.find();
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/menu.webp'),
fit: BoxFit.cover,
alignment: Alignment.topCenter,
),
),
child: null,
),
/*ExpansionTile(
title: const Text('Login'),
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
readOnly: true,
controller: _usernameController,
decoration: const InputDecoration(
labelText: 'Benutzername',
),
),
const SizedBox(height: 10),
TextField(
readOnly: true,
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Passwort',
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("noch nicht implementiert lol"),
),
final success = await login(
_usernameController.text,
_passwordController.text,
);
if (success) {
Navigator.pop(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Login fehlgeschlagen!")),
);
}
);
},
child: const Text('Login'),
),
],
Obx(() {
if (authController.token.value != null &&
authController.avatarUrl.value != null) {
return DrawerHeader(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(authController.avatarUrl.value!),
fit: BoxFit.cover,
alignment: Alignment.topCenter,
),
),
],
),*/
child: null,
);
} else {
return DrawerHeader(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/menu.webp'),
fit: BoxFit.cover,
alignment: Alignment.topCenter,
),
),
child: null,
);
}
}),
Obx(() {
if (authController.token.value != null) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
if (authController.username.value != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
'Hamlo ${authController.username.value!}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
ElevatedButton(
onPressed: () async {
await authController.logout();
_showMsg('Erfolgreich ausgeloggt.');
},
child: const Text('Logout'),
),
],
),
);
} else {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Text(
'Du bist nicht eingeloggt.',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
Get.bottomSheet(LoginPage(), isDismissible: false);
},
child: const Text('Login'),
),
],
),
);
}
}),
ExpansionTile(
title: const Text('Theme'),
children: [
@ -121,7 +135,7 @@ class EndDrawer extends StatelessWidget {
title: Text('v${AppVersion.version}'),
onTap: () {
Navigator.pop(context);
_showMsg('jooong lass das, hier ist nichts', context);
_showMsg('jooong lass das, hier ist nichts');
},
),
],

View File

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
class FavoriteAvatars extends StatelessWidget {
final List favorites;
final Brightness brightness;
const FavoriteAvatars({
super.key,
required this.favorites,
required this.brightness,
});
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
...favorites.map((favorite) {
return Container(
height: 32,
width: 32,
margin: const EdgeInsets.only(right: 5.0),
decoration: BoxDecoration(
border: Border.all(
color: brightness == Brightness.dark
? Colors.white
: Colors.black,
width: 1.0,
),
),
child: CachedNetworkImage(
imageUrl: favorite.avatarUrl,
fit: BoxFit.cover,
),
);
}),
],
);
}
}

View File

@ -1,14 +1,14 @@
import 'dart:async';
import 'package:f0ckapp/controller/media_controller.dart';
import 'package:f0ckapp/models/media_item.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:cached_video_player_plus/cached_video_player_plus.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:f0ckapp/controller/media_controller.dart';
import 'package:f0ckapp/models/media_item.dart';
import 'package:f0ckapp/widgets/videooverlay_widget.dart';
import 'package:get/get.dart';
class VideoWidget extends StatefulWidget {
final MediaItem details;