Compare commits
5 Commits
v1.0.26+26
...
v1.1.0+30
Author | SHA1 | Date | |
---|---|---|---|
f1eb52518b | |||
c7d996a402 | |||
ee93ef576b | |||
78ff1953ad | |||
6fb4775043 |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
@ -4,12 +4,14 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:f0ckapp/providers/MediaProvider.dart';
|
import 'package:f0ckapp/providers/MediaProvider.dart';
|
||||||
import 'package:f0ckapp/providers/ThemeProvider.dart';
|
import 'package:f0ckapp/providers/ThemeProvider.dart';
|
||||||
import 'package:f0ckapp/screens/MediaGrid.dart';
|
import 'package:f0ckapp/screens/MediaGrid.dart';
|
||||||
|
import 'package:f0ckapp/utils/AppVersion.dart';
|
||||||
|
|
||||||
import 'package:provider/provider.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]);
|
||||||
|
await AppVersion.init();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
|
@ -34,6 +34,7 @@ class MediaItem {
|
|||||||
String get thumbnailUrl => 'https://f0ck.me/t/$id.webp';
|
String get thumbnailUrl => 'https://f0ck.me/t/$id.webp';
|
||||||
String get mediaUrl => 'https://f0ck.me/b/$dest';
|
String get mediaUrl => 'https://f0ck.me/b/$dest';
|
||||||
String get coverUrl => 'https://f0ck.me/ca/$id.webp';
|
String get coverUrl => 'https://f0ck.me/ca/$id.webp';
|
||||||
|
String get postUrl => 'https://f0ck.me/$id';
|
||||||
}
|
}
|
||||||
|
|
||||||
class Tag {
|
class Tag {
|
||||||
|
@ -8,7 +8,7 @@ class MediaProvider extends ChangeNotifier {
|
|||||||
bool _random = false;
|
bool _random = false;
|
||||||
String? _tag;
|
String? _tag;
|
||||||
int _crossAxisCount = 0;
|
int _crossAxisCount = 0;
|
||||||
final List<MediaItem> _mediaItems = [];
|
List<MediaItem> _mediaItems = [];
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
|
||||||
List<String> types = ["alles", "image", "video", "audio"];
|
List<String> types = ["alles", "image", "video", "audio"];
|
||||||
@ -22,25 +22,26 @@ class MediaProvider extends ChangeNotifier {
|
|||||||
int get crossAxisCount => _crossAxisCount;
|
int get crossAxisCount => _crossAxisCount;
|
||||||
List<MediaItem> get mediaItems => _mediaItems;
|
List<MediaItem> get mediaItems => _mediaItems;
|
||||||
bool get isLoading => _isLoading;
|
bool get isLoading => _isLoading;
|
||||||
|
Function get resetMedia => _resetMedia;
|
||||||
|
|
||||||
void setType(String type) {
|
void setType(String type) {
|
||||||
_typeid = types.indexOf(type);
|
_typeid = types.indexOf(type);
|
||||||
loadMedia(reload: true);
|
_resetMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMode(int mode) {
|
void setMode(int mode) {
|
||||||
_mode = mode;
|
_mode = mode;
|
||||||
loadMedia(reload: true);
|
_resetMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggleRandom() {
|
void toggleRandom() {
|
||||||
_random = !_random;
|
_random = !_random;
|
||||||
loadMedia(reload: true);
|
_resetMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTag(String? tag) {
|
void setTag(String? tag) {
|
||||||
_tag = tag;
|
_tag = tag;
|
||||||
loadMedia(reload: true);
|
_resetMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCrossAxisCount(int crossAxisCount) {
|
void setCrossAxisCount(int crossAxisCount) {
|
||||||
@ -49,40 +50,46 @@ class MediaProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setMediaItems(List<MediaItem> mediaItems) {
|
void setMediaItems(List<MediaItem> mediaItems) {
|
||||||
|
if (_mediaItems != mediaItems) {
|
||||||
_mediaItems.clear();
|
_mediaItems.clear();
|
||||||
addMediaItems(mediaItems);
|
_mediaItems.addAll(mediaItems);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void addMediaItems(List<MediaItem> newItems) {
|
void addMediaItems(List<MediaItem> newItems) {
|
||||||
_mediaItems.addAll(newItems);
|
_mediaItems.addAll(newItems);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadMedia({bool reload = false}) async {
|
void _resetMedia() {
|
||||||
|
_mediaItems.clear();
|
||||||
|
notifyListeners();
|
||||||
|
loadMedia();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadMedia({bool notify = true}) async {
|
||||||
if (_isLoading) return;
|
if (_isLoading) return;
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
notifyListeners();
|
if (notify) notifyListeners();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final newMedia = await fetchMedia(
|
final newMedia = await fetchMedia(
|
||||||
older: reload
|
older: _mediaItems.isNotEmpty ? _mediaItems.last.id : null,
|
||||||
? null
|
|
||||||
: _mediaItems.isNotEmpty
|
|
||||||
? _mediaItems.last.id
|
|
||||||
: null,
|
|
||||||
type: type,
|
type: type,
|
||||||
mode: mode,
|
mode: mode,
|
||||||
random: random,
|
random: random,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
);
|
);
|
||||||
|
|
||||||
reload ? setMediaItems(newMedia) : addMediaItems(newMedia);
|
if(_mediaItems != newMedia) {
|
||||||
|
addMediaItems(newMedia);
|
||||||
|
if (notify) notifyListeners();
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Fehler beim Laden der Medien: $e');
|
debugPrint('Fehler beim Laden der Medien: $e');
|
||||||
} finally {
|
} finally {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
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:provider/provider.dart';
|
||||||
@ -8,6 +10,7 @@ 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:f0ckapp/providers/MediaProvider.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
class DetailView extends StatefulWidget {
|
class DetailView extends StatefulWidget {
|
||||||
final int initialItemId;
|
final int initialItemId;
|
||||||
@ -21,6 +24,7 @@ class DetailView extends StatefulWidget {
|
|||||||
class _DetailViewState extends State<DetailView> {
|
class _DetailViewState extends State<DetailView> {
|
||||||
late PageController _pageController;
|
late PageController _pageController;
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
|
int _currentIndex = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -30,8 +34,14 @@ class _DetailViewState extends State<DetailView> {
|
|||||||
final initialIndex = provider.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);
|
||||||
|
|
||||||
|
_currentIndex = initialIndex;
|
||||||
|
_pageController.addListener(() {
|
||||||
|
setState(() => _currentIndex = _pageController.page?.round() ?? 0);
|
||||||
|
});
|
||||||
|
|
||||||
_preloadAdjacentMedia(initialIndex);
|
_preloadAdjacentMedia(initialIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,17 +100,71 @@ class _DetailViewState extends State<DetailView> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: Text('f0ck #${widget.initialItemId} (${provider.type})'),
|
title: Text(
|
||||||
|
'f0ck #${provider.mediaItems.elementAt(_currentIndex).id.toString()}',
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
PopupMenuButton<String>(
|
||||||
|
onSelected: (value) {
|
||||||
|
final item = provider.mediaItems.elementAt(_currentIndex);
|
||||||
|
switch (value) {
|
||||||
|
case 'media':
|
||||||
|
final params = ShareParams(
|
||||||
|
files: [
|
||||||
|
XFile.fromData(
|
||||||
|
utf8.encode(item.mediaUrl),
|
||||||
|
mimeType: item.mime,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
SharePlus.instance.share(params);
|
||||||
|
break;
|
||||||
|
case 'direct_link':
|
||||||
|
SharePlus.instance.share(ShareParams(text: item.mediaUrl));
|
||||||
|
break;
|
||||||
|
case 'post_link':
|
||||||
|
SharePlus.instance.share(ShareParams(text: item.postUrl));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 'media',
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(Icons.image),
|
||||||
|
title: Text('Als Datei'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 'direct_link',
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(Icons.link),
|
||||||
|
title: Text('Link zum Bild'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 'post_link',
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(Icons.article),
|
||||||
|
title: Text('Link zum Post'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
icon: Icon(Icons.share),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
PageTransformer(
|
PageTransformer(
|
||||||
controller: _pageController,
|
controller: _pageController,
|
||||||
pages: provider.mediaItems.map((item) {
|
pages: provider.mediaItems.map((item) {
|
||||||
|
int itemIndex = provider.mediaItems.indexOf(item);
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: SmartRefreshIndicator(
|
child: SmartRefreshIndicator(
|
||||||
onRefresh: _loadMoreMedia,
|
onRefresh: _loadMoreMedia,
|
||||||
child: _buildMediaItem(item),
|
child: _buildMediaItem(item, _currentIndex == itemIndex),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
@ -109,10 +173,6 @@ class _DetailViewState extends State<DetailView> {
|
|||||||
),
|
),
|
||||||
persistentFooterButtons: provider.tag != null
|
persistentFooterButtons: provider.tag != null
|
||||||
? [
|
? [
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text('tag: '),
|
|
||||||
InputChip(
|
InputChip(
|
||||||
label: Text(provider.tag!),
|
label: Text(provider.tag!),
|
||||||
backgroundColor: const Color(0xFF090909),
|
backgroundColor: const Color(0xFF090909),
|
||||||
@ -122,14 +182,12 @@ class _DetailViewState extends State<DetailView> {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMediaItem(MediaItem item) {
|
Widget _buildMediaItem(MediaItem item, bool isActive) {
|
||||||
final provider = Provider.of<MediaProvider>(context);
|
final provider = Provider.of<MediaProvider>(context);
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
@ -143,12 +201,12 @@ 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: true),
|
VideoWidget(details: item, isActive: isActive),
|
||||||
const SizedBox(height: 20),
|
/*const SizedBox(height: 20),
|
||||||
Text(
|
Text(
|
||||||
item.mime,
|
'f0ck #${item.id.toString()}',
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 18),
|
style: const TextStyle(color: Colors.white, fontSize: 18),
|
||||||
),
|
),*/
|
||||||
const SizedBox(height: 10, width: double.infinity),
|
const SizedBox(height: 10, width: double.infinity),
|
||||||
Wrap(
|
Wrap(
|
||||||
alignment: WrapAlignment.center,
|
alignment: WrapAlignment.center,
|
||||||
@ -159,7 +217,7 @@ class _DetailViewState extends State<DetailView> {
|
|||||||
if (tag.tag == 'sfw' || tag.tag == 'nsfw') return;
|
if (tag.tag == 'sfw' || tag.tag == 'nsfw') return;
|
||||||
setState(() {
|
setState(() {
|
||||||
provider.setTag(tag.tag);
|
provider.setTag(tag.tag);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context, true);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
label: Text(tag.tag),
|
label: Text(tag.tag),
|
||||||
|
@ -2,8 +2,8 @@ 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:provider/provider.dart';
|
||||||
import 'package:f0ckapp/screens/DetailView.dart';
|
import 'package:f0ckapp/screens/DetailView.dart';
|
||||||
import 'package:f0ckapp/services/Api.dart';
|
|
||||||
import 'package:f0ckapp/providers/MediaProvider.dart';
|
import 'package:f0ckapp/providers/MediaProvider.dart';
|
||||||
|
import 'package:f0ckapp/utils/AppVersion.dart';
|
||||||
|
|
||||||
class MediaGrid extends StatefulWidget {
|
class MediaGrid extends StatefulWidget {
|
||||||
const MediaGrid({super.key});
|
const MediaGrid({super.key});
|
||||||
@ -27,12 +27,18 @@ class _MediaGridState extends State<MediaGrid> {
|
|||||||
|
|
||||||
_scrollController.addListener(() {
|
_scrollController.addListener(() {
|
||||||
if (_scrollController.position.pixels >=
|
if (_scrollController.position.pixels >=
|
||||||
_scrollController.position.maxScrollExtent - 100) {
|
_scrollController.position.maxScrollExtent - 200) {
|
||||||
provider.loadMedia();
|
provider.loadMedia(notify: false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _calculateCrossAxisCount(BuildContext context, int defaultCount) {
|
||||||
|
return defaultCount == 0
|
||||||
|
? (MediaQuery.of(context).size.width / 110).clamp(3, 5).toInt()
|
||||||
|
: defaultCount;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final provider = Provider.of<MediaProvider>(context);
|
final provider = Provider.of<MediaProvider>(context);
|
||||||
@ -42,20 +48,15 @@ class _MediaGridState extends State<MediaGrid> {
|
|||||||
key: scaffoldKey,
|
key: scaffoldKey,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
//centerTitle: true,
|
//centerTitle: true,
|
||||||
title: Text('f0ck v1.0.26+26'),
|
title: Text('fApp v${AppVersion.version}'),
|
||||||
actions: [
|
actions: [
|
||||||
DropdownButton<String>(
|
IconButton(
|
||||||
// mode
|
icon: Icon(
|
||||||
value: provider.modes[provider.mode],
|
provider.random ? Icons.shuffle_on_outlined : Icons.shuffle,
|
||||||
isDense: true,
|
),
|
||||||
icon: SizedBox.shrink(),
|
onPressed: () {
|
||||||
items: provider.modes.map((String value) {
|
provider.toggleRandom();
|
||||||
return DropdownMenuItem<String>(value: value, child: Text(value));
|
_scrollController.jumpTo(0);
|
||||||
}).toList(),
|
|
||||||
onChanged: (String? newValue) {
|
|
||||||
if (newValue != null) {
|
|
||||||
provider.setMode(provider.modes.indexOf(newValue));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
@ -66,68 +67,72 @@ class _MediaGridState extends State<MediaGrid> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
bottomNavigationBar: BottomAppBar(
|
||||||
|
height: 50,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
Text('type: '),
|
||||||
|
DropdownButton<String>(
|
||||||
|
// type
|
||||||
|
value: provider.type,
|
||||||
|
isDense: true,
|
||||||
|
//icon: SizedBox.shrink(),
|
||||||
|
items: provider.types.map((String value) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: value,
|
||||||
|
child: Text(value, style: TextStyle(color: Colors.white)),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
onChanged: (String? newValue) {
|
||||||
|
if (newValue != null) {
|
||||||
|
provider.setType(newValue);
|
||||||
|
_scrollController.jumpTo(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Text('mode: '),
|
||||||
|
DropdownButton<String>(
|
||||||
|
// 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));
|
||||||
|
_scrollController.jumpTo(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
endDrawer: Drawer(
|
endDrawer: Drawer(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
children: [
|
children: [
|
||||||
DrawerHeader(
|
DrawerHeader(
|
||||||
padding: EdgeInsets.all(0),
|
decoration: BoxDecoration(
|
||||||
child: Image.asset('assets/images/menu.webp', fit: BoxFit.cover),
|
image: DecorationImage(
|
||||||
|
image: AssetImage('assets/images/menu.webp'),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: null,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text('v${AppVersion.version}'),
|
||||||
'All',
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: provider.type == 'alles'
|
|
||||||
? FontWeight.bold
|
|
||||||
: FontWeight.normal,
|
|
||||||
color: provider.type == 'alles' ? Colors.blue : Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
provider.setType('all');
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
},
|
SnackBar(content: Text('jooong lass das, hier ist nichts')),
|
||||||
),
|
);
|
||||||
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');
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -135,36 +140,32 @@ class _MediaGridState extends State<MediaGrid> {
|
|||||||
),
|
),
|
||||||
persistentFooterButtons: provider.tag != null
|
persistentFooterButtons: provider.tag != null
|
||||||
? [
|
? [
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text('tag: '),
|
|
||||||
InputChip(
|
InputChip(
|
||||||
label: Text(provider.tag!),
|
label: Text(provider.tag!),
|
||||||
backgroundColor: const Color(0xFF090909),
|
backgroundColor: const Color(0xFF090909),
|
||||||
labelStyle: const TextStyle(color: Colors.white),
|
labelStyle: const TextStyle(color: Colors.white),
|
||||||
onDeleted: () {
|
onDeleted: () {
|
||||||
provider.setTag(null);
|
provider.setTag(null);
|
||||||
|
_scrollController.jumpTo(0);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
: null,
|
: null,
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await provider.loadMedia(reload: true);
|
await provider.resetMedia();
|
||||||
|
_scrollController.jumpTo(0);
|
||||||
},
|
},
|
||||||
child: Consumer<MediaProvider>(
|
child: Consumer<MediaProvider>(
|
||||||
builder: (context, mediaProvider, child) {
|
builder: (context, mediaProvider, child) {
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
|
key: PageStorageKey('mediaGrid'),
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: mediaProvider.crossAxisCount == 0
|
crossAxisCount: _calculateCrossAxisCount(
|
||||||
? (MediaQuery.of(context).size.width / 110)
|
context,
|
||||||
.clamp(3, 5)
|
provider.crossAxisCount,
|
||||||
.toInt()
|
),
|
||||||
: mediaProvider.crossAxisCount,
|
|
||||||
crossAxisSpacing: 5.0,
|
crossAxisSpacing: 5.0,
|
||||||
mainAxisSpacing: 5.0,
|
mainAxisSpacing: 5.0,
|
||||||
),
|
),
|
||||||
@ -177,12 +178,18 @@ class _MediaGridState extends State<MediaGrid> {
|
|||||||
final item = provider.mediaItems[index];
|
final item = provider.mediaItems[index];
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => Navigator.push(
|
onTap: () async {
|
||||||
|
bool? ret = await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => DetailView(initialItemId: item.id),
|
builder: (context) =>
|
||||||
),
|
DetailView(initialItemId: item.id),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
if (ret != null && ret) {
|
||||||
|
_scrollController.jumpTo(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: <Widget>[
|
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}';
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
@ -5,12 +5,16 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
import share_plus
|
||||||
import sqflite_darwin
|
import sqflite_darwin
|
||||||
import video_player_avfoundation
|
import video_player_avfoundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
||||||
}
|
}
|
||||||
|
88
pubspec.lock
88
pubspec.lock
@ -73,6 +73,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.19.1"
|
version: "1.19.1"
|
||||||
|
cross_file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cross_file
|
||||||
|
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.4+2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -256,6 +264,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.16.0"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -272,6 +288,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
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:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -360,6 +392,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.28.0"
|
version: "0.28.0"
|
||||||
|
share_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: share_plus
|
||||||
|
sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.0"
|
||||||
|
share_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_plus_platform_interface
|
||||||
|
sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -477,6 +525,38 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.1"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.4"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -541,6 +621,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.13.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
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.26+26
|
version: 1.1.0+30
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.9.0-100.2.beta
|
sdk: ^3.9.0-100.2.beta
|
||||||
@ -38,6 +38,8 @@ dependencies:
|
|||||||
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
|
provider: ^6.1.5
|
||||||
|
package_info_plus: ^8.3.0
|
||||||
|
share_plus: ^11.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
share_plus
|
||||||
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
Reference in New Issue
Block a user