parent
f238c1cf0f
commit
ddda7ab779
@ -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<String, String> 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),
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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<SearchCityScreen> createState() => _SearchCityScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchCityScreenState extends State<SearchCityScreen> {
|
||||||
|
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<dynamic> 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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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<List<Tuple2<String, String>>?> 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<dynamic> cities = data['list'];
|
||||||
|
List<Tuple2<String, String>> 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 {}
|
Loading…
Reference in new issue