diff --git a/Sources/justMUSIC/android/app/src/debug/AndroidManifest.xml b/Sources/justMUSIC/android/app/src/debug/AndroidManifest.xml
index 44a649b..04c234b 100644
--- a/Sources/justMUSIC/android/app/src/debug/AndroidManifest.xml
+++ b/Sources/justMUSIC/android/app/src/debug/AndroidManifest.xml
@@ -5,4 +5,5 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
+
diff --git a/Sources/justMUSIC/lib/components/buttonPostComponent.dart b/Sources/justMUSIC/lib/components/buttonPostComponent.dart
index 166f426..28c4587 100644
--- a/Sources/justMUSIC/lib/components/buttonPostComponent.dart
+++ b/Sources/justMUSIC/lib/components/buttonPostComponent.dart
@@ -1,6 +1,7 @@
import 'package:flutter/Material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:ionicons/ionicons.dart';
+import 'package:tuple/tuple.dart';
import '../values/constants.dart';
@@ -15,54 +16,68 @@ class PhotoPostComponent extends StatelessWidget {
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: postbutton, borderRadius: BorderRadius.circular(8)),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Icon(
- Ionicons.camera,
- size: 15,
- color: Colors.white,
- ),
- SizedBox(
- width: 10,
- ),
- Text(
- 'Prendre un selfie',
- style: GoogleFonts.plusJakartaSans(
- color: Colors.white, fontSize: 12),
- )
- ]),
- )
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Icon(
+ Ionicons.camera,
+ size: 15,
+ color: Colors.white,
+ ),
+ SizedBox(
+ width: 10,
+ ),
+ Expanded(
+ child: Text(
+ 'Prendre un selfie',
+ style: GoogleFonts.plusJakartaSans(
+ color: Colors.white, fontSize: 12),
+ overflow: TextOverflow.ellipsis,
+ maxLines: 1,
+ ),
+ )
+ ]),
+ ))
: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: fillButton, borderRadius: BorderRadius.circular(8)),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- "Selfie enregistré",
- style: GoogleFonts.plusJakartaSans(
- color: Colors.white, fontSize: 12),
- ),
- SizedBox(
- width: 10,
- ),
- Icon(
- Icons.close,
- size: 12,
- color: Colors.white,
- ),
- ]),
- );
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Expanded(
+ child: Text(
+ "Selfie enregistré",
+ style: GoogleFonts.plusJakartaSans(
+ color: Colors.white, fontSize: 12),
+ overflow: TextOverflow.ellipsis,
+ maxLines: 1,
+ ),
+ ),
+ SizedBox(
+ width: 10,
+ ),
+ Icon(
+ Icons.close,
+ size: 12,
+ color: Colors.white,
+ ),
+ ]),
+ ));
}
}
class LocationPostComponent extends StatelessWidget {
final bool empty;
- const LocationPostComponent({Key? key, required this.empty})
+ final Tuple2? location;
+ const LocationPostComponent(
+ {Key? key, required this.empty, required this.location})
: super(key: key);
@override
@@ -73,48 +88,59 @@ class LocationPostComponent extends StatelessWidget {
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: postbutton, borderRadius: BorderRadius.circular(8)),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Icon(
- Icons.location_on,
- size: 15,
- color: Colors.white,
- ),
- SizedBox(
- width: 10,
- ),
- Text(
- 'Ajouter un lieu',
- style: GoogleFonts.plusJakartaSans(
- color: Colors.white, fontSize: 12),
- )
- ]),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Icon(
+ Icons.location_on,
+ size: 15,
+ color: Colors.white,
+ ),
+ SizedBox(
+ width: 10,
+ ),
+ Expanded(
+ child: Text(
+ 'Ajouter un lieu',
+ style: GoogleFonts.plusJakartaSans(
+ color: Colors.white, fontSize: 12),
+ overflow: TextOverflow.ellipsis,
+ ),
+ )
+ ])),
)
: Container(
width: double.infinity,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: fillButton, borderRadius: BorderRadius.circular(8)),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- "Lieu enregistré",
- style: GoogleFonts.plusJakartaSans(
- color: Colors.white, fontSize: 12),
- ),
- SizedBox(
- width: 10,
- ),
- Icon(
- Icons.close,
- size: 12,
- color: Colors.white,
- ),
- ]),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Expanded(
+ child: Text(
+ '${location?.item1}, ${location?.item2}',
+ style: GoogleFonts.plusJakartaSans(
+ color: Colors.white, fontSize: 12),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ SizedBox(
+ width: 10,
+ ),
+ Icon(
+ Icons.close,
+ size: 12,
+ color: Colors.white,
+ ),
+ ]),
+ ),
);
}
}
diff --git a/Sources/justMUSIC/lib/components/city_list_component.dart b/Sources/justMUSIC/lib/components/city_list_component.dart
new file mode 100644
index 0000000..a1ef958
--- /dev/null
+++ b/Sources/justMUSIC/lib/components/city_list_component.dart
@@ -0,0 +1,45 @@
+import 'package:flutter/Material.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:justmusic/values/constants.dart';
+import 'package:tuple/tuple.dart';
+
+class CityListComponent extends StatelessWidget {
+ final Tuple2 location;
+ const CityListComponent({Key? key, required this.location}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: double.infinity,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 10),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Flexible(
+ child: RichText(
+ overflow: TextOverflow.ellipsis,
+ maxLines: 1,
+ text: TextSpan(children: [
+ TextSpan(
+ text: location.item2 + ", ",
+ style: GoogleFonts.plusJakartaSans(
+ color: grayText,
+ fontWeight: FontWeight.w400,
+ fontSize: 17),
+ ),
+ TextSpan(
+ text: location.item1,
+ style: GoogleFonts.plusJakartaSans(
+ color: Colors.white,
+ fontWeight: FontWeight.w400,
+ fontSize: 17),
+ ),
+ ])),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/Sources/justMUSIC/lib/components/editable_post_component.dart b/Sources/justMUSIC/lib/components/editable_post_component.dart
index dda4c74..f66759f 100644
--- a/Sources/justMUSIC/lib/components/editable_post_component.dart
+++ b/Sources/justMUSIC/lib/components/editable_post_component.dart
@@ -11,8 +11,10 @@ import 'package:image_picker/image_picker.dart';
import 'package:insta_image_viewer/insta_image_viewer.dart';
import 'package:justmusic/values/constants.dart';
import 'package:text_scroll/text_scroll.dart';
+import 'package:tuple/tuple.dart';
import '../model/Music.dart';
+import '../screens/search_location_screen.dart';
import 'buttonPostComponent.dart';
class EditablePostComponent extends StatefulWidget {
@@ -28,7 +30,27 @@ class _EditablePostComponentState extends State
final ImagePicker picker = ImagePicker();
late Animation animation;
late AnimationController animationController;
+ late AnimationController _controller;
File? image;
+ Tuple2? selectedCity;
+
+ @override
+ void initState() {
+ _controller = AnimationController(
+ vsync: this,
+ duration: const Duration(milliseconds: 400),
+ );
+ animationController = AnimationController(
+ vsync: this,
+ duration: Duration(milliseconds: 400),
+ );
+ animation = CurvedAnimation(
+ parent: animationController,
+ curve: Curves.easeInOutSine,
+ );
+ animationController.forward();
+ super.initState();
+ }
Future pickImage() async {
try {
@@ -43,18 +65,34 @@ class _EditablePostComponentState extends State
}
}
- @override
- void initState() {
- animationController = AnimationController(
- vsync: this,
- duration: Duration(milliseconds: 400),
- );
- animation = CurvedAnimation(
- parent: animationController,
- curve: Curves.easeInOutSine,
+ void _selectLocation(Tuple2 location) {
+ Navigator.pop(context);
+ setState(() {
+ selectedCity = location;
+ });
+ }
+
+ void searchLocation() {
+ showModalBottomSheet(
+ transitionAnimationController: _controller,
+ barrierColor: Colors.black.withOpacity(0.7),
+ backgroundColor: Colors.transparent,
+ elevation: 1,
+ constraints: const BoxConstraints(
+ maxWidth: 600,
+ ),
+ isScrollControlled: true,
+ context: context,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(20), topRight: Radius.circular(20))),
+ builder: ((context) {
+ return ClipRRect(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(20), topRight: Radius.circular(20)),
+ child: SearchCityScreen(callback: _selectLocation));
+ }),
);
- animationController.forward();
- super.initState();
}
@override
@@ -219,8 +257,14 @@ class _EditablePostComponentState extends State
),
Expanded(
flex: 5,
- child: LocationPostComponent(
- empty: true,
+ child: GestureDetector(
+ onTap: () {
+ manageLocation();
+ },
+ child: LocationPostComponent(
+ empty: selectedCity == null,
+ location: selectedCity,
+ ),
),
),
],
@@ -280,4 +324,14 @@ class _EditablePostComponentState extends State
pickImage();
}
}
+
+ void manageLocation() {
+ if (selectedCity != null) {
+ setState(() {
+ selectedCity = null;
+ });
+ } else {
+ searchLocation();
+ }
+ }
}
diff --git a/Sources/justMUSIC/lib/screens/post_screen.dart b/Sources/justMUSIC/lib/screens/post_screen.dart
index ecc1051..1f53d8b 100644
--- a/Sources/justMUSIC/lib/screens/post_screen.dart
+++ b/Sources/justMUSIC/lib/screens/post_screen.dart
@@ -3,6 +3,7 @@ import 'package:flutter/Material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:justmusic/components/back_button.dart';
import 'package:justmusic/screens/search_song_screen.dart';
+import 'package:tuple/tuple.dart';
import '../components/editable_post_component.dart';
import '../components/post_button_component.dart';
import '../model/Music.dart';
@@ -21,6 +22,7 @@ class _PostScreenState extends State
late AnimationController _controller;
Music? selectedMusic;
+ Tuple2? selectedCity;
@override
void initState() {
@@ -39,7 +41,7 @@ class _PostScreenState extends State
});
}
- void openDetailPost() {
+ void openSearchSong() {
showModalBottomSheet(
transitionAnimationController: _controller,
barrierColor: Colors.black.withOpacity(0.7),
@@ -68,11 +70,11 @@ class _PostScreenState extends State
resizeToAvoidBottomInset: true,
backgroundColor: bgColor,
extendBodyBehindAppBar: true,
- appBar: PreferredSize(
+ appBar: const PreferredSize(
preferredSize: Size(double.infinity, 80),
child: SafeArea(
child: Padding(
- padding: const EdgeInsets.only(
+ padding: EdgeInsets.only(
left: defaultPadding,
right: defaultPadding,
top: defaultPadding),
@@ -104,7 +106,7 @@ class _PostScreenState extends State
height: 100.h,
),
GestureDetector(
- onTap: openDetailPost,
+ onTap: openSearchSong,
child: EditablePostComponent(music: selectedMusic)),
SizedBox(
height: 40.h,
diff --git a/Sources/justMUSIC/lib/screens/search_location_screen.dart b/Sources/justMUSIC/lib/screens/search_location_screen.dart
new file mode 100644
index 0000000..73f6d1a
--- /dev/null
+++ b/Sources/justMUSIC/lib/screens/search_location_screen.dart
@@ -0,0 +1,87 @@
+import 'dart:ui';
+
+import 'package:flutter/Material.dart';
+
+import '../components/city_list_component.dart';
+import '../services/GeoApi.dart';
+import '../values/constants.dart';
+
+class SearchCityScreen extends StatefulWidget {
+ final Function callback;
+ const SearchCityScreen({Key? key, required this.callback}) : super(key: key);
+
+ @override
+ State createState() => _SearchCityScreenState();
+}
+
+class _SearchCityScreenState extends State {
+ final ScrollController _scrollController = ScrollController();
+ final GeoApi api = GeoApi();
+
+ @override
+ Widget build(BuildContext context) {
+ double screenHeight = MediaQuery.of(context).size.height;
+ return BackdropFilter(
+ filter: ImageFilter.blur(
+ sigmaX: 60.0,
+ sigmaY: 60.0,
+ ),
+ child: Container(
+ color: bgAppBar.withOpacity(0.5),
+ height: screenHeight - 50,
+ padding: const EdgeInsets.only(top: 10),
+ child: Column(
+ children: [
+ Align(
+ child: Container(
+ width: 60,
+ height: 5,
+ decoration: BoxDecoration(
+ color: Color(0xFF3A3A3A).withOpacity(0.6),
+ borderRadius: BorderRadius.circular(20))),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Flexible(
+ child: ScrollConfiguration(
+ behavior: ScrollBehavior().copyWith(scrollbars: true),
+ child: FutureBuilder(
+ future: api.getNearbyCities(),
+ builder:
+ (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.hasData) {
+ return ListView.builder(
+ physics: const BouncingScrollPhysics(
+ decelerationRate: ScrollDecelerationRate.fast),
+ controller: _scrollController,
+ itemCount: snapshot.data.length,
+ itemBuilder: (context, index) {
+ return InkWell(
+ onTap: () {
+ widget.callback(snapshot.data[index]);
+ },
+ child: Padding(
+ padding:
+ const EdgeInsets.symmetric(horizontal: 20),
+ child: CityListComponent(
+ location: snapshot.data[index],
+ ),
+ ));
+ });
+ } else {
+ return Center(
+ child: CircularProgressIndicator(
+ color: grayColor,
+ ),
+ );
+ }
+ },
+ ),
+ ))
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/Sources/justMUSIC/lib/services/GeoApi.dart b/Sources/justMUSIC/lib/services/GeoApi.dart
new file mode 100644
index 0000000..7f203bc
--- /dev/null
+++ b/Sources/justMUSIC/lib/services/GeoApi.dart
@@ -0,0 +1,60 @@
+import 'package:geolocator/geolocator.dart';
+import 'package:http/http.dart' as http;
+import 'dart:convert';
+
+import 'package:tuple/tuple.dart';
+
+class GeoApi {
+ final String apiKey = "85a2724ad38b3994c2b7ebe1d239bbff";
+ Future>?> getNearbyCities() async {
+ try {
+ LocationPermission permission = await Geolocator.checkPermission();
+ bool serviceEnabled;
+
+ // Test if location services are enabled.
+ serviceEnabled = await Geolocator.isLocationServiceEnabled();
+ if (!serviceEnabled) {
+ // Location services are not enabled don't continue
+ // accessing the position and request users of the
+ // App to enable the location services.
+ return Future.error('Location services are disabled.');
+ }
+
+ permission = await Geolocator.checkPermission();
+ if (permission == LocationPermission.denied) {
+ permission = await Geolocator.requestPermission();
+ if (permission == LocationPermission.denied) {
+ return Future.error('Location permissions are denied');
+ }
+ }
+
+ if (permission == LocationPermission.deniedForever) {
+ return Future.error(
+ 'Location permissions are permanently denied, we cannot request permissions.');
+ }
+
+ Position position = await Geolocator.getCurrentPosition(
+ desiredAccuracy: LocationAccuracy.high);
+ String apiUrl =
+ 'http://api.openweathermap.org/data/2.5/find?lat=${position.latitude}&lon=${position.longitude}&cnt=10&appid=$apiKey';
+ var response = await http.get(Uri.parse(apiUrl));
+ if (response.statusCode == 200) {
+ var data = json.decode(response.body);
+ List cities = data['list'];
+ List> cityInfo = cities.map((city) {
+ String cityName = city['name'] as String;
+ String countryName = city['sys']['country'] as String;
+ return Tuple2(cityName, countryName);
+ }).toList();
+ return cityInfo;
+ } else {
+ print('Failed to fetch data');
+ }
+ } catch (e) {
+ print('Error: $e');
+ }
+ return null;
+ }
+}
+
+class Tuple {}
diff --git a/Sources/justMUSIC/pubspec.lock b/Sources/justMUSIC/pubspec.lock
index dfbd0a1..7f77bef 100644
--- a/Sources/justMUSIC/pubspec.lock
+++ b/Sources/justMUSIC/pubspec.lock
@@ -272,6 +272,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "9.2.0"
+ geolocator:
+ dependency: "direct main"
+ description:
+ name: geolocator
+ sha256: "5c23f3613f50586c0bbb2b8f970240ae66b3bd992088cf60dd5ee2e6f7dde3a8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.0.2"
+ geolocator_android:
+ dependency: transitive
+ description:
+ name: geolocator_android
+ sha256: b06c72853c993ae533f482d81a12805d7a441f5231d9668718bc7131d7464082
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.2.0"
+ geolocator_apple:
+ dependency: transitive
+ description:
+ name: geolocator_apple
+ sha256: "36527c555f4c425f7d8fa8c7c07d67b78e3ff7590d40448051959e1860c1cfb4"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.7"
+ geolocator_platform_interface:
+ dependency: transitive
+ description:
+ name: geolocator_platform_interface
+ sha256: af4d69231452f9620718588f41acc4cb58312368716bfff2e92e770b46ce6386
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.7"
+ geolocator_web:
+ dependency: transitive
+ description:
+ name: geolocator_web
+ sha256: f68a122da48fcfff68bbc9846bb0b74ef651afe84a1b1f6ec20939de4d6860e1
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.6"
+ geolocator_windows:
+ dependency: transitive
+ description:
+ name: geolocator_windows
+ sha256: f5911c88e23f48b598dd506c7c19eff0e001645bdc03bb6fecb9f4549208354d
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.1"
google_fonts:
dependency: "direct main"
description:
@@ -621,6 +669,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.0"
+ tuple:
+ dependency: "direct main"
+ description:
+ name: tuple
+ sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.2"
typed_data:
dependency: transitive
description:
diff --git a/Sources/justMUSIC/pubspec.yaml b/Sources/justMUSIC/pubspec.yaml
index 67c874a..4afb32a 100644
--- a/Sources/justMUSIC/pubspec.yaml
+++ b/Sources/justMUSIC/pubspec.yaml
@@ -58,6 +58,8 @@ dependencies:
pinch_zoom: ^1.0.0
smooth_list_view: ^1.0.4
animated_appear: ^0.0.4
+ geolocator: ^9.0.2
+ tuple: ^2.0.2
dev_dependencies:
flutter_test: