Compare commits

...

5 Commits

Author SHA1 Message Date
e945844151 v1.1.10+40
All checks were successful
Flutter Schmutter / build (push) Successful in 3m56s
- download button lel
2025-06-09 19:08:23 +02:00
74eb6e3d26 readme & license 2025-06-09 15:42:13 +02:00
9755066d1e full retard renaming 2025-06-09 15:04:03 +02:00
671b3cfbe0 v1.1.9+39
All checks were successful
Flutter Schmutter / build (push) Successful in 3m35s
2025-06-09 14:02:59 +02:00
93fb3536ee v1.1.8+38
All checks were successful
Flutter Schmutter / build (push) Successful in 3m48s
- blah
2025-06-08 19:40:06 +02:00
18 changed files with 320 additions and 233 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 f0ck
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,16 +1,48 @@
# f0ckapp
# fApp
![f0ck.me Logo](https://git.lat/f0ck/fApp/raw/branch/master/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png)
## Overview
A new Flutter project.
fApp is the mobile application for the website [f0ck.me](https://f0ck.me). This app provides a user-friendly interface to access the content of the website and utilize its features conveniently from your mobile device.
## Getting Started
## Installation
This project is a starting point for a Flutter application.
fApp is available in its own F-Droid repository.
A few resources to get you started if this is your first Flutter project:
### Installation Steps
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
1. Add the F-Droid repository to your F-Droid app:
- Go to the settings in the F-Droid app.
- Select "Repositories" and add the URL `https://fdroid.flumm.io/fdroid/repo/`.
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
2. Search for "f0ckapp" in the F-Droid app and install the app.
## Development
### Prerequisites
- Flutter SDK
- Dart SDK
- Android Studio or Visual Studio Code
### Setting Up the Project
1. Clone the repository:
```bash
git clone https://git.lat/f0ck/fApp.git
cd fApp
2. Install dependencies:
```flutter pub get```
3. Run the app:
```flutter run```
### Contributing
don't.
### License
This project is licensed under the MIT License. See the LICENSE file for more information.
### Contact
For questions or feedback, you can reach us at [contact@f0ck.me](mailto:contact@f0ck.me).

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:label="f0ckapp"
@ -29,14 +30,14 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="flutter_deeplinking_enabled" android:value="false"/>
<meta-data android:name="flutter_deeplinking_enabled" android:value="true"/>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" android:host="f0ck.me"/>
</intent-filter>
<intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>

View File

@ -1,36 +1,89 @@
import 'package:app_links/app_links.dart';
import 'package:f0ckapp/providers/media_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:f0ckapp/screens/MediaGrid.dart';
import 'package:f0ckapp/utils/AppVersion.dart';
import 'package:f0ckapp/providers/ThemeProvider.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:f0ckapp/screens/mediagrid_screen.dart';
import 'package:f0ckapp/screens/detailview_screen.dart';
import 'package:f0ckapp/utils/appversion_util.dart';
import 'package:f0ckapp/providers/theme_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
await FlutterDownloader.initialize();
await AppVersion.init();
final Uri? initialUri = await AppLinks().getInitialLink();
runApp(ProviderScope(child: F0ckApp(initialUri: initialUri)));
runApp(ProviderScope(child: F0ckApp()));
}
class F0ckApp extends ConsumerWidget {
final Uri? initialUri;
const F0ckApp({super.key, this.initialUri});
F0ckApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) {
return const MediaGrid();
},
),
GoRoute(
path: '/:rest(.*)',
builder: (context, state) {
final isInternalLink = (state.extra is bool && state.extra == true);
final fullPath = state.matchedLocation;
final regExp = RegExp(
r'^(?:/tag/(?<tag>.+?))?(?:/(?<mime>image|audio|video))?(?:/(?<itemid>\d+))?$',
);
final match = regExp.firstMatch(fullPath);
if (match == null) {
return const Scaffold(body: Center(child: Text('Ungültiger Link')));
}
final String? tag = match.namedGroup('tag');
final String? mime = match.namedGroup('mime');
final String? idStr = match.namedGroup('itemid');
final int? itemId = idStr != null ? int.tryParse(idStr) : null;
const preloadOffset = 50;
return Consumer(
builder: (context, ref, child) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
final mediaNotifier = ref.read(mediaProvider.notifier);
if (!isInternalLink) {
mediaNotifier.setType(mime ?? "alles");
mediaNotifier.setTag(tag);
}
if (itemId != null) {
await mediaNotifier.loadMedia(id: itemId + preloadOffset);
}
});
if (itemId != null) {
return DetailView(initialItemId: itemId);
} else {
return MediaGrid();
}
},
);
},
),
],
);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Consumer(
builder: (context, ref, _) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ref.watch(themeNotifierProvider),
home: MediaGrid(initialUri: initialUri),
);
},
final theme = ref.watch(themeNotifierProvider);
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: _router,
theme: theme,
);
}
}

View File

@ -1,8 +1,8 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:f0ckapp/models/MediaItem.dart';
import 'package:f0ckapp/services/Api.dart';
import 'package:f0ckapp/models/mediaitem_model.dart';
import 'package:f0ckapp/services/api_service.dart';
const List<String> mediaTypes = ["alles", "image", "video", "audio"];
const List<String> mediaModes = ["sfw", "nsfw", "untagged", "all"];
@ -107,11 +107,7 @@ class MediaNotifier extends StateNotifier<MediaState> {
}
Future<void> loadMedia({int? id}) async {
//if (state.isLoading) return;
if (id != null) {
print('requested id: ${id.toString()}');
}
if (state.isLoading) return;
state = state.replace(isLoading: true);
try {
final older =

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

View File

@ -2,17 +2,20 @@ import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:go_router/go_router.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
import 'package:f0ckapp/models/MediaItem.dart';
import 'package:f0ckapp/widgets/VideoWidget.dart';
import 'package:f0ckapp/utils/SmartRefreshIndicator.dart';
import 'package:f0ckapp/utils/PageTransformer.dart';
import 'package:f0ckapp/providers/MediaProvider.dart';
import 'package:f0ckapp/models/mediaitem_model.dart';
import 'package:f0ckapp/widgets/video_widget.dart';
import 'package:f0ckapp/utils/smartrefreshindicator_util.dart';
import 'package:f0ckapp/utils/pagetransformer_util.dart';
import 'package:f0ckapp/providers/media_provider.dart';
class DetailView extends ConsumerStatefulWidget {
final int initialItemId;
@ -24,26 +27,13 @@ class DetailView extends ConsumerStatefulWidget {
}
class _DetailViewState extends ConsumerState<DetailView> {
late PageController _pageController;
PageController? _pageController;
bool isLoading = false;
int _currentIndex = 0;
@override
void initState() {
super.initState();
final mediaState = ref.read(mediaProvider);
final initialIndex = mediaState.mediaItems.indexWhere(
(item) => item.id == widget.initialItemId,
);
_pageController = PageController(initialPage: initialIndex);
_currentIndex = initialIndex;
_pageController.addListener(() {
setState(() => _currentIndex = _pageController.page?.round() ?? 0);
});
_preloadAdjacentMedia(initialIndex);
}
void _preloadAdjacentMedia(int index) async {
@ -79,40 +69,111 @@ class _DetailViewState extends ConsumerState<DetailView> {
..showSnackBar(SnackBar(content: Text(message)));
}
Future<void> _downloadMedia() async {
final mediaState = ref.read(mediaProvider);
final currentItem = mediaState.mediaItems[_currentIndex];
if (Platform.isAndroid || Platform.isIOS) {
var status = await Permission.storage.status;
if (!status.isGranted) {
status = await Permission.storage.request();
if (!status.isGranted) {
_showError("Speicherberechtigung wurde nicht erteilt.");
return;
}
}
}
String localPath;
if (Platform.isAndroid) {
final directory = await getExternalStorageDirectory();
localPath = "${directory!.path}/Download/fApp";
} else if (Platform.isIOS) {
final directory = await getApplicationDocumentsDirectory();
localPath = directory.path;
} else {
final directory = await getTemporaryDirectory();
localPath = directory.path;
}
final savedDir = Directory(localPath);
if (!await savedDir.exists()) {
await savedDir.create(recursive: true);
}
try {
await FlutterDownloader.enqueue(
url: currentItem.mediaUrl,
savedDir: localPath,
fileName: currentItem.mediaUrl.split('/').last,
showNotification: true,
openFileFromNotification: true,
);
if (mounted) {
_showError('Download gestartet: ${currentItem.mediaUrl}');
}
} catch (e) {
_showError('Download fehlgeschlagen: $e');
}
}
@override
void dispose() {
_pageController.dispose();
_pageController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final mediaState = ref.watch(mediaProvider);
final mediaNotifier = ref.read(mediaProvider.notifier);
final int itemIndex = mediaState.mediaItems.indexWhere(
(item) => item.id == widget.initialItemId,
);
print('itemIndex: ${itemIndex}; initial: ${widget.initialItemId}');
if (mediaState.mediaItems.isEmpty) {
if (itemIndex == -1) {
Future.microtask(() {
ref
.read(mediaProvider.notifier)
.loadMedia(id: widget.initialItemId + 50);
});
return Scaffold(
appBar: AppBar(),
body: const Center(child: CircularProgressIndicator()),
);
}
if (_pageController == null) {
_pageController = PageController(initialPage: itemIndex);
_currentIndex = itemIndex;
_pageController!.addListener(() {
setState(() => _currentIndex = _pageController!.page?.round() ?? 0);
});
_preloadAdjacentMedia(itemIndex);
}
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('f0ck #${mediaState.mediaItems[_currentIndex].id}'),
automaticallyImplyLeading: false,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
context.canPop() ? context.pop() : context.go('/', extra: true);
},
),
actions: [
IconButton(
icon: Icon(Icons.fullscreen),
icon: const Icon(Icons.fullscreen),
onPressed: () {
// wip
_showError('download ist wip');
},
),
IconButton(
icon: Icon(Icons.download),
onPressed: () {
// wip
},
icon: const Icon(Icons.download),
onPressed: _downloadMedia,
),
PopupMenuButton<String>(
onSelected: (value) async {
@ -170,7 +231,7 @@ class _DetailViewState extends ConsumerState<DetailView> {
body: Stack(
children: [
PageTransformer(
controller: _pageController,
controller: _pageController!,
pages: mediaState.mediaItems.map((item) {
int itemIndex = mediaState.mediaItems.indexOf(item);
return SafeArea(
@ -189,8 +250,8 @@ class _DetailViewState extends ConsumerState<DetailView> {
child: InputChip(
label: Text(mediaState.tag!),
onDeleted: () {
mediaNotifier.setTag(null);
Navigator.pop(context);
ref.read(mediaProvider.notifier).setTag(null);
context.go('/', extra: true);
},
),
),
@ -224,7 +285,7 @@ class _DetailViewState extends ConsumerState<DetailView> {
if (tag.tag == 'sfw' || tag.tag == 'nsfw') return;
setState(() {
mediaNotifier.setTag(tag.tag);
Navigator.pop(context, true);
context.go('/', extra: true);
});
},
label: Text(tag.tag),

View File

@ -1,21 +1,18 @@
import 'package:flutter/material.dart';
import 'package:app_links/app_links.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:f0ckapp/screens/DetailView.dart';
import 'package:f0ckapp/providers/MediaProvider.dart';
import 'package:f0ckapp/utils/AppVersion.dart';
import 'package:f0ckapp/utils/ParseDeepLink.dart';
import 'package:f0ckapp/providers/ThemeProvider.dart';
import 'package:f0ckapp/providers/media_provider.dart';
import 'package:f0ckapp/utils/appversion_util.dart';
import 'package:f0ckapp/providers/theme_provider.dart';
const List<String> mediaTypes = ["alles", "image", "video", "audio"];
const List<String> mediaModes = ["sfw", "nsfw", "untagged", "all"];
class MediaGrid extends ConsumerStatefulWidget {
final Uri? initialUri = null;
const MediaGrid({super.key, required initialUri});
const MediaGrid({super.key});
@override
ConsumerState<MediaGrid> createState() => _MediaGridState();
@ -28,8 +25,6 @@ class _MediaGridState extends ConsumerState<MediaGrid> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final appLinks = AppLinks();
int _calculateCrossAxisCount(BuildContext context, int defaultCount) {
return defaultCount == 0
? (MediaQuery.of(context).size.width / 110).clamp(3, 5).toInt()
@ -48,23 +43,6 @@ class _MediaGridState extends ConsumerState<MediaGrid> {
ref.read(mediaProvider.notifier).loadMedia();
}
});
appLinks.uriLinkStream.listen((Uri uri) async {
final parsedResult = parseDeepLink(uri);
if (parsedResult == null) return;
if (parsedResult['route'] != 'complex') return;
final params = parsedResult['params'] as Map<String, String>;
await handleComplexDeepLink(params, context, ref, _scrollController);
});
//print('initial: ${parseDeepLink(widget.initialUri)}');
Future.microtask(() async {
final initparsedResult = parseDeepLink(widget.initialUri);
if (initparsedResult == null) return;
if (initparsedResult['route'] != 'complex') return;
final initparams = initparsedResult['params'] as Map<String, String>;
await handleComplexDeepLink(initparams, context, ref, _scrollController);
});
}
@override
@ -302,15 +280,7 @@ class _MediaGridState extends ConsumerState<MediaGrid> {
return InkWell(
onTap: () async {
bool? ret = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailView(initialItemId: item.id),
),
);
if (ret != null && ret) {
_scrollController.jumpTo(0);
}
context.push('/${item.id}', extra: true);
},
child: Stack(
fit: StackFit.expand,

View File

@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:f0ckapp/models/MediaItem.dart';
import 'package:f0ckapp/models/mediaitem_model.dart';
final storage = FlutterSecureStorage();

View File

@ -1,83 +0,0 @@
import 'package:f0ckapp/providers/MediaProvider.dart';
import 'package:f0ckapp/screens/DetailView.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Map<String, RegExp> routes = {
//'login': RegExp(r'^/login/?$'),
//'user': RegExp(r'^/user/(?<user>.*)$'),
'complex': RegExp(
r'^/?'
r'(?:tag/(?<tag>.+?))?'
r'(?:/user/(?<username>.+?)/(?<set>f0cks|favs))?'
r'(?:/(?<media>image|audio|video))?'
r'(?:/(?<id>\d+))?'
r'$',
),
//'random': RegExp(r'^/random$'),
//'search': RegExp(r'^/search/?$'),
};
Map<String, dynamic> parseDeepLink(Uri? uri) {
if (uri == null) {
return {};
}
String url = uri.toString().replaceAll("https://f0ck.me", "");
for (final MapEntry<String, RegExp> entry in routes.entries) {
final String routeName = entry.key;
final RegExp pattern = entry.value;
final RegExpMatch? match = pattern.firstMatch(url.toString());
if (match != null) {
Map<String, String> params = <String, String>{};
for (String name in match.groupNames) {
params[name] = match.namedGroup(name) ?? '';
}
return {'route': routeName, 'params': params};
}
}
return {};
}
Future<void> handleComplexDeepLink(
Map<String, String> params,
BuildContext context,
WidgetRef ref,
ScrollController scrollController,
) async {
final media = params['media'];
const validMediaTypes = {'audio', 'video', 'image'};
if (media != null && validMediaTypes.contains(media)) {
ref.read(mediaProvider.notifier).setType(media);
}
final idParam = params['id'];
if (idParam == null || idParam.isEmpty) return;
final int? id = int.tryParse(idParam);
if (id == null) return;
final mediaState = ref.read(mediaProvider);
final index = mediaState.mediaItems.indexWhere((item) => item.id == id);
if (index == -1) {
await ref.read(mediaProvider.notifier).loadMedia(id: id + 50);
final updatedState = ref.read(mediaProvider);
final updatedIndex = updatedState.mediaItems.indexWhere(
(item) => item.id == id,
);
print(updatedIndex.toString());
if (updatedIndex == -1) {
return;
}
}
bool? navigationResult = await Navigator.push(
context,
MaterialPageRoute(builder: (_) => DetailView(initialItemId: id)),
);
if (navigationResult == true) {
scrollController.jumpTo(0);
}
}

View File

@ -6,9 +6,9 @@ import 'package:cached_video_player_plus/cached_video_player_plus.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:f0ckapp/models/MediaItem.dart';
import 'package:f0ckapp/widgets/VideoOverlay.dart';
import 'package:f0ckapp/providers/MediaProvider.dart';
import 'package:f0ckapp/models/mediaitem_model.dart';
import 'package:f0ckapp/widgets/videooverlay_widget.dart';
import 'package:f0ckapp/providers/media_provider.dart';
class VideoWidget extends ConsumerStatefulWidget {
final MediaItem details;

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:cached_video_player_plus/cached_video_player_plus.dart';
import 'package:f0ckapp/providers/MediaProvider.dart';
import 'package:f0ckapp/providers/media_provider.dart';
class VideoControlsOverlay extends ConsumerWidget {
final CachedVideoPlayerPlusController controller;

View File

@ -1,38 +1,6 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
app_links:
dependency: "direct main"
description:
name: app_links
sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba"
url: "https://pub.dev"
source: hosted
version: "6.4.0"
app_links_linux:
dependency: transitive
description:
name: app_links_linux
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
url: "https://pub.dev"
source: hosted
version: "1.0.3"
app_links_platform_interface:
dependency: transitive
description:
name: app_links_platform_interface
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
app_links_web:
dependency: transitive
description:
name: app_links_web
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
url: "https://pub.dev"
source: hosted
version: "1.0.4"
async:
dependency: transitive
description:
@ -182,6 +150,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flutter_downloader:
dependency: "direct main"
description:
name: flutter_downloader
sha256: "93a9ddbd561f8a3f5483b4189453fba145a0a1014a88143c96a966296b78a118"
url: "https://pub.dev"
source: hosted
version: "1.12.0"
flutter_lints:
dependency: "direct dev"
description:
@ -272,14 +248,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
gtk:
dependency: transitive
go_router:
dependency: "direct main"
description:
name: gtk
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
name: go_router
sha256: b453934c36e289cef06525734d1e676c1f91da9e22e2017d9dcab6ce0f999175
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "15.1.3"
html:
dependency: transitive
description:
@ -344,6 +320,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:
@ -409,7 +393,7 @@ packages:
source: hosted
version: "1.9.1"
path_provider:
dependency: transitive
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
@ -456,6 +440,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: "2d070d8684b68efb580a5997eb62f675e8a885ef0be6e754fb9ef489c177470f"
url: "https://pub.dev"
source: hosted
version: "12.0.0+1"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6"
url: "https://pub.dev"
source: hosted
version: "13.0.1"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
url: "https://pub.dev"
source: hosted
version: "9.4.7"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
url: "https://pub.dev"
source: hosted
version: "0.1.3+5"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
url: "https://pub.dev"
source: hosted
version: "4.3.0"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.2.1"
platform:
dependency: transitive
description:

View File

@ -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.1.7+37
version: 1.1.10+40
environment:
sdk: ^3.9.0-100.2.beta
@ -41,7 +41,10 @@ dependencies:
share_plus: ^11.0.0
flutter_secure_storage: ^9.2.4
flutter_riverpod: ^2.6.1
app_links: ^6.4.0
go_router: ^15.1.3
flutter_downloader: ^1.12.0
permission_handler: ^12.0.0+1
path_provider: ^2.1.5
dev_dependencies:
flutter_test: