Merge branch 'dev' into 'test'

Dev

See merge request Didvan/didvan-app!4
This commit is contained in:
MohammadTaha Basiri 2022-03-07 09:36:08 +00:00
commit bc443339a6
36 changed files with 837 additions and 174 deletions

View File

@ -65,5 +65,6 @@ flutter {
}
dependencies {
implementation platform('com.google.firebase:firebase-bom:29.1.0')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

@ -0,0 +1,39 @@
{
"project_info": {
"project_number": "935017686266",
"project_id": "didvan-9b7da",
"storage_bucket": "didvan-9b7da.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:935017686266:android:f9cbc9aba8e3d65ed2d543",
"android_client_info": {
"package_name": "com.didvan.didvanapp"
}
},
"oauth_client": [
{
"client_id": "935017686266-lebnol7rb05oi9h0mripb41c892d2gij.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "935017686266-lebnol7rb05oi9h0mripb41c892d2gij.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

View File

@ -7,6 +7,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.google.gms:google-services:4.3.10'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View File

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '9.0'
platform :ios, '10.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -1,6 +1,41 @@
PODS:
- audio_session (0.0.1):
- Flutter
- Firebase/CoreOnly (8.11.0):
- FirebaseCore (= 8.11.0)
- Firebase/Messaging (8.11.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 8.11.0)
- firebase_core (1.13.1):
- Firebase/CoreOnly (= 8.11.0)
- Flutter
- firebase_messaging (11.2.8):
- Firebase/Messaging (= 8.11.0)
- firebase_core
- Flutter
- FirebaseCore (8.11.0):
- FirebaseCoreDiagnostics (~> 8.0)
- GoogleUtilities/Environment (~> 7.7)
- GoogleUtilities/Logger (~> 7.7)
- FirebaseCoreDiagnostics (8.12.0):
- GoogleDataTransport (~> 9.1)
- GoogleUtilities/Environment (~> 7.7)
- GoogleUtilities/Logger (~> 7.7)
- nanopb (~> 2.30908.0)
- FirebaseInstallations (8.12.0):
- FirebaseCore (~> 8.0)
- GoogleUtilities/Environment (~> 7.7)
- GoogleUtilities/UserDefaults (~> 7.7)
- PromisesObjC (< 3.0, >= 1.2)
- FirebaseMessaging (8.11.0):
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleDataTransport (~> 9.1)
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
- GoogleUtilities/Environment (~> 7.7)
- GoogleUtilities/Reachability (~> 7.7)
- GoogleUtilities/UserDefaults (~> 7.7)
- nanopb (~> 2.30908.0)
- Flutter (1.0.0)
- flutter_secure_storage (3.3.1):
- Flutter
@ -9,6 +44,27 @@ PODS:
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- GoogleDataTransport (9.1.2):
- GoogleUtilities/Environment (~> 7.2)
- nanopb (~> 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/AppDelegateSwizzler (7.7.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Environment (7.7.0):
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/Logger (7.7.0):
- GoogleUtilities/Environment
- GoogleUtilities/Network (7.7.0):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (7.7.0)"
- GoogleUtilities/Reachability (7.7.0):
- GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (7.7.0):
- GoogleUtilities/Logger
- image_cropper (0.0.4):
- Flutter
- TOCropViewController (~> 2.6.1)
@ -16,8 +72,14 @@ PODS:
- Flutter
- just_audio (0.0.1):
- Flutter
- nanopb (2.30908.0):
- nanopb/decode (= 2.30908.0)
- nanopb/encode (= 2.30908.0)
- nanopb/decode (2.30908.0)
- nanopb/encode (2.30908.0)
- path_provider_ios (0.0.1):
- Flutter
- PromisesObjC (2.0.0)
- record (0.0.1):
- Flutter
- sqflite (0.0.2):
@ -29,6 +91,8 @@ PODS:
DEPENDENCIES:
- audio_session (from `.symlinks/plugins/audio_session/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- flutter_vibrate (from `.symlinks/plugins/flutter_vibrate/ios`)
@ -42,12 +106,25 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- Firebase
- FirebaseCore
- FirebaseCoreDiagnostics
- FirebaseInstallations
- FirebaseMessaging
- FMDB
- GoogleDataTransport
- GoogleUtilities
- nanopb
- PromisesObjC
- TOCropViewController
EXTERNAL SOURCES:
audio_session:
:path: ".symlinks/plugins/audio_session/ios"
firebase_core:
:path: ".symlinks/plugins/firebase_core/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
Flutter:
:path: Flutter
flutter_secure_storage:
@ -71,19 +148,30 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
Firebase: 44dd9724c84df18b486639e874f31436eaa9a20c
firebase_core: 08f6a85f62060111de5e98d6a214810d11365de9
firebase_messaging: 36238f3d0b933af8c919aef608408aae06ba22e8
FirebaseCore: 2f4f85b453cc8fea4bb2b37e370007d2bcafe3f0
FirebaseCoreDiagnostics: 3b40dfadef5b90433a60ae01f01e90fe87aa76aa
FirebaseInstallations: 25764cf322e77f99449395870a65b2bef88e1545
FirebaseMessaging: 02e248e8997f71fa8cc9d78e9d49ec1a701ba14a
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
flutter_vibrate: 9f4c2ab57008965f78969472367c329dd77eb801
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98
image_picker: 9aa50e1d8cdacdbed739e925b7eea16d014367e6
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5
PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58
record: 7ee2393532f8553bbb09fa19e95478323b7c0a99
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af
PODFILE CHECKSUM: a75497545d4391e2d394c3668e20cfb1c2bbd4aa
PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea
COCOAPODS: 1.11.2

View File

@ -13,6 +13,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
D194CE3E27D4A4740049AFC7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D194CE3D27D4A4740049AFC7 /* GoogleService-Info.plist */; };
E870A5F479A60D6704DD5DF2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75DBECA488F412614712FB74 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
@ -47,6 +48,7 @@
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
98ACB01D5FA5A78DB2686183 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
C97DED20C4A171F16FB949CD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
D194CE3D27D4A4740049AFC7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -112,6 +114,7 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
D194CE3D27D4A4740049AFC7 /* GoogleService-Info.plist */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
@ -189,6 +192,7 @@
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
D194CE3E27D4A4740049AFC7 /* GoogleService-Info.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);

View File

@ -1,13 +1,30 @@
import UIKit
import Flutter
import Firebase
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
@objc class AppDelegate: FlutterAppDelegate, MessagingDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
Messaging.messaging().delegate = self
GeneratedPluginRegistrant.register(with: self)
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
}

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CLIENT_ID</key>
<string>935017686266-54hu01v9cc5pqpgofo1gk2n3hegj4r2m.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.935017686266-54hu01v9cc5pqpgofo1gk2n3hegj4r2m</string>
<key>API_KEY</key>
<string>AIzaSyCMa-zg_uVhOfTnea5Klz6aPZlgHwVGj7U</string>
<key>GCM_SENDER_ID</key>
<string>935017686266</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.didvan.didvanapp</string>
<key>PROJECT_ID</key>
<string>didvan-9b7da</string>
<key>STORAGE_BUCKET</key>
<string>didvan-9b7da.appspot.com</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:935017686266:ios:de47638bd662463fd2d543</string>
</dict>
</plist>

View File

@ -26,12 +26,14 @@
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>NSMicrophoneUsageDescription</key>
<string>We need to access to the microphone to record audio file</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need to access to the user gallery to add user profile photo</string>
<key>NSCameraUsageDescription</key>
<string>We need to access to the user gallery to add user profile photo</string>
<string>We need to access to the microphone to record audio file</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need to access to the user gallery to add user profile photo</string>
<key>NSCameraUsageDescription</key>
<string>We need to access to the user gallery to add user profile photo</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>

View File

@ -4,6 +4,8 @@ class Content {
final String? audio;
final String? video;
final String? image;
final String? largeImage;
final String? caption;
const Content({
required this.order,
@ -11,6 +13,8 @@ class Content {
required this.audio,
required this.video,
required this.image,
required this.largeImage,
required this.caption,
});
factory Content.fromJson(Map<String, dynamic> json) => Content(
@ -19,6 +23,8 @@ class Content {
audio: json['audio'],
video: json['video'],
image: json['image'],
largeImage: json['largeImage'],
caption: json['caption'],
);
Map<String, dynamic> toJson() => {
@ -27,5 +33,7 @@ class Content {
'audio': audio,
'video': video,
'image': image,
'largeImage': largeImage,
'caption': caption,
};
}

View File

@ -21,7 +21,7 @@ class ServerDataProvider {
directTypes.add(MapEntry(types[i]['id'], types[i]['label']));
}
} else {
throw 'Fetchin direct types failed!';
throw 'Fetching direct types failed!';
}
}
}

View File

@ -25,8 +25,10 @@ import 'package:didvan/views/home/settings/direct_list/direct_list_state.dart';
import 'package:didvan/views/home/settings/general_settings/settings.dart';
import 'package:didvan/views/home/settings/general_settings/settings_state.dart';
import 'package:didvan/views/home/settings/profile/profile.dart';
import 'package:didvan/views/home/studio/studio_state.dart';
import 'package:didvan/views/splash/splash.dart';
import 'package:didvan/routes/routes.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -57,6 +59,9 @@ class RouteGenerator {
ChangeNotifierProvider<NewsState>(
create: (context) => NewsState(),
),
ChangeNotifierProvider<StudioState>(
create: (context) => StudioState(),
),
],
child: const Home(),
),
@ -161,13 +166,20 @@ class RouteGenerator {
static Route _createRoute(page) {
return MaterialPageRoute(
builder: (context) => Container(
color: Theme.of(context).colorScheme.surface,
child: SafeArea(
child: page,
top: false,
),
),
builder: (context) {
final shortestSide = MediaQuery.of(context).size.shortestSide;
final bool useMobileLayout = shortestSide < 600;
if (kIsWeb && !useMobileLayout) {
return Center(child: AspectRatio(aspectRatio: 9 / 16, child: page));
}
return Container(
color: Theme.of(context).colorScheme.surface,
child: SafeArea(
child: page,
top: false,
),
);
},
);
}
}

View File

@ -1,5 +1,7 @@
import 'package:didvan/services/media/media.dart';
import 'package:didvan/services/storage/storage.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
@ -10,6 +12,7 @@ class AppInitializer {
StorageService.appDocsDir =
(await getApplicationDocumentsDirectory()).path;
StorageService.appTempsDir = (await getTemporaryDirectory()).path;
await _initializeFirebase();
MediaService.init();
}
}
@ -42,4 +45,29 @@ class AppInitializer {
return ThemeMode.light;
}
}
static Future<void> _initializeFirebase() async {
try {
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: 'AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw',
appId: '1:935017686266:android:f9cbc9aba8e3d65ed2d543',
messagingSenderId: '935017686266',
projectId: 'didvan-9b7da',
),
);
} catch (e) {
Firebase.app();
}
final FirebaseMessaging fcm = FirebaseMessaging.instance;
await fcm.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
}
}

View File

@ -7,6 +7,8 @@ import 'package:just_audio/just_audio.dart';
class MediaService {
static final AudioPlayer audioPlayer = AudioPlayer();
static String? audioPlayerTag;
static String? audioPlayerTitle;
static String? audioPlayerCover;
static void init() {
audioPlayer.positionStream.listen((event) {
@ -54,6 +56,11 @@ class MediaService {
}
}
static Future<void> resetAudioPlayer() async {
audioPlayerTag = null;
MediaService.audioPlayer.stop();
}
static Future<XFile?> pickImage({required ImageSource source}) async {
final imagePicker = ImagePicker();
final XFile? pickedFile = await imagePicker.pickImage(source: source);

View File

@ -34,7 +34,7 @@ class RequestHelper {
_urlConcatGenerator([
MapEntry('limit', limit?.toString() ?? '3'),
MapEntry('type', type),
MapEntry('id', itemId.toString()),
MapEntry('id', itemId?.toString() ?? '1'),
MapEntry('tags', _urlListConcatGenerator(ids))
]);

View File

@ -5,6 +5,7 @@ import 'package:didvan/views/widgets/didvan/text_field.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
class UsernameInput extends StatefulWidget {
const UsernameInput({
@ -70,7 +71,7 @@ class _UsernameInputState extends State<UsernameInput> {
.textTheme
.caption!
.copyWith(color: Theme.of(context).colorScheme.primary),
recognizer: TapGestureRecognizer()..onTap = () {},
recognizer: TapGestureRecognizer()..onTap = _openTermsOfUse,
),
const TextSpan(text: 'و\n'),
TextSpan(
@ -79,7 +80,7 @@ class _UsernameInputState extends State<UsernameInput> {
.textTheme
.caption!
.copyWith(color: Theme.of(context).colorScheme.primary),
recognizer: TapGestureRecognizer()..onTap = () {},
recognizer: TapGestureRecognizer()..onTap = _openTermsOfUse,
),
const TextSpan(text: 'را می‌پذیرم'),
],
@ -92,4 +93,8 @@ class _UsernameInputState extends State<UsernameInput> {
],
);
}
void _openTermsOfUse() {
launch('https://didvan.app/termsOfUse.html');
}
}

View File

@ -1,6 +1,7 @@
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/providers/server_data_provider.dart';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/views/home/direct/direct_state.dart';
import 'package:didvan/views/home/direct/widgets/message.dart';
import 'package:didvan/views/home/direct/widgets/message_box.dart';
@ -35,59 +36,65 @@ class _DirectState extends State<Direct> {
Widget build(BuildContext context) {
final state = context.watch<DirectState>();
final d = MediaQuery.of(context);
return Material(
child: Stack(
children: [
Positioned(
top: 0,
bottom: 56,
left: 0,
right: 0,
child: DidvanScaffold(
reverse: true,
backgroundColor: Theme.of(context).colorScheme.surface,
appBarData: AppBarData(
hasBack: true,
subtitle: 'ارتباط با سردبیر',
title: widget.pageData['type'] ?? 'پشتیبانی اپلیکیشن',
return WillPopScope(
onWillPop: () async {
MediaService.resetAudioPlayer();
return true;
},
child: Material(
child: Stack(
children: [
Positioned(
top: 0,
bottom: 56,
left: 0,
right: 0,
child: DidvanScaffold(
reverse: true,
backgroundColor: Theme.of(context).colorScheme.surface,
appBarData: AppBarData(
hasBack: true,
subtitle: 'ارتباط با سردبیر',
title: widget.pageData['type'] ?? 'پشتیبانی اپلیکیشن',
),
slivers: [
if (state.appState != AppState.busy)
SliverPadding(
padding: state.replyRadar == null
? EdgeInsets.zero
: const EdgeInsets.only(bottom: 68),
sliver: SliverStateHandler<DirectState>(
itemPadding: const EdgeInsets.only(bottom: 12),
state: state,
builder: (context, state, index) => Message(
message: state.messages[index],
),
childCount: state.messages.length,
onRetry: state.getMessages,
),
),
],
children: [
if (state.appState == AppState.busy)
SizedBox(
height: d.size.height - kToolbarHeight - d.padding.top,
child: Center(
child: SpinKitSpinningLines(
color: Theme.of(context).colorScheme.primary,
),
),
),
],
),
slivers: [
if (state.appState != AppState.busy)
SliverPadding(
padding: state.replyRadar == null
? EdgeInsets.zero
: const EdgeInsets.only(bottom: 68),
sliver: SliverStateHandler<DirectState>(
itemPadding: const EdgeInsets.only(bottom: 12),
state: state,
builder: (context, state, index) => Message(
message: state.messages[index],
),
childCount: state.messages.length,
onRetry: state.getMessages,
),
),
],
children: [
if (state.appState == AppState.busy)
SizedBox(
height: d.size.height - kToolbarHeight - d.padding.top,
child: Center(
child: SpinKitSpinningLines(
color: Theme.of(context).colorScheme.primary,
),
),
),
],
),
),
Positioned(
bottom: d.viewInsets.bottom,
right: 0,
left: 0,
child: const MessageBox(),
),
],
Positioned(
bottom: d.viewInsets.bottom,
right: 0,
left: 0,
child: const MessageBox(),
),
],
),
),
);
}

View File

@ -1,3 +1,4 @@
import 'dart:developer';
import 'dart:io';
import 'package:didvan/models/enums.dart';
@ -31,7 +32,7 @@ class DirectState extends CoreProvier {
final messageDatas = service.result['messages'];
for (var i = 0; i < messageDatas.length; i++) {
messages.add(MessageData.fromJson(messageDatas[i]));
_addToDailyGrouped();
_addToDailyGrouped(messages.last);
}
appState = AppState.idle;
return;
@ -75,8 +76,8 @@ class DirectState extends CoreProvier {
}
}
void _addToDailyGrouped() {
final createdAt = messages.last.createdAt.split('T').first;
void _addToDailyGrouped(MessageData message) {
String createdAt = message.createdAt.replaceAll('T', ' ').split(' ').first;
if (!dailyMessages.containsKey(createdAt)) {
dailyMessages.addAll({
createdAt: [messages.last.id]
@ -88,7 +89,6 @@ class DirectState extends CoreProvier {
Future<void> sendMessage() async {
if ((text == null || text!.isEmpty) && recordedFile == null) return;
replyRadar = null;
messages.insert(
0,
MessageData(
@ -104,7 +104,7 @@ class DirectState extends CoreProvier {
audioDuration: audioDuration,
),
);
_addToDailyGrouped();
_addToDailyGrouped(messages.first);
final body = {};
if (text != null) {
body.addAll({'text': text});
@ -115,6 +115,7 @@ class DirectState extends CoreProvier {
final uploadFile = recordedFile;
text = null;
recordedFile = null;
replyRadar = null;
notifyListeners();
final service =
RequestService(RequestHelper.sendDirectMessage(typeId), body: body);

View File

@ -58,6 +58,7 @@ class Message extends StatelessWidget {
_MessageContainer(
writedByAdmin: message.writedByAdmin,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (message.text != null) DidvanText(message.text!),
if (message.audio != null || message.audioFile != null)

View File

@ -28,12 +28,19 @@ class _HashtagState extends State<Hashtag> {
@override
Widget build(BuildContext context) {
return DidvanScaffold(
appBarData: AppBarData(title: widget.tag.label, hasBack: true),
appBarData: AppBarData(title: '#' + widget.tag.label, hasBack: true),
slivers: [
Consumer<HashtagState>(
builder: (context, state, child) => SliverStateHandler<HashtagState>(
itemPadding: const EdgeInsets.only(bottom: 8),
state: state,
placeholder: RadarOverview.placeholder,
builder: (context, state, index) {
index++;
if (index % 15 == 0 && index / 15 >= state.page) {
state.getTagItems(page: index ~/ 15 + 1);
}
index--;
final item = state.items[index];
final type = item.type;
if (type == 'radar') {
@ -51,7 +58,7 @@ class _HashtagState extends State<Hashtag> {
return Container();
},
childCount: state.items.length,
onRetry: () {},
onRetry: () => state.getTagItems(page: 1),
),
)
],

View File

@ -17,6 +17,8 @@ class HashtagState extends CoreProvier {
}
final service = RequestService(RequestHelper.tag(
ids: [id],
itemId: 1,
type: 'radar',
limit: 15,
page: page,
));

View File

@ -65,7 +65,7 @@ class _RadarState extends State<Radar> {
CustomScrollView(
physics: _isAnimating
? const NeverScrollableScrollPhysics()
: const ScrollPhysics(),
: const ClampingScrollPhysics(),
controller: _scrollController,
slivers: [
const SliverToBoxAdapter(child: LogoAppBar()),

View File

@ -8,7 +8,6 @@ import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/providers/core_provider.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:flutter/material.dart';
class RadarDetailsState extends CoreProvier {
final List<RadarDetailsData?> radars = [];

View File

@ -4,6 +4,7 @@ import 'package:didvan/models/view/radar_category.dart';
import 'package:didvan/views/home/radar/radar_state.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
@ -18,6 +19,22 @@ class CategoryItem extends StatelessWidget {
required this.category,
}) : super(key: key);
double _width(context) {
final Size ds = MediaQuery.of(context).size;
if (kIsWeb) {
if (!_useWebMobileLayout(context)) {
return ds.height / 16 * 9 / 3;
}
}
return ds.width / 3;
}
bool _useWebMobileLayout(context) {
final shortestSide = MediaQuery.of(context).size.shortestSide;
final bool useMobileLayout = shortestSide < 600;
return kIsWeb && useMobileLayout;
}
@override
Widget build(BuildContext context) {
final Size ds = MediaQuery.of(context).size;
@ -34,7 +51,7 @@ class CategoryItem extends StatelessWidget {
child: AnimatedContainer(
duration: DesignConfig.mediumAnimationDuration,
padding: isColapsed ? const EdgeInsets.all(4) : EdgeInsets.zero,
width: isColapsed ? 100 : ds.width / 3,
width: isColapsed ? 100 : _width(context),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: DesignConfig.lowBorderRadius,
@ -48,8 +65,12 @@ class CategoryItem extends StatelessWidget {
duration: DesignConfig.mediumAnimationDuration,
isVisible: !isColapsed,
child: Container(
width: ds.width / 5,
height: ds.width / 5,
width: !_useWebMobileLayout(context)
? _width(context) / 2
: ds.width / 5,
height: !_useWebMobileLayout(context)
? _width(context) / 2
: ds.width / 5,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
boxShadow: DesignConfig.defaultShadow,

View File

@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
class PodcastDetails extends StatelessWidget {
const PodcastDetails({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Material(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(top: Radius.circular(8)),
),
),
);
}
}

View File

@ -1,10 +1,17 @@
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/home/studio/studio_state.dart';
import 'package:didvan/views/home/studio/widgets/slider.dart';
import 'package:didvan/views/home/studio/widgets/tab_bar.dart';
import 'package:didvan/views/home/widgets/logo_app_bar.dart';
import 'package:didvan/views/home/widgets/search_field.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/radial_button.dart';
import 'package:didvan/views/widgets/item_title.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Studio extends StatefulWidget {
const Studio({Key? key}) : super(key: key);
@ -51,7 +58,73 @@ class _StudioState extends State<Studio> {
const SliverToBoxAdapter(
child: StudioSlider(),
),
const SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
child: DidvanDivider(
verticalPadding: 0,
),
),
),
SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const ItemTitle(title: 'تازه‌ترین‌ها'),
DidvanIconButton(
gestureSize: 36,
icon: DidvanIcons.sort_regular,
onPressed: _showSortDialog,
),
],
),
),
),
],
);
}
void _showSortDialog() {
final state = context.read<StudioState>();
ActionSheetUtils.showBottomSheet(
data: ActionSheetData(
content: StatefulBuilder(
builder: (context, setState) => Column(
children: [
DidvanRadialButton(
title: 'جدیدترین‌ها',
onSelected: () => setState(
() => state.selectedSortTypeIndex = 0,
),
value: state.selectedSortTypeIndex == 0,
),
const SizedBox(height: 24),
DidvanRadialButton(
title: 'پربازدیدترین‌ها',
onSelected: () => setState(
() => state.selectedSortTypeIndex = 1,
),
value: state.selectedSortTypeIndex == 1,
),
const SizedBox(height: 24),
DidvanRadialButton(
title: 'پربحث‌ترین‌ها',
onSelected: () => setState(
() => state.selectedSortTypeIndex = 2,
),
value: state.selectedSortTypeIndex == 2,
),
],
),
),
title: 'مرتب‌‌سازی',
titleIcon: DidvanIcons.sort_regular,
hasDismissButton: false,
confrimTitle: 'مرتب سازی',
onConfirmed: () {},
),
);
}
}

View File

@ -1,5 +1,7 @@
import 'package:didvan/providers/core_provider.dart';
class StudioState extends CoreProvier {
int selectedSortTypeIndex = 0;
bool videosSelected = true;
}

View File

@ -1,8 +1,10 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/views/home/studio/studio_state.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class StudioTabBar extends StatelessWidget {
const StudioTabBar({
@ -11,11 +13,16 @@ class StudioTabBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final state = context.watch<StudioState>();
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).colorScheme.border),
border: Border.all(
color: state.videosSelected
? Theme.of(context).colorScheme.secondary
: Theme.of(context).primaryColor,
),
borderRadius: DesignConfig.lowBorderRadius,
),
child: Row(
@ -26,7 +33,7 @@ class StudioTabBar extends StatelessWidget {
selectedColor: Theme.of(context).colorScheme.secondary,
title: 'ویدئو',
onTap: () {},
isSelected: true,
isSelected: state.videosSelected,
),
),
Container(
@ -40,7 +47,7 @@ class StudioTabBar extends StatelessWidget {
selectedColor: Theme.of(context).colorScheme.focusedBorder,
title: 'پادکست',
onTap: () {},
isSelected: true,
isSelected: !state.videosSelected,
),
),
],
@ -64,7 +71,8 @@ class _StudioTypeButton extends StatelessWidget {
required this.isSelected,
}) : super(key: key);
Color? get _color => isSelected ? selectedColor : null;
Color? _color(context) =>
isSelected ? selectedColor : Theme.of(context).colorScheme.hint;
@override
Widget build(BuildContext context) {
@ -77,20 +85,20 @@ class _StudioTypeButton extends StatelessWidget {
Icon(
icon,
size: 32,
color: _color,
color: _color(context),
),
if (!isSelected) const SizedBox(height: 18),
if (isSelected)
Container(
width: 88,
height: 1,
color: _color,
color: _color(context),
),
if (isSelected)
DidvanText(
title,
style: Theme.of(context).textTheme.overline,
color: _color,
color: _color(context),
)
],
),

View File

@ -1,7 +1,10 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
class DidvanBNB extends StatelessWidget {
@ -14,54 +17,93 @@ class DidvanBNB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 72,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
boxShadow: DesignConfig.defaultShadow,
),
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
_NavBarItem(
isSelected: currentTabIndex == 0,
title: 'اخبار',
selectedIcon: DidvanIcons.news_solid,
unselectedIcon: DidvanIcons.news_light,
onTap: () => onTabChanged(0),
),
_NavBarItem(
isSelected: currentTabIndex == 1,
title: 'آمار',
selectedIcon: DidvanIcons.chart_solid,
unselectedIcon: DidvanIcons.chart_light,
onTap: () => onTabChanged(1),
),
_NavBarItem(
isSelected: currentTabIndex == 2,
title: 'رادار',
selectedIcon: DidvanIcons.radar_solid,
unselectedIcon: DidvanIcons.radar_light,
onTap: () => onTabChanged(2),
),
_NavBarItem(
isSelected: currentTabIndex == 3,
title: 'استودیو',
selectedIcon: DidvanIcons.play_circle_solid,
unselectedIcon: DidvanIcons.play_circle_light,
onTap: () => onTabChanged(3),
),
_NavBarItem(
isSelected: currentTabIndex == 4,
title: 'تنظیمات',
selectedIcon: DidvanIcons.setting_solid,
unselectedIcon: DidvanIcons.setting_light,
onTap: () => onTabChanged(4),
),
],
),
);
return StreamBuilder<bool>(
stream: MediaService.audioPlayer.playingStream,
builder: (context, snapshot) {
return Stack(
children: [
AnimatedContainer(
duration: DesignConfig.lowAnimationDuration,
height: snapshot.data == true ? 120 : 72,
decoration: BoxDecoration(
color: DesignConfig.isDark
? Theme.of(context).colorScheme.focused
: Theme.of(context).colorScheme.navigation,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(16),
),
),
child: Row(
children: [
const DidvanIconButton(
icon: DidvanIcons.close_regular,
gestureSize: 24,
onPressed: MediaService.resetAudioPlayer,
),
const SizedBox(width: 16),
if (MediaService.audioPlayerCover != null)
SkeletonImage(imageUrl: MediaService.audioPlayerCover!),
const SizedBox(width: 16),
],
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
height: 72,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius:
const BorderRadius.vertical(top: Radius.circular(16)),
boxShadow: DesignConfig.defaultShadow,
),
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
_NavBarItem(
isSelected: currentTabIndex == 0,
title: 'اخبار',
selectedIcon: DidvanIcons.news_solid,
unselectedIcon: DidvanIcons.news_light,
onTap: () => onTabChanged(0),
),
_NavBarItem(
isSelected: currentTabIndex == 1,
title: 'آمار',
selectedIcon: DidvanIcons.chart_solid,
unselectedIcon: DidvanIcons.chart_light,
onTap: () => onTabChanged(1),
),
_NavBarItem(
isSelected: currentTabIndex == 2,
title: 'رادار',
selectedIcon: DidvanIcons.radar_solid,
unselectedIcon: DidvanIcons.radar_light,
onTap: () => onTabChanged(2),
),
_NavBarItem(
isSelected: currentTabIndex == 3,
title: 'استودیو',
selectedIcon: DidvanIcons.play_circle_solid,
unselectedIcon: DidvanIcons.play_circle_light,
onTap: () => onTabChanged(3),
),
_NavBarItem(
isSelected: currentTabIndex == 4,
title: 'تنظیمات',
selectedIcon: DidvanIcons.setting_solid,
unselectedIcon: DidvanIcons.setting_light,
onTap: () => onTabChanged(4),
),
],
),
),
),
],
);
});
}
}

View File

@ -0,0 +1,138 @@
import 'package:didvan/models/overview_data.dart';
import 'package:didvan/models/requests/news.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/home/widgets/bookmark_button.dart';
import 'package:didvan/views/widgets/didvan/card.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
class PodcastOverview extends StatelessWidget {
final OverviewData news;
final NewsRequestArgs? newsRequestArgs;
final void Function(int id, bool value) onMarkChanged;
final bool hasUnmarkConfirmation;
const PodcastOverview({
Key? key,
required this.news,
required this.onMarkChanged,
this.newsRequestArgs,
this.hasUnmarkConfirmation = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return DidvanCard(
onTap: () => Navigator.of(context).pushNamed(
Routes.newsDetails,
arguments: {
'onMarkChanged': onMarkChanged,
'id': news.id,
'args': newsRequestArgs,
'hasUnmarkConfirmation': hasUnmarkConfirmation,
},
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
SkeletonImage(
imageUrl: news.image,
width: 64,
height: 64,
),
const SizedBox(width: 8),
Expanded(
child: SizedBox(
height: 64,
child: DidvanText(
news.title,
style: Theme.of(context).textTheme.bodyText1,
),
),
),
],
),
const SizedBox(height: 8),
DidvanText(
news.description,
maxLines: 3,
),
const DidvanDivider(verticalPadding: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
DidvanText(
news.reference!,
style: Theme.of(context).textTheme.caption,
),
DidvanText(
' - ' + DateTimeUtils.momentGenerator(news.createdAt),
style: Theme.of(context).textTheme.caption,
),
],
),
BookmarkButton(
value: news.marked,
onMarkChanged: (value) => onMarkChanged(news.id, value),
askForConfirmation: hasUnmarkConfirmation,
),
],
),
],
),
);
}
static Widget get placeholder => DidvanCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const ShimmerPlaceholder(height: 64, width: 64),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
ShimmerPlaceholder(height: 18, width: 200),
SizedBox(height: 8),
ShimmerPlaceholder(height: 18, width: 100),
],
),
],
),
const SizedBox(height: 12),
const ShimmerPlaceholder(
height: 16,
width: double.infinity,
),
const SizedBox(height: 8),
const ShimmerPlaceholder(
height: 16,
width: double.infinity,
),
const SizedBox(height: 8),
const ShimmerPlaceholder(
height: 16,
width: 100,
),
const DidvanDivider(verticalPadding: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
ShimmerPlaceholder(height: 12, width: 150),
ShimmerPlaceholder(height: 24, width: 24),
],
),
],
),
);
}

View File

@ -8,7 +8,6 @@ import 'package:didvan/views/home/widgets/tag_item.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/card.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/item_title.dart';
@ -191,14 +190,17 @@ class _DidvanPageViewState extends State<DidvanPageView> {
margin: EdgeInsets.zero,
padding: EdgeInsets.zero,
),
'span': Style(
textAlign: TextAlign.center,
fontSize: FontSize.small,
alignment: Alignment.center,
),
},
);
}
if (content.caption != null) {
return Center(
child: DidvanText(
content.caption,
style: Theme.of(context).textTheme.caption,
),
);
}
if (content.image != null) {
return GestureDetector(
onTap: () {
@ -211,19 +213,15 @@ class _DidvanPageViewState extends State<DidvanPageView> {
child: Center(
child: SkeletonImage(
width: MediaQuery.of(context).size.width,
height: 200,
imageUrl: content.image,
imageUrl: content.largeImage ?? content.image,
),
),
),
),
Positioned(
right: 16,
child: DidvanIconButton(
backgroundColor: Theme.of(context).colorScheme.primary,
icon: DidvanIcons.back_regular,
onPressed: Navigator.of(context).pop,
),
const Positioned(
right: 24,
top: 24,
child: _BackButton(),
),
],
),
@ -231,7 +229,7 @@ class _DidvanPageViewState extends State<DidvanPageView> {
},
child: SkeletonImage(
imageUrl: content.image!,
aspectRatio: 16 / 9,
width: double.infinity,
),
);
}
@ -277,9 +275,8 @@ class _DidvanPageViewState extends State<DidvanPageView> {
}
class _BackButton extends StatefulWidget {
final ScrollController scrollController;
const _BackButton({Key? key, required this.scrollController})
: super(key: key);
final ScrollController? scrollController;
const _BackButton({Key? key, this.scrollController}) : super(key: key);
@override
__BackButtonState createState() => __BackButtonState();
@ -290,20 +287,26 @@ class __BackButtonState extends State<_BackButton> {
@override
void didUpdateWidget(covariant _BackButton oldWidget) {
_isVisible = false;
_handleScroll();
if (widget.scrollController != null) {
_isVisible = false;
_handleScroll();
}
super.didUpdateWidget(oldWidget);
}
@override
void initState() {
_handleScroll();
if (widget.scrollController != null) {
_handleScroll();
} else {
_isVisible = true;
}
super.initState();
}
void _handleScroll() {
widget.scrollController.addListener(() {
final position = widget.scrollController.position.pixels;
widget.scrollController!.addListener(() {
final position = widget.scrollController!.position.pixels;
final size = MediaQuery.of(context).size.height * 0.3;
if (position > size && _isVisible == false) {
setState(() {

View File

@ -0,0 +1,54 @@
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
class DidvanRadialButton extends StatelessWidget {
final String title;
final VoidCallback onSelected;
final bool value;
const DidvanRadialButton({
Key? key,
required this.title,
required this.onSelected,
required this.value,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: value ? null : onSelected,
child: Container(
color: Colors.transparent,
child: Row(
children: [
Container(
padding: const EdgeInsets.all(4),
height: 24,
width: 24,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: value
? Theme.of(context).colorScheme.secondary
: Theme.of(context).colorScheme.text,
),
),
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: value
? Theme.of(context).colorScheme.secondary
: Colors.transparent,
shape: BoxShape.circle,
),
),
),
const SizedBox(width: 8),
DidvanText(title),
],
),
),
);
}
}

View File

@ -8,17 +8,17 @@ import 'package:cached_network_image_platform_interface/cached_network_image_pla
class SkeletonImage extends StatelessWidget {
final String imageUrl;
final double width;
final double height;
final double? width;
final double? height;
final BorderRadius? borderRadius;
final double? aspectRatio;
const SkeletonImage({
Key? key,
required this.imageUrl,
this.width = 300,
this.height = 140,
this.borderRadius = DesignConfig.lowBorderRadius,
this.aspectRatio,
this.width,
this.height,
}) : super(key: key);
@override
@ -30,13 +30,11 @@ class SkeletonImage extends StatelessWidget {
width: width,
height: height,
imageUrl: RequestHelper.baseUrl + imageUrl,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
borderRadius: borderRadius ?? DesignConfig.lowBorderRadius,
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
imageBuilder: (context, imageProvider) => ClipRRect(
borderRadius: borderRadius ?? DesignConfig.lowBorderRadius,
child: Image(
image: imageProvider,
fit: BoxFit.cover,
),
),
progressIndicatorBuilder: (context, url, progress) =>

View File

@ -148,6 +148,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.2"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
url: "https://pub.dartlang.org"
source: hosted
version: "1.13.1"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.5"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.1"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
url: "https://pub.dartlang.org"
source: hosted
version: "11.2.8"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.1"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.9"
flutter:
dependency: "direct main"
description: flutter
@ -762,5 +804,5 @@ packages:
source: hosted
version: "5.3.1"
sdks:
dart: ">=2.15.0 <3.0.0"
dart: ">=2.16.0 <3.0.0"
flutter: ">=2.5.0"

View File

@ -60,6 +60,8 @@ dependencies:
url_launcher: ^6.0.18
audio_video_progress_bar: ^0.10.0
image_cropper: ^1.5.0
firebase_messaging: ^11.2.8
firebase_core: ^1.13.1
dev_dependencies: