diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index 2328905..2795ffb 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -814,6 +814,13 @@ + + + + + + @@ -1013,6 +1020,7 @@ + diff --git a/Sources/justMUSIC/lib/components/comment_component.dart b/Sources/justMUSIC/lib/components/comment_component.dart index 31878e6..7743f8c 100644 --- a/Sources/justMUSIC/lib/components/comment_component.dart +++ b/Sources/justMUSIC/lib/components/comment_component.dart @@ -1,68 +1,87 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; - -import '../values/constants.dart'; - -class CommentComponent extends StatelessWidget { - const CommentComponent({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - width: double.infinity, - decoration: BoxDecoration(color: bgComment, borderRadius: BorderRadius.circular(20)), - padding: EdgeInsets.all(20), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ClipOval( - child: SizedBox.fromSize( - // Image radius - child: Image( - image: AssetImage("assets/images/exemple_profile.png"), - width: 40, - ), - ), - ), - Expanded( - child: Column( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - width: 10, - ), - Text( - "Melina", - style: GoogleFonts.plusJakartaSans(color: Colors.white, fontWeight: FontWeight.w600), - ), - Padding( - padding: EdgeInsets.only(top: 6, left: 10), - child: Text( - "Il y a 2 min(s)", - style: GoogleFonts.plusJakartaSans( - color: Colors.white.withOpacity(0.6), fontWeight: FontWeight.w400, fontSize: 10), - ), - ), - ], - ), - SizedBox( - height: 8, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Text( - "J’adore ce son aussi, je trouve qu’il avait vraiment une plume de fou.", - style: GoogleFonts.plusJakartaSans(color: Colors.white, fontWeight: FontWeight.w300, fontSize: 11), - ), - ), - ], - ), - ) - ], - ), - ); - } -} +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import 'package:justmusic/model/Comment.dart'; + +import '../values/constants.dart'; + +class CommentComponent extends StatelessWidget { + final Comment comment; + + const CommentComponent({Key? key, required this.comment}) : super(key: key); + + @override + Widget build(BuildContext context) { + final now = DateTime.now(); + final difference = now.difference(comment.date); + + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: bgComment.withOpacity(0.6), + borderRadius: BorderRadius.circular(15)), + padding: EdgeInsets.fromLTRB(20, 10, 20, 10), + margin: EdgeInsets.only(bottom: 13), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ClipOval( + child: SizedBox.fromSize( + // Image radius + child: Image( + image: NetworkImage(comment.user.pp), + width: 40, + ), + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: 10, + ), + Text( + comment.user.pseudo, + style: GoogleFonts.plusJakartaSans( + color: Colors.white, fontWeight: FontWeight.w600), + ), + Padding( + padding: EdgeInsets.only(top: 6, left: 10), + child: Text( + "il y a ${difference.inHours > 0 ? difference.inHours : difference.inMinutes}${difference.inHours > 0 ? "h" : "m"}", + style: GoogleFonts.plusJakartaSans( + color: Colors.white.withOpacity(0.6), + fontWeight: FontWeight.w400, + fontSize: 10), + ), + ), + ], + ), + SizedBox( + height: 4, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Text( + comment.text, + style: GoogleFonts.plusJakartaSans( + color: Colors.white, + fontWeight: FontWeight.w400, + fontSize: 15), + ), + ), + ], + ), + ) + ], + ), + ); + } +} diff --git a/Sources/justMUSIC/lib/components/top_nav_bar_component.dart b/Sources/justMUSIC/lib/components/top_nav_bar_component.dart index 7a63204..950d0ee 100644 --- a/Sources/justMUSIC/lib/components/top_nav_bar_component.dart +++ b/Sources/justMUSIC/lib/components/top_nav_bar_component.dart @@ -1,269 +1,299 @@ -import 'package:another_flushbar/flushbar.dart'; -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:ionicons/ionicons.dart'; -import 'package:lottie/lottie.dart'; -import 'package:zoom_tap_animation/zoom_tap_animation.dart'; - -import '../config/routes.dart'; -import '../main.dart'; -import '../values/constants.dart'; - -class TopNavBarComponent extends StatefulWidget { - final Function(bool) callback; - const TopNavBarComponent({Key? key, required this.callback}) : super(key: key); - - @override - State createState() => _TopNavBarComponentState(); -} - -class _TopNavBarComponentState extends State with TickerProviderStateMixin { - bool choice = true; - late AnimationController _controller; - bool isDismissed = true; - - final DateTime midnight = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day + 1); - - void actionSurBouton() async { - widget.callback(choice); - await MyApp.postViewModel.getBestPosts(); - await MyApp.postViewModel.getPostsFriends(); - } - - @override - void initState() { - _controller = AnimationController( - vsync: this, - duration: Duration(seconds: 3), - ); - super.initState(); - } - - void showCapsuleDot(bool isAvailable) { - isAvailable - ? Flushbar( - maxWidth: 210, - animationDuration: Duration(seconds: 1), - forwardAnimationCurve: Curves.easeOutCirc, - margin: EdgeInsets.fromLTRB(0, 0, 0, 0), - icon: Icon( - Ionicons.sparkles, - color: Colors.white, - size: 18, - ), - padding: EdgeInsets.fromLTRB(8, 8, 8, 8), - messageText: Align( - alignment: Alignment.centerLeft, - child: Text( - "Capsule disponible", - style: GoogleFonts.plusJakartaSans(color: Colors.grey, fontSize: 15), - ), - ), - flushbarStyle: FlushbarStyle.FLOATING, - flushbarPosition: FlushbarPosition.BOTTOM, - textDirection: Directionality.of(context), - borderRadius: BorderRadius.circular(1000), - borderWidth: 1, - borderColor: Colors.white.withOpacity(0.04), - duration: const Duration(minutes: 100), - leftBarIndicatorColor: Colors.transparent, - positionOffset: 20, - onTap: (_) { - Navigator.pop(context); - Navigator.pushNamed(context, '/post'); - }, - ).show(context) - : Flushbar( - maxWidth: 155, - animationDuration: Duration(seconds: 1), - forwardAnimationCurve: Curves.easeOutCirc, - margin: EdgeInsets.fromLTRB(0, 0, 0, 0), - icon: Lottie.asset( - 'assets/animations/LottieHourGlass.json', - width: 26, - fit: BoxFit.fill, - ), - padding: EdgeInsets.fromLTRB(8, 8, 8, 8), - messageText: Align( - alignment: Alignment.centerLeft, - child: CountdownTimer( - endTime: midnight.millisecondsSinceEpoch - 2 * 60 * 60 * 1000, - textStyle: GoogleFonts.plusJakartaSans(color: Colors.grey, fontSize: 15), - ), - ), - flushbarStyle: FlushbarStyle.FLOATING, - flushbarPosition: FlushbarPosition.BOTTOM, - textDirection: Directionality.of(context), - borderRadius: BorderRadius.circular(1000), - borderWidth: 1, - borderColor: Colors.white.withOpacity(0.04), - duration: const Duration(minutes: 100), - leftBarIndicatorColor: Colors.transparent, - positionOffset: 20, - onTap: (_) { - Navigator.pop(context); - }, - ).show(context); - } - - void checkAvailable() async { - print("test"); - var res = await MyApp.postViewModel.getAvailable(); - print(res); - ModalRoute? route = ModalRoute.of(context); - if (route != null) { - if (route.settings.name != '/flushbarRoute') { - print("yes"); - showCapsuleDot(res); - } - } - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: defaultPadding), - child: Container( - padding: EdgeInsets.symmetric(horizontal: defaultPadding), - width: double.infinity, - height: 100, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - flex: 1, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () async { - Navigator.of(context).push(routeAddFriend()); - }, - child: const Icon( - Icons.person_add_alt_1_rounded, - color: Colors.white, - size: 30, - ), - ), - ), - ConstrainedBox( - constraints: BoxConstraints(maxWidth: 200), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - ZoomTapAnimation( - enableLongTapRepeatEvent: false, - longTapRepeatDuration: const Duration(milliseconds: 100), - begin: 1.0, - onTap: () { - checkAvailable(); - }, - end: 0.97, - beginDuration: const Duration(milliseconds: 70), - endDuration: const Duration(milliseconds: 100), - beginCurve: Curves.decelerate, - endCurve: Curves.easeInOutSine, - child: Image( - image: AssetImage("assets/images/logo.png"), - height: 30, - )), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - if (!choice) { - setState(() { - choice = !choice; - actionSurBouton(); - }); - } - }, - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - if (choice) { - return Padding( - padding: const EdgeInsets.only(left: 8, top: 0, right: 8, bottom: 6), - child: AutoSizeText( - "Mes amis", - style: GoogleFonts.plusJakartaSans( - fontWeight: FontWeight.w500, fontSize: 16, color: Colors.white), - ), - ); - } else { - return Padding( - padding: const EdgeInsets.only(left: 8, top: 0, right: 8, bottom: 6), - child: AutoSizeText( - "Mes amis", - style: GoogleFonts.plusJakartaSans( - fontWeight: FontWeight.w300, fontSize: 16, color: unactiveFeed), - )); - } - }, - ), - ), - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - if (choice) { - setState(() { - choice = !choice; - actionSurBouton(); - }); - } - }, - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - if (choice) { - return Padding( - padding: const EdgeInsets.only(left: 8, top: 0, right: 8, bottom: 6), - child: AutoSizeText( - "Discovery", - style: GoogleFonts.plusJakartaSans( - fontWeight: FontWeight.w300, fontSize: 16, color: unactiveFeed), - )); - } else { - return Padding( - padding: const EdgeInsets.only(left: 8, top: 0, right: 8, bottom: 6), - child: AutoSizeText( - "Discovery", - style: GoogleFonts.plusJakartaSans( - fontWeight: FontWeight.w500, fontSize: 16, color: Colors.white), - )); - } - }, - ), - ), - ], - ), - ], - ), - ), - Flexible( - flex: 1, - child: GestureDetector( - onTap: () async { - await MyApp.userViewModel.updateUserCurrent(); - Navigator.of(context).push(routeProfile()); - }, - child: ClipOval( - child: SizedBox.fromSize( - // Image radius - child: FadeInImage.assetNetwork( - placeholder: 'assets/images/loadingPlaceholder.gif', - image: MyApp.userViewModel.userCurrent.pp, - width: 30, - )), - ), - ), - ) - ], - ), - ), - ); - } -} +import 'package:another_flushbar/flushbar.dart'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:ionicons/ionicons.dart'; +import 'package:lottie/lottie.dart'; +import 'package:zoom_tap_animation/zoom_tap_animation.dart'; + +import '../config/routes.dart'; +import 'package:timezone/timezone.dart' as tz; +import '../main.dart'; +import '../values/constants.dart'; + +class TopNavBarComponent extends StatefulWidget { + final Function(bool) callback; + + const TopNavBarComponent({Key? key, required this.callback}) + : super(key: key); + + @override + State createState() => _TopNavBarComponentState(); +} + +class _TopNavBarComponentState extends State + with TickerProviderStateMixin { + bool choice = true; + + bool isDismissed = true; + + final DateTime midnight = DateTime( + DateTime.now().year, DateTime.now().month, DateTime.now().day + 1); + + void actionSurBouton(bool choice) async { + widget.callback(choice); + } + + @override + void initState() { + super.initState(); + } + + Future showCapsuleDot() async { + // Get the timezone for France + final franceTimeZone = tz.getLocation('Europe/Paris'); + + // Get the current date and time in France timezone + var now = tz.TZDateTime.now(franceTimeZone); + + // Calculate the midnight time for the next day in France timezone + var midnight = + tz.TZDateTime(franceTimeZone, now.year, now.month, now.day + 1); + + bool res = await MyApp.postViewModel.getAvailable(); + if (res) { + Flushbar( + maxWidth: 210, + animationDuration: Duration(seconds: 1), + forwardAnimationCurve: Curves.easeOutCirc, + margin: EdgeInsets.fromLTRB(0, 0, 0, 0), + icon: Icon( + Ionicons.sparkles, + color: Colors.white, + size: 18, + ), + padding: EdgeInsets.fromLTRB(8, 8, 8, 8), + messageText: Align( + alignment: Alignment.centerLeft, + child: Text( + "Capsule disponible", + style: + GoogleFonts.plusJakartaSans(color: Colors.grey, fontSize: 15), + ), + ), + flushbarStyle: FlushbarStyle.FLOATING, + flushbarPosition: FlushbarPosition.BOTTOM, + textDirection: Directionality.of(context), + borderRadius: BorderRadius.circular(1000), + borderWidth: 1, + borderColor: Colors.white.withOpacity(0.04), + duration: const Duration(minutes: 100), + leftBarIndicatorColor: Colors.transparent, + positionOffset: 20, + onTap: (_) { + Navigator.pop(context); + Navigator.pushNamed(context, '/post'); + }, + ).show(context).then((value) { + setState(() { + isDismissed = !isDismissed; + }); + }); + } else { + Flushbar( + maxWidth: 155, + animationDuration: Duration(seconds: 1), + forwardAnimationCurve: Curves.easeOutCirc, + margin: EdgeInsets.fromLTRB(0, 0, 0, 0), + icon: Lottie.asset( + 'assets/animations/LottieHourGlass.json', + width: 26, + fit: BoxFit.fill, + ), + padding: EdgeInsets.fromLTRB(8, 8, 8, 8), + messageText: Align( + alignment: Alignment.centerLeft, + child: CountdownTimer( + endTime: midnight.millisecondsSinceEpoch, + textStyle: + GoogleFonts.plusJakartaSans(color: Colors.grey, fontSize: 15), + ), + ), + flushbarStyle: FlushbarStyle.FLOATING, + flushbarPosition: FlushbarPosition.BOTTOM, + textDirection: Directionality.of(context), + borderRadius: BorderRadius.circular(1000), + borderWidth: 1, + borderColor: Colors.white.withOpacity(0.04), + duration: const Duration(minutes: 100), + leftBarIndicatorColor: Colors.transparent, + positionOffset: 20, + onTap: (_) {}, + ).show(context).then((value) { + setState(() { + isDismissed = !isDismissed; + }); + }); + } + } + + void checkAvailable() async { + if (isDismissed) { + showCapsuleDot(); + setState(() { + isDismissed = !isDismissed; + }); + } + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: defaultPadding), + child: Container( + padding: EdgeInsets.symmetric(horizontal: defaultPadding), + width: double.infinity, + height: 100, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + flex: 1, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () async { + Navigator.of(context).push(routeAddFriend()); + }, + child: const Icon( + Icons.person_add_alt_1_rounded, + color: Colors.white, + size: 30, + ), + ), + ), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 200), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ZoomTapAnimation( + enableLongTapRepeatEvent: false, + longTapRepeatDuration: const Duration(milliseconds: 100), + begin: 1.0, + onTap: () { + checkAvailable(); + }, + end: 0.97, + beginDuration: const Duration(milliseconds: 70), + endDuration: const Duration(milliseconds: 100), + beginCurve: Curves.decelerate, + endCurve: Curves.easeInOutSine, + child: Image( + image: AssetImage("assets/images/logo.png"), + height: 30, + )), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + if (!choice) { + setState(() { + choice = !choice; + actionSurBouton(false); + }); + } + }, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints constraints) { + if (choice) { + return Padding( + padding: const EdgeInsets.only( + left: 8, top: 0, right: 8, bottom: 6), + child: AutoSizeText( + "Mes amis", + style: GoogleFonts.plusJakartaSans( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.white), + ), + ); + } else { + return Padding( + padding: const EdgeInsets.only( + left: 8, top: 0, right: 8, bottom: 6), + child: AutoSizeText( + "Mes amis", + style: GoogleFonts.plusJakartaSans( + fontWeight: FontWeight.w300, + fontSize: 16, + color: unactiveFeed), + )); + } + }, + ), + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + if (choice) { + setState(() { + choice = !choice; + actionSurBouton(true); + }); + } + }, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints constraints) { + if (choice) { + return Padding( + padding: const EdgeInsets.only( + left: 8, top: 0, right: 8, bottom: 6), + child: AutoSizeText( + "Discovery", + style: GoogleFonts.plusJakartaSans( + fontWeight: FontWeight.w300, + fontSize: 16, + color: unactiveFeed), + )); + } else { + return Padding( + padding: const EdgeInsets.only( + left: 8, top: 0, right: 8, bottom: 6), + child: AutoSizeText( + "Discovery", + style: GoogleFonts.plusJakartaSans( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.white), + )); + } + }, + ), + ), + ], + ), + ], + ), + ), + Flexible( + flex: 1, + child: GestureDetector( + onTap: () async { + await MyApp.userViewModel.updateUserCurrent(); + Navigator.of(context).push(routeProfile()); + }, + child: ClipOval( + child: SizedBox.fromSize( + // Image radius + child: FadeInImage.assetNetwork( + placeholder: 'assets/images/loadingPlaceholder.gif', + image: MyApp.userViewModel.userCurrent.pp, + width: 30, + )), + ), + ), + ) + ], + ), + ), + ); + } +} diff --git a/Sources/justMUSIC/lib/main.dart b/Sources/justMUSIC/lib/main.dart index 577a1d4..83fabcd 100644 --- a/Sources/justMUSIC/lib/main.dart +++ b/Sources/justMUSIC/lib/main.dart @@ -11,20 +11,23 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:justmusic/screens/add_friend_screen.dart'; import 'package:justmusic/screens/explanations_screen.dart'; import 'package:justmusic/screens/feed_screen.dart'; +import 'package:justmusic/screens/loading_screen.dart'; import 'package:justmusic/screens/login_screen.dart'; import 'package:justmusic/screens/launching_rocker_screen.dart'; import 'package:justmusic/screens/post_screen.dart'; import 'package:justmusic/screens/profile_screen.dart'; import 'package:justmusic/screens/registration_screen.dart'; import 'package:justmusic/screens/welcome_screen.dart'; -import 'package:justmusic/values/constants.dart'; +import 'package:justmusic/view_model/CommentViewModel.dart'; import 'package:justmusic/view_model/MusicViewModel.dart'; import 'package:justmusic/view_model/PostViewModel.dart'; import 'package:justmusic/view_model/UserViewModel.dart'; import 'package:justmusic/model/User.dart' as userJustMusic; import 'firebase_options.dart'; +import 'package:timezone/data/latest.dart' as tz; Future main() async { + tz.initializeTimeZones(); WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, @@ -40,6 +43,7 @@ class MyApp extends StatefulWidget { static MusicViewModel musicViewModel = MusicViewModel(); static PostViewModel postViewModel = PostViewModel(); static AudioPlayer audioPlayer = AudioPlayer(); + static CommentViewModel commentViewModel = CommentViewModel(); const MyApp({super.key}); @@ -64,7 +68,7 @@ class _MyAppState extends State { return null; } else { MyApp.userViewModel.userCurrent = - (await (MyApp.userViewModel.getUser(user.uid)))!; + (await (MyApp.userViewModel.getUser(user.uid)))!; userCurrent = Stream.value(MyApp.userViewModel.userCurrent); print('User is signed in!'); } @@ -100,46 +104,40 @@ class _MyAppState extends State { }, debugShowCheckedModeBanner: false, theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. primarySwatch: Colors.blue, ), - home: FirebaseAuth.instance.currentUser != null - ? StreamBuilder( - stream: userCurrent, - initialData: null, - builder: (context, snapshot) { - if (snapshot.hasData) { - print("hasdata"); - - return AnimatedSwitcher( - duration: Duration(milliseconds: 1000), - transitionBuilder: (child, animation) { - return FadeTransition( - opacity: animation, child: child); - }, - child: FeedScreen(), - ); - } else { - return Scaffold( - backgroundColor: bgColor, - body: Center( - child: Image( - image: AssetImage("assets/images/logo.png"), - width: 130, - ), - ), - ); - } - }) - : WellcomeScreen()); + home: StreamBuilder( + stream: FirebaseAuth.instance.authStateChanges(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return LoadingScreen(); + } else if (snapshot.hasData) { + return FutureBuilder( + future: MyApp.userViewModel.getUser(snapshot.data!.uid), + builder: (context, userSnapshot) { + if (userSnapshot.connectionState == + ConnectionState.waiting) { + return LoadingScreen(); + } else if (userSnapshot.hasData) { + MyApp.userViewModel.userCurrent = userSnapshot.data!; + return AnimatedSwitcher( + duration: Duration(milliseconds: 1000), + transitionBuilder: (child, animation) { + return FadeTransition( + opacity: animation, child: child); + }, + child: FeedScreen(), + ); + } else { + return WellcomeScreen(); + } + }, + ); + } else { + return WellcomeScreen(); + } + }, + )); }, designSize: Size(390, 844), ); diff --git a/Sources/justMUSIC/lib/model/mapper/CommentMapper.dart b/Sources/justMUSIC/lib/model/mapper/CommentMapper.dart index cfa69f7..ae4b96b 100644 --- a/Sources/justMUSIC/lib/model/mapper/CommentMapper.dart +++ b/Sources/justMUSIC/lib/model/mapper/CommentMapper.dart @@ -5,13 +5,10 @@ import '../Comment.dart'; import '../User.dart'; class CommentMapper { - static Future toModel(DocumentSnapshot> snapshot) async { + static Future toModel( + DocumentSnapshot> snapshot) async { final data = snapshot.data(); User? user = await MyApp.userViewModel.getUser(data?['user_id']); - return Comment( - snapshot.id, - user!, - data?["text"], - data?["date"]); + return Comment(snapshot.id, user!, data?["text"], data?["date"].toDate()); } } diff --git a/Sources/justMUSIC/lib/screens/detail_post_screen.dart b/Sources/justMUSIC/lib/screens/detail_post_screen.dart index 93b13c3..140e42d 100644 --- a/Sources/justMUSIC/lib/screens/detail_post_screen.dart +++ b/Sources/justMUSIC/lib/screens/detail_post_screen.dart @@ -1,451 +1,640 @@ -import 'dart:async'; - -import 'package:flutter/Material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:google_fonts/google_fonts.dart'; - -import 'package:text_scroll/text_scroll.dart'; -import 'package:zoom_tap_animation/zoom_tap_animation.dart'; - -import '../components/button_play_component.dart'; -import '../components/comment_component.dart'; - -import '../main.dart'; -import '../model/Post.dart'; -import '../values/constants.dart'; - -class DetailPostScreen extends StatefulWidget { - final Post post; - const DetailPostScreen({super.key, required this.post}); - - @override - State createState() => _DetailPostScreenState(); -} - -class _DetailPostScreenState extends State { - TextEditingController _textController = TextEditingController(); - late FocusNode myFocusNode; - late StreamSubscription keyboardSubscription; - Future resetFullScreen() async { - await SystemChannels.platform.invokeMethod( - 'SystemChrome.restoreSystemUIOverlays', - ); - } - - bool choice = false; - DateTime today = DateTime.now(); - - void switchChoice() { - setState(() { - choice = !choice; - }); - } - - @override - void dispose() { - MyApp.audioPlayer.release(); - myFocusNode.dispose(); - super.dispose(); - } - - @override - void initState() { - print("post: ${widget.post.date.toString()}"); - print("ajrd: ${DateTime.now().toString()}"); - myFocusNode = FocusNode(); - var keyboardVisibilityController = KeyboardVisibilityController(); - print('Keyboard visibility direct query: ${keyboardVisibilityController.isVisible}'); - - super.initState(); - - keyboardSubscription = keyboardVisibilityController.onChange.listen((bool visible) { - if (!visible) { - myFocusNode.unfocus(); - } - }); - } - - final ScrollController _scrollController = ScrollController(); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - FocusScopeNode currentFocus = FocusScope.of(context); - if (!currentFocus.hasPrimaryFocus) { - currentFocus.unfocus(); - resetFullScreen(); - } - }, - child: Container( - height: 760.h, - child: Column( - children: [ - Expanded( - child: Stack( - children: [ - ScrollConfiguration( - behavior: MyBehavior(), - child: SingleChildScrollView( - controller: _scrollController, - physics: AlwaysScrollableScrollPhysics(), - child: Stack( - clipBehavior: Clip.hardEdge, - children: [ - Align( - alignment: Alignment.topCenter, - child: Container( - height: 400, - width: double.infinity, - child: FadeInImage.assetNetwork( - placeholder: "assets/images/loadingPlaceholder.gif", - image: choice ? widget.post.selfie! : widget.post.music.cover!, - width: double.infinity, - fit: BoxFit.cover, - ), - )), - Column( - children: [ - Container( - height: 200, - margin: EdgeInsets.only(top: 230), - width: double.infinity, - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Colors.transparent, bgModal], - stops: [0, 0.8]), - ), - child: Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 10), - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.only(right: 10), - child: choice - ? Padding( - padding: const EdgeInsets.all(4), - child: ClipOval( - child: SizedBox.fromSize( - // Image radius - child: Image( - image: NetworkImage(widget.post.user.pp), - width: 45, - ), - ), - ), - ) - : widget.post.music.previewUrl != null - ? ButtonPlayComponent(music: widget.post.music) - : Container(), - ), - Flexible( - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: ScrollConfiguration( - behavior: ScrollBehavior().copyWith(scrollbars: false), - child: TextScroll( - choice - ? widget.post.user.pseudo - : widget.post.music.title!, - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w800, - fontSize: 22), - mode: TextScrollMode.endless, - pauseBetween: Duration(milliseconds: 500), - velocity: Velocity(pixelsPerSecond: Offset(20, 0)), - ))), - Padding( - padding: const EdgeInsets.only(left: 20.0), - child: choice - ? DateTime(today.year, today.month, today.day) - .isAtSameMomentAs(DateTime(widget.post.date.year, - widget.post.date.month, widget.post.date.day)) - ? Text( - "Aujourd'hui, ${widget.post.date.hour}:${widget.post.date.minute}", - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w900, - fontSize: 18), - ) - : Text( - "hier, ${widget.post.date.hour}:${widget.post.date.minute}", - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w900, - fontSize: 18), - ) - : Text( - widget.post.music.date.toString(), - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w900, - fontSize: 18), - ), - ) - ], - ), - ), - choice - ? widget.post.location.item2 != null - ? Text( - "${widget.post.location.item1}, ${widget.post.location.item2}", - style: GoogleFonts.plusJakartaSans( - color: Colors.white.withOpacity(0.5), - fontWeight: FontWeight.w400, - fontSize: 15), - ) - : Text( - "", - style: GoogleFonts.plusJakartaSans( - color: Colors.white.withOpacity(0.4), - fontWeight: FontWeight.w300, - fontSize: 13), - ) - : ScrollConfiguration( - behavior: ScrollBehavior().copyWith(scrollbars: false), - child: TextScroll(widget.post.music.artists.first.name!, - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w500, - fontSize: 17), - mode: TextScrollMode.endless, - pauseBetween: Duration(milliseconds: 500), - velocity: Velocity(pixelsPerSecond: Offset(20, 0))), - ) - ], - ), - ), - ], - ), - ), - ), - widget.post.description != null - ? Align( - alignment: Alignment.bottomLeft, - child: Padding( - padding: const EdgeInsets.fromLTRB(50, 35, 50, 35), - child: Text( - widget.post.description!, - textAlign: TextAlign.left, - style: GoogleFonts.plusJakartaSans( - height: 1, - color: Colors.white, - fontWeight: FontWeight.w400, - fontSize: 14), - ), - ), - ) - : Container( - height: 30, - ), - Container( - width: double.infinity, - decoration: const BoxDecoration( - color: bgAppBar, - border: Border( - top: BorderSide( - color: Color(0xFF262626), // Couleur de la bordure - width: 1.0, // Épaisseur de la bordure - ), - ), - ), - child: Column( - children: [ - Padding( - padding: EdgeInsets.symmetric(vertical: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - SvgPicture.asset("assets/images/heart.svg", semanticsLabel: 'Like Logo'), - GestureDetector( - onTap: () { - myFocusNode.requestFocus(); - }, - child: SvgPicture.asset("assets/images/chat.svg", - semanticsLabel: 'Chat Logo')), - SvgPicture.asset("assets/images/add.svg", - semanticsLabel: 'Add playlist Logo'), - SvgPicture.asset("assets/images/save.svg", semanticsLabel: 'Save Logo'), - SvgPicture.asset("assets/images/report.svg", - semanticsLabel: 'Report Logo'), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(15.0), - child: RichText( - text: TextSpan( - text: "3", - style: GoogleFonts.plusJakartaSans( - color: Colors.white, fontWeight: FontWeight.w800), - children: [ - TextSpan( - text: " commentaires", - style: GoogleFonts.plusJakartaSans( - color: Colors.white, fontWeight: FontWeight.w400), - ) - ])), - ), - Padding( - padding: EdgeInsets.fromLTRB(20, 0, 20, 20), - child: Wrap( - runSpacing: 13, - children: [ - CommentComponent(), - CommentComponent(), - CommentComponent(), - ], - ), - ) - ], - ), - ), - ], - ), - widget.post.selfie != null - ? Align( - alignment: Alignment.topRight, - child: ZoomTapAnimation( - onTap: () { - if (widget.post.selfie != null) { - switchChoice(); - } - }, - enableLongTapRepeatEvent: false, - longTapRepeatDuration: const Duration(milliseconds: 100), - begin: 1.0, - end: 0.96, - beginDuration: const Duration(milliseconds: 70), - endDuration: const Duration(milliseconds: 100), - beginCurve: Curves.decelerate, - endCurve: Curves.easeInOutSine, - child: Container( - margin: EdgeInsets.all(20), - width: 120, - height: 120, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all(width: 4, color: Colors.white)), - child: ClipRRect( - borderRadius: BorderRadius.circular(15), - // implement image - child: Image( - image: NetworkImage( - choice ? widget.post.music.cover! : widget.post.selfie!), - fit: BoxFit.cover, - )), - ), - ), - ) - : Container() - ], - ), - )), - Align( - alignment: Alignment.topCenter, - child: Container( - height: 50, - width: double.infinity, - color: Colors.transparent, - child: Align( - alignment: Alignment.topCenter, - child: Container( - margin: EdgeInsets.only(top: 10), - width: 60, - height: 5, - decoration: BoxDecoration( - color: Colors.white.withOpacity(0.6), borderRadius: BorderRadius.circular(20))), - ), - ), - ), - ], - ), - ), - Padding( - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), - child: Container( - height: 70, - width: double.infinity, - decoration: BoxDecoration( - border: Border(top: BorderSide(color: grayColor, width: 2)), color: textFieldMessage), - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Row( - children: [ - ClipOval( - child: SizedBox.fromSize( - // Image radius - child: Image.network( - MyApp.userViewModel.userCurrent.pp, - width: 45, - ), - ), - ), - SizedBox( - width: 10, - ), - Expanded( - child: TextField( - keyboardAppearance: Brightness.dark, - controller: _textController, - focusNode: myFocusNode, - cursorColor: primaryColor, - keyboardType: TextInputType.emailAddress, - style: GoogleFonts.plusJakartaSans(color: Colors.white), - decoration: InputDecoration( - suffixIcon: Icon( - Icons.send, - color: grayText, - size: 20, - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(width: 1, color: grayText), - borderRadius: BorderRadius.all(Radius.circular(100))), - contentPadding: EdgeInsets.only(top: 0, bottom: 0, left: 20, right: 20), - fillColor: bgModal, - filled: true, - focusColor: Color.fromRGBO(255, 255, 255, 0.30), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(width: 1, color: grayText), - borderRadius: BorderRadius.all(Radius.circular(100))), - hintText: 'Ajoutez une réponse...', - hintStyle: GoogleFonts.plusJakartaSans(color: grayText)), - ), - ) - ], - ), - ), - )), - ), - ], - ), - )); - } -} - -class MyBehavior extends ScrollBehavior { - @override - Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) { - return child; - } -} +import 'dart:async'; + +import 'package:flutter/Material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import 'package:text_scroll/text_scroll.dart'; +import 'package:zoom_tap_animation/zoom_tap_animation.dart'; + +import '../components/button_play_component.dart'; +import '../components/comment_component.dart'; + +import '../main.dart'; +import '../model/Post.dart'; +import '../model/Comment.dart'; +import '../values/constants.dart'; + +class DetailPostScreen extends StatefulWidget { + final Post post; + + const DetailPostScreen({super.key, required this.post}); + + @override + State createState() => _DetailPostScreenState(); +} + +class _DetailPostScreenState extends State { + TextEditingController _textController = TextEditingController(); + late FocusNode myFocusNode; + late StreamSubscription keyboardSubscription; + + Future resetFullScreen() async { + await SystemChannels.platform.invokeMethod( + 'SystemChrome.restoreSystemUIOverlays', + ); + } + + bool choice = false; + DateTime today = DateTime.now(); + + void switchChoice() { + setState(() { + choice = !choice; + }); + } + + @override + void dispose() { + MyApp.audioPlayer.release(); + myFocusNode.dispose(); + super.dispose(); + } + + @override + void initState() { + print("post: ${widget.post.date.toString()}"); + print("ajrd: ${DateTime.now().toString()}"); + myFocusNode = FocusNode(); + var keyboardVisibilityController = KeyboardVisibilityController(); + print( + 'Keyboard visibility direct query: ${keyboardVisibilityController.isVisible}'); + + super.initState(); + + keyboardSubscription = + keyboardVisibilityController.onChange.listen((bool visible) { + if (!visible) { + myFocusNode.unfocus(); + } + }); + } + + final ScrollController _scrollController = ScrollController(); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus) { + currentFocus.unfocus(); + resetFullScreen(); + } + }, + child: Container( + height: 760.h, + child: Column( + children: [ + Expanded( + child: Stack( + children: [ + ScrollConfiguration( + behavior: MyBehavior(), + child: SingleChildScrollView( + controller: _scrollController, + physics: AlwaysScrollableScrollPhysics(), + child: Stack( + clipBehavior: Clip.hardEdge, + children: [ + Align( + alignment: Alignment.topCenter, + child: Container( + height: 400, + width: double.infinity, + child: FadeInImage.assetNetwork( + placeholder: + "assets/images/loadingPlaceholder.gif", + image: choice + ? widget.post.selfie! + : widget.post.music.cover!, + width: double.infinity, + fit: BoxFit.cover, + ), + ), + ), + Column( + children: [ + Container( + height: 200, + margin: EdgeInsets.only(top: 230), + width: double.infinity, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Colors.transparent, bgModal], + stops: [0, 0.8], + ), + ), + child: Padding( + padding: + const EdgeInsets.fromLTRB(20, 0, 20, 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: + const EdgeInsets.only(right: 10), + child: choice + ? Padding( + padding: + const EdgeInsets.all(4), + child: ClipOval( + child: SizedBox.fromSize( + // Image radius + child: Image( + image: NetworkImage( + widget.post.user.pp), + width: 45, + ), + ), + ), + ) + : widget.post.music.previewUrl != + null + ? ButtonPlayComponent( + music: widget.post.music) + : Container(), + ), + Flexible( + child: Column( + mainAxisAlignment: + MainAxisAlignment.end, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Flexible( + child: Row( + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + Expanded( + child: ScrollConfiguration( + behavior: ScrollBehavior() + .copyWith( + scrollbars: + false), + child: TextScroll( + choice + ? widget.post.user + .pseudo + : widget.post.music + .title!, + style: GoogleFonts + .plusJakartaSans( + height: 1, + color: Colors.white, + fontWeight: + FontWeight.w800, + fontSize: 22, + ), + mode: TextScrollMode + .endless, + pauseBetween: Duration( + milliseconds: 500), + velocity: Velocity( + pixelsPerSecond: + Offset(20, 0)), + ), + ), + ), + Padding( + padding: + const EdgeInsets.only( + left: 20.0), + child: choice + ? DateTime( + today.year, + today.month, + today.day) + .isAtSameMomentAs( + DateTime( + widget.post.date + .year, + widget.post.date + .month, + widget.post.date + .day, + ), + ) + ? Text( + "Aujourd'hui, ${widget.post.date.hour}:${widget.post.date.minute}", + style: GoogleFonts + .plusJakartaSans( + height: 1, + color: Colors + .white, + fontWeight: + FontWeight + .w900, + fontSize: 18, + ), + ) + : Text( + "hier, ${widget.post.date.hour}:${widget.post.date.minute}", + style: GoogleFonts + .plusJakartaSans( + height: 1, + color: Colors + .white, + fontWeight: + FontWeight + .w900, + fontSize: 18, + ), + ) + : Text( + widget + .post.music.date + .toString(), + style: GoogleFonts + .plusJakartaSans( + height: 1, + color: + Colors.white, + fontWeight: + FontWeight + .w900, + fontSize: 18, + ), + ), + ), + ], + ), + ), + choice + ? widget.post.location.item2 != + null + ? Text( + "${widget.post.location.item1}, ${widget.post.location.item2}", + style: GoogleFonts + .plusJakartaSans( + color: Colors.white + .withOpacity(0.5), + fontWeight: + FontWeight.w400, + fontSize: 15, + ), + ) + : Text( + "", + style: GoogleFonts + .plusJakartaSans( + color: Colors.white + .withOpacity(0.4), + fontWeight: + FontWeight.w300, + fontSize: 13, + ), + ) + : ScrollConfiguration( + behavior: ScrollBehavior() + .copyWith( + scrollbars: false), + child: TextScroll( + widget.post.music.artists + .first.name!, + style: GoogleFonts + .plusJakartaSans( + height: 1, + color: Colors.white, + fontWeight: + FontWeight.w500, + fontSize: 17, + ), + mode: TextScrollMode + .endless, + pauseBetween: Duration( + milliseconds: 500), + velocity: Velocity( + pixelsPerSecond: + Offset(20, 0)), + ), + ), + ], + ), + ), + ], + ), + ), + ), + widget.post.description != null + ? Align( + alignment: Alignment.bottomLeft, + child: Padding( + padding: const EdgeInsets.fromLTRB( + 50, 35, 50, 35), + child: Text( + widget.post.description!, + textAlign: TextAlign.left, + style: GoogleFonts.plusJakartaSans( + height: 1, + color: Colors.white, + fontWeight: FontWeight.w400, + fontSize: 14, + ), + ), + ), + ) + : Container( + height: 30, + ), + Container( + width: double.infinity, + decoration: const BoxDecoration( + color: bgAppBar, + border: Border( + top: BorderSide( + color: Color(0xFF262626), + width: 1.0, + ), + ), + ), + child: Column( + children: [ + Padding( + padding: + EdgeInsets.symmetric(vertical: 20), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + SvgPicture.asset( + "assets/images/heart.svg", + semanticsLabel: 'Like Logo'), + GestureDetector( + onTap: () { + myFocusNode.requestFocus(); + }, + child: SvgPicture.asset( + "assets/images/chat.svg", + semanticsLabel: 'Chat Logo'), + ), + SvgPicture.asset( + "assets/images/add.svg", + semanticsLabel: + 'Add playlist Logo'), + SvgPicture.asset( + "assets/images/save.svg", + semanticsLabel: 'Save Logo'), + SvgPicture.asset( + "assets/images/report.svg", + semanticsLabel: 'Report Logo'), + ], + ), + ), + FutureBuilder>( + future: MyApp.commentViewModel + .getCommentsByPostId(widget.post.id), + builder: (BuildContext context, + AsyncSnapshot> + snapshot) { + if (snapshot.hasData) { + print("test:"); + return Column( + children: [ + snapshot.data!.length > 0 + ? Padding( + padding: + const EdgeInsets.all( + 15.0), + child: RichText( + text: TextSpan( + text: snapshot + .data!.length + .toString(), + style: GoogleFonts + .plusJakartaSans( + color: Colors.white, + fontWeight: + FontWeight.w800, + ), + children: [ + TextSpan( + text: snapshot + .data! + .length > + 1 + ? " commentaires" + : " commentaire", + style: GoogleFonts + .plusJakartaSans( + color: Colors + .white, + fontWeight: + FontWeight + .w400, + ), + ), + ], + ), + ), + ) + : Container(), + snapshot.data!.length > 0 + ? Padding( + padding: const EdgeInsets + .fromLTRB( + 20, 0, 20, 20), + child: ListView.builder( + shrinkWrap: true, + physics: + NeverScrollableScrollPhysics(), + itemCount: snapshot + .data?.length, + itemBuilder: + (BuildContext + context, + int index) { + return CommentComponent( + comment: snapshot + .data![ + index]); + }, + ), + ) + : Container(), + ], + ); + } else { + return Container( + child: Center( + child: + CupertinoActivityIndicator(), + ), + ); + } + }, + ), + ], + ), + ), + ], + ), + widget.post.selfie != null + ? Align( + alignment: Alignment.topRight, + child: ZoomTapAnimation( + onTap: () { + if (widget.post.selfie != null) { + switchChoice(); + } + }, + enableLongTapRepeatEvent: false, + longTapRepeatDuration: + const Duration(milliseconds: 100), + begin: 1.0, + end: 0.96, + beginDuration: + const Duration(milliseconds: 70), + endDuration: + const Duration(milliseconds: 100), + beginCurve: Curves.decelerate, + endCurve: Curves.easeInOutSine, + child: Container( + margin: EdgeInsets.all(20), + width: 120, + height: 120, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + width: 4, color: Colors.white), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(15), + // implementer l'image + child: Image( + image: NetworkImage(choice + ? widget.post.music.cover! + : widget.post.selfie!), + fit: BoxFit.cover, + ), + ), + ), + ), + ) + : Container(), + ], + ), + ), + ), + Align( + alignment: Alignment.topCenter, + child: Container( + height: 50, + width: double.infinity, + color: Colors.transparent, + child: Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only(top: 10), + width: 60, + height: 5, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.6), + borderRadius: BorderRadius.circular(20), + ), + ), + ), + ), + ), + ], + ), + ), + Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom), + child: Container( + height: 70, + width: double.infinity, + decoration: BoxDecoration( + border: Border(top: BorderSide(color: grayColor, width: 2)), + color: textFieldMessage, + ), + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + ClipOval( + child: SizedBox.fromSize( + // Rayon de l'image + child: Image.network( + MyApp.userViewModel.userCurrent.pp, + width: 45, + ), + ), + ), + SizedBox(width: 10), + Expanded( + child: TextField( + keyboardAppearance: Brightness.dark, + controller: _textController, + focusNode: myFocusNode, + onSubmitted: (value) async { + if (value.isNotEmpty) { + await MyApp.commentViewModel + .addComment(value, widget.post.id); + } + setState(() { + _textController.clear(); + }); + }, + cursorColor: primaryColor, + keyboardType: TextInputType.emailAddress, + style: GoogleFonts.plusJakartaSans( + color: Colors.white), + decoration: InputDecoration( + suffixIcon: Icon( + Icons.send, + color: grayText, + size: 20, + ), + focusedBorder: OutlineInputBorder( + borderSide: + BorderSide(width: 1, color: grayText), + borderRadius: + BorderRadius.all(Radius.circular(100)), + ), + contentPadding: EdgeInsets.only( + top: 0, bottom: 0, left: 20, right: 20), + fillColor: bgModal, + filled: true, + focusColor: Color.fromRGBO(255, 255, 255, 0.30), + enabledBorder: OutlineInputBorder( + borderSide: + BorderSide(width: 1, color: grayText), + borderRadius: + BorderRadius.all(Radius.circular(100)), + ), + hintText: 'Ajoutez une réponse...', + hintStyle: + GoogleFonts.plusJakartaSans(color: grayText), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} + +class MyBehavior extends ScrollBehavior { + @override + Widget buildOverscrollIndicator( + BuildContext context, Widget child, ScrollableDetails details) { + return child; + } +} diff --git a/Sources/justMUSIC/lib/screens/feed_screen.dart b/Sources/justMUSIC/lib/screens/feed_screen.dart index d52babe..ff77931 100644 --- a/Sources/justMUSIC/lib/screens/feed_screen.dart +++ b/Sources/justMUSIC/lib/screens/feed_screen.dart @@ -1,16 +1,13 @@ import 'dart:async'; - -import 'package:another_flushbar/flushbar.dart'; import 'package:circular_reveal_animation/circular_reveal_animation.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:ionicons/ionicons.dart'; import 'package:justmusic/main.dart'; -import 'package:lottie/lottie.dart'; - +import 'package:justmusic/main.dart'; +import 'package:tuple/tuple.dart'; import '../components/post_component.dart'; import '../components/top_nav_bar_component.dart'; import '../model/Post.dart'; @@ -24,7 +21,8 @@ class FeedScreen extends StatefulWidget { State createState() => _FeedScreenState(); } -class _FeedScreenState extends State with SingleTickerProviderStateMixin { +class _FeedScreenState extends State + with SingleTickerProviderStateMixin { late AnimationController animationController; late Animation animation; late List friendFeed; @@ -32,18 +30,14 @@ class _FeedScreenState extends State with SingleTickerProviderStateM late List discoveryFeed; late List displayFeed; - final DateTime midnight = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day + 1); bool isDismissed = true; + bool choiceFeed = false; @override void initState() { super.initState(); - - MyApp.postViewModel.getPostsFriends(); friendFeed = MyApp.postViewModel.postsFriends; - MyApp.postViewModel.getBestPosts(); discoveryFeed = MyApp.postViewModel.bestPosts; - displayFeed = []; animationController = AnimationController( vsync: this, duration: Duration(milliseconds: 400), @@ -55,98 +49,14 @@ class _FeedScreenState extends State with SingleTickerProviderStateM animationController.forward(); } - Future showCapsuleDot() async { - bool res = await MyApp.postViewModel.getAvailable(); - if (isDismissed) { - if (res) { - setState(() { - isDismissed = !isDismissed; - }); - Flushbar( - maxWidth: 210, - animationDuration: Duration(seconds: 1), - forwardAnimationCurve: Curves.easeOutCirc, - margin: EdgeInsets.fromLTRB(0, 0, 0, 0), - icon: Icon( - Ionicons.sparkles, - color: Colors.white, - size: 18, - ), - padding: EdgeInsets.fromLTRB(8, 8, 8, 8), - messageText: Align( - alignment: Alignment.centerLeft, - child: Text( - "Capsule disponible", - style: GoogleFonts.plusJakartaSans(color: Colors.grey, fontSize: 15), - ), - ), - flushbarStyle: FlushbarStyle.FLOATING, - flushbarPosition: FlushbarPosition.BOTTOM, - textDirection: Directionality.of(context), - borderRadius: BorderRadius.circular(1000), - borderWidth: 1, - isDismissible: false, - borderColor: Colors.white.withOpacity(0.04), - duration: const Duration(minutes: 100), - leftBarIndicatorColor: Colors.transparent, - positionOffset: 20, - onTap: (_) { - Navigator.pop(context); - Navigator.pushNamed(context, '/post'); - }, - ).show(context).then((value) { - isDismissed = !isDismissed; - }); - } else { - setState(() { - isDismissed = !isDismissed; - }); - Flushbar( - maxWidth: 155, - animationDuration: Duration(seconds: 1), - isDismissible: false, - forwardAnimationCurve: Curves.easeOutCirc, - margin: EdgeInsets.fromLTRB(0, 0, 0, 0), - icon: Lottie.asset( - 'assets/animations/LottieHourGlass.json', - width: 26, - fit: BoxFit.fill, - ), - padding: EdgeInsets.fromLTRB(8, 8, 8, 8), - messageText: Align( - alignment: Alignment.centerLeft, - child: CountdownTimer( - endTime: midnight.millisecondsSinceEpoch - 2 * 60 * 60 * 1000, - textStyle: GoogleFonts.plusJakartaSans(color: Colors.grey, fontSize: 15), - ), - ), - flushbarStyle: FlushbarStyle.FLOATING, - flushbarPosition: FlushbarPosition.BOTTOM, - textDirection: Directionality.of(context), - borderRadius: BorderRadius.circular(1000), - borderWidth: 1, - borderColor: Colors.white.withOpacity(0.04), - duration: const Duration(minutes: 100), - leftBarIndicatorColor: Colors.transparent, - positionOffset: 20, - onTap: (_) {}, - ).show(context).then((value) { - { - setState(() { - isDismissed = !isDismissed; - }); - } - }); - } - } - } - Future _refresh() async { - print("refresh"); - discoveryFeed = await MyApp.postViewModel.getBestPosts(); - setState(() { - displayFeed = discoveryFeed.reversed.toList(); - }); + if (choiceFeed) { + await MyApp.postViewModel.getBestPosts(); + setState(() {}); + } else { + await MyApp.postViewModel.getPostsFriends(); + setState(() {}); + } } void changeFeed(bool choice) { @@ -156,14 +66,14 @@ class _FeedScreenState extends State with SingleTickerProviderStateM animationController.reset(); displayFeed = MyApp.postViewModel.postsFriends.reversed.toList(); animationController.forward(); - print(displayFeed.length); + choiceFeed = false; }); } else { setState(() { animationController.reset(); displayFeed = MyApp.postViewModel.bestPosts.reversed.toList(); - print(displayFeed.length); animationController.forward(); + choiceFeed = true; }); } } @@ -178,18 +88,32 @@ class _FeedScreenState extends State with SingleTickerProviderStateM isScrollControlled: true, context: context, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20))), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), topRight: Radius.circular(20))), builder: ((BuildContext context) { return ClipRRect( - borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), topRight: Radius.circular(20)), child: DetailPostScreen(post: displayFeed[index])); }), ); } + _fetchData() async { + friendFeed = await MyApp.postViewModel.getPostsFriends(); + discoveryFeed = await MyApp.postViewModel.getBestPosts(); + return Tuple2(friendFeed, displayFeed); + } + @override Widget build(BuildContext context) { - showCapsuleDot(); + if (choiceFeed) { + displayFeed = MyApp.postViewModel.postsFriends.reversed.toList(); + } else { + displayFeed = MyApp.postViewModel.bestPosts.reversed.toList(); + } + _fetchData(); + return Scaffold( resizeToAvoidBottomInset: true, backgroundColor: bgColor, @@ -203,16 +127,21 @@ class _FeedScreenState extends State with SingleTickerProviderStateM Container( decoration: const BoxDecoration( image: DecorationImage( - image: AssetImage("assets/images/empty_bg.png"), fit: BoxFit.cover, opacity: 0.3), + image: AssetImage("assets/images/empty_bg.png"), + fit: BoxFit.cover, + opacity: 0.3), ), child: Padding( - padding: EdgeInsets.only(top: 140.h, left: defaultPadding), + padding: + EdgeInsets.only(top: 140.h, left: defaultPadding), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Suis tes amis pour voir leurs capsules", style: GoogleFonts.plusJakartaSans( - color: Colors.white, fontSize: 23, fontWeight: FontWeight.w800)) + color: Colors.white, + fontSize: 23, + fontWeight: FontWeight.w800)) ], ), ), @@ -225,8 +154,14 @@ class _FeedScreenState extends State with SingleTickerProviderStateM decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topRight, - stops: [0.3, 1], - colors: [bgColor.withOpacity(0.9), bgColor.withOpacity(0)])), + stops: [ + 0.3, + 1 + ], + colors: [ + bgColor.withOpacity(0.9), + bgColor.withOpacity(0) + ])), ), ), ), @@ -242,35 +177,67 @@ class _FeedScreenState extends State with SingleTickerProviderStateM ) : Container( width: double.infinity, + height: double.infinity, child: Stack( fit: StackFit.expand, children: [ - Align( - alignment: Alignment.topCenter, - child: CircularRevealAnimation( - animation: animation, - centerOffset: Offset(30.w, -100), - child: Container( - constraints: BoxConstraints(maxWidth: 600), - padding: EdgeInsets.fromLTRB(defaultPadding, 100.h, defaultPadding, 0), - child: RefreshIndicator( - displacement: 20, - triggerMode: RefreshIndicatorTriggerMode.onEdge, - onRefresh: _refresh, - child: ListView.builder( - physics: const BouncingScrollPhysics(decelerationRate: ScrollDecelerationRate.fast), - clipBehavior: Clip.none, - shrinkWrap: true, - itemCount: displayFeed.length, - itemBuilder: (BuildContext context, int index) { - return Padding( - padding: const EdgeInsets.only(bottom: 40), - child: - PostComponent(callback: openDetailPost, post: displayFeed[index], index: index), - ); - }, - ), - )), + Expanded( + child: Align( + alignment: Alignment.topCenter, + child: CircularRevealAnimation( + animation: animation, + centerOffset: Offset(30.w, -100), + child: Expanded( + child: Container( + height: double.infinity, + constraints: BoxConstraints(maxWidth: 600), + padding: EdgeInsets.fromLTRB( + defaultPadding, 100.h, defaultPadding, 0), + child: Expanded( + child: FutureBuilder( + future: _fetchData(), + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return RefreshIndicator( + displacement: 20, + triggerMode: + RefreshIndicatorTriggerMode + .onEdge, + onRefresh: _refresh, + child: Expanded( + child: ListView.builder( + physics: + const AlwaysScrollableScrollPhysics(), + clipBehavior: Clip.none, + shrinkWrap: true, + itemCount: displayFeed.length, + itemBuilder: + (BuildContext context, + int index) { + return Padding( + padding: + const EdgeInsets.only( + bottom: 40), + child: PostComponent( + callback: openDetailPost, + post: displayFeed[index], + index: index), + ); + }, + ), + ), + ); + } else { + return Center( + child: CupertinoActivityIndicator(), + ); + } + }, + ), + )), + ), + ), ), ), Align( @@ -281,8 +248,14 @@ class _FeedScreenState extends State with SingleTickerProviderStateM decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topRight, - stops: [0.3, 1], - colors: [bgColor.withOpacity(0.9), bgColor.withOpacity(0)])), + stops: [ + 0.3, + 1 + ], + colors: [ + bgColor.withOpacity(0.9), + bgColor.withOpacity(0) + ])), ), ), ), diff --git a/Sources/justMUSIC/lib/screens/loading_screen.dart b/Sources/justMUSIC/lib/screens/loading_screen.dart new file mode 100644 index 0000000..e9bb106 --- /dev/null +++ b/Sources/justMUSIC/lib/screens/loading_screen.dart @@ -0,0 +1,20 @@ +import 'package:flutter/Material.dart'; + +import '../values/constants.dart'; + +class LoadingScreen extends StatelessWidget { + const LoadingScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: bgColor, + body: Center( + child: Image( + image: AssetImage("assets/images/logo.png"), + width: 130, + ), + ), + ); + } +} diff --git a/Sources/justMUSIC/lib/services/CommentService.dart b/Sources/justMUSIC/lib/services/CommentService.dart index f139639..0808b25 100644 --- a/Sources/justMUSIC/lib/services/CommentService.dart +++ b/Sources/justMUSIC/lib/services/CommentService.dart @@ -20,6 +20,7 @@ class CommentService { var response = await FirebaseFirestore.instance .collection("comments") .where("post_id", isEqualTo: id) + .orderBy("date", descending: true) .get(); return response.docs; diff --git a/Sources/justMUSIC/lib/view_model/CommentViewModel.dart b/Sources/justMUSIC/lib/view_model/CommentViewModel.dart index b0ca81f..c3aa0a4 100644 --- a/Sources/justMUSIC/lib/view_model/CommentViewModel.dart +++ b/Sources/justMUSIC/lib/view_model/CommentViewModel.dart @@ -1,37 +1,37 @@ -import 'package:justmusic/model/mapper/CommentMapper.dart'; - -import '../model/Comment.dart'; -import '../services/CommentService.dart'; - -class CommentViewModel { - List _comments = []; - final CommentService _commentService = CommentService(); - - // Constructor - CommentViewModel(); - - // Methods - addComment(String text, String idPost) async { - try { - await _commentService.createComment(text,idPost); - } catch(e) { - print(e); - rethrow; - } - } - - Future> getCommentsByPostId(String id) async { - try { - var responseData = await _commentService.getCommentsByPostId(id); - var commentsFutures = responseData.map((value) async { - return await CommentMapper.toModel(value); - }).toList(); - _comments = await Future.wait(commentsFutures); - return _comments; - } catch(e) { - print(e); - _comments = []; - return []; - } - } -} +import 'package:justmusic/model/mapper/CommentMapper.dart'; + +import '../model/Comment.dart'; +import '../services/CommentService.dart'; + +class CommentViewModel { + List _comments = []; + final CommentService _commentService = CommentService(); + + // Constructor + CommentViewModel(); + + // Methods + addComment(String text, String idPost) async { + try { + await _commentService.createComment(text, idPost); + } catch (e) { + print(e); + rethrow; + } + } + + Future> getCommentsByPostId(String id) async { + try { + var responseData = await _commentService.getCommentsByPostId(id); + var commentsFutures = responseData.map((value) async { + return await CommentMapper.toModel(value); + }).toList(); + _comments = await Future.wait(commentsFutures); + return _comments; + } catch (e) { + print(e); + _comments = []; + return []; + } + } +} diff --git a/Sources/justMUSIC/pubspec.lock b/Sources/justMUSIC/pubspec.lock index ec336cd..8268097 100644 --- a/Sources/justMUSIC/pubspec.lock +++ b/Sources/justMUSIC/pubspec.lock @@ -941,6 +941,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0" + timezone: + dependency: "direct main" + description: + name: timezone + sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" + url: "https://pub.dev" + source: hosted + version: "0.9.2" top_snackbar_flutter: dependency: "direct main" description: diff --git a/Sources/justMUSIC/pubspec.yaml b/Sources/justMUSIC/pubspec.yaml index af7008b..ae178c7 100644 --- a/Sources/justMUSIC/pubspec.yaml +++ b/Sources/justMUSIC/pubspec.yaml @@ -1,129 +1,130 @@ -name: justmusic -description: A new Flutter project. - -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -# In Windows, build-name is used as the major, minor, and patch parts -# of the product and file versions while build-number is used as the build suffix. -version: 1.0.0+1 - -environment: - sdk: '>=2.18.2 <3.0.0' - -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. -dependencies: - flutter: - sdk: flutter - http: ^0.13.5 - - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - google_fonts: ^4.0.4 - gradiantbutton: ^0.0.1 - smooth_corner: ^1.1.0 - flutter_signin_button: ^2.0.0 - flutter_screenutil: ^5.7.0 - auto_size_text: ^3.0.0 - gradient_borders: ^1.0.0 - text_scroll: ^0.2.0 - circular_reveal_animation: ^2.0.1 - zoom_tap_animation: ^1.1.0 - custom_draggable_widget: ^0.0.2 - modal_bottom_sheet: ^2.1.2 - flutter_animated_play_button: ^0.3.0 - audioplayers: ^4.1.0 - ionicons: ^0.2.2 - top_snackbar_flutter: ^3.1.0 - firebase_core: ^2.15.0 - firebase_auth: ^4.7.2 - cloud_firestore: ^4.8.4 - image_picker: ^1.0.1 - insta_image_viewer: ^1.0.2 - pinch_zoom: ^1.0.0 - smooth_list_view: ^1.0.4 - animated_appear: ^0.0.4 - geolocator: ^9.0.2 - tuple: ^2.0.2 - firebase_storage: ^11.2.5 - another_flushbar: ^1.12.30 - flutter_countdown_timer: ^4.1.0 - intl: ^0.18.1 - lottie: ^2.5.0 - custom_refresh_indicator: ^2.2.1 - animations: ^2.0.7 - flutter_svg: ^2.0.7 - flutter_keyboard_visibility: ^5.4.1 - firebase_messaging: ^14.6.5 - -dev_dependencies: - flutter_test: - sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - assets: - - assets/images/ - - assets/animations/ - - assets/ - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages +name: justmusic +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=2.18.2 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + http: ^0.13.5 + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + google_fonts: ^4.0.4 + gradiantbutton: ^0.0.1 + smooth_corner: ^1.1.0 + flutter_signin_button: ^2.0.0 + flutter_screenutil: ^5.7.0 + auto_size_text: ^3.0.0 + gradient_borders: ^1.0.0 + text_scroll: ^0.2.0 + circular_reveal_animation: ^2.0.1 + zoom_tap_animation: ^1.1.0 + custom_draggable_widget: ^0.0.2 + modal_bottom_sheet: ^2.1.2 + flutter_animated_play_button: ^0.3.0 + audioplayers: ^4.1.0 + ionicons: ^0.2.2 + top_snackbar_flutter: ^3.1.0 + firebase_core: ^2.15.0 + firebase_auth: ^4.7.2 + cloud_firestore: ^4.8.4 + image_picker: ^1.0.1 + insta_image_viewer: ^1.0.2 + pinch_zoom: ^1.0.0 + smooth_list_view: ^1.0.4 + animated_appear: ^0.0.4 + geolocator: ^9.0.2 + tuple: ^2.0.2 + firebase_storage: ^11.2.5 + another_flushbar: ^1.12.30 + flutter_countdown_timer: ^4.1.0 + intl: ^0.18.1 + lottie: ^2.5.0 + custom_refresh_indicator: ^2.2.1 + animations: ^2.0.7 + flutter_svg: ^2.0.7 + flutter_keyboard_visibility: ^5.4.1 + timezone: ^0.9.2 + firebase_messaging: ^14.6.5 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/images/ + - assets/animations/ + - assets/ + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages