mobile-flutter/covas_mobile/lib/pages/ListItemMenu.dart

339 lines
11 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv
2024-06-23 17:23:26 +02:00
import 'dart:convert';
2024-06-24 23:57:43 +02:00
import 'dart:io';
2024-10-28 22:52:00 +01:00
import 'ItemMenu.dart';
import 'Camera.dart';
2024-06-30 15:17:10 +02:00
import '../classes/events.dart';
2024-06-23 20:57:54 +02:00
import 'package:shared_preferences/shared_preferences.dart';
2024-06-27 00:01:07 +02:00
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
2024-07-06 17:29:41 +02:00
import 'package:camera/camera.dart';
import 'package:mapbox_gl/mapbox_gl.dart'; // Add this import
2024-11-04 23:53:24 +01:00
import 'dart:math';
2024-06-30 15:17:10 +02:00
import '../variable/globals.dart' as globals;
2024-06-24 23:57:43 +02:00
2024-06-23 17:23:26 +02:00
void main() {
2024-10-28 22:52:00 +01:00
initializeDateFormatting("fr_FR", null).then((_) => runApp(const MyApp()));
2024-06-23 17:23:26 +02:00
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: const ListItemMenu(),
debugShowCheckedModeBanner: false,
);
}
}
class ListItemMenu extends StatefulWidget {
const ListItemMenu({super.key});
@override
State<ListItemMenu> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<ListItemMenu> {
2024-06-24 23:57:43 +02:00
Future<List<Events>> postsFuture = getPosts();
2024-10-23 22:43:03 +02:00
List<Events> filteredPosts = [];
String geographicalZone = '';
String query = '';
2024-11-04 23:53:24 +01:00
List<Map<String, dynamic>> suggestions = [];
TextEditingController inputGeo = TextEditingController();
// Fetching events from API
2024-06-24 23:57:43 +02:00
static Future<List<Events>> getPosts() async {
2024-06-23 20:57:54 +02:00
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
2024-06-24 23:57:43 +02:00
final List<Events> body = [];
2024-06-23 20:57:54 +02:00
if (accessToken.isNotEmpty) {
2024-06-30 14:48:48 +02:00
var url = Uri.parse("${globals.api}/events");
2024-06-24 23:57:43 +02:00
final response = await http.get(url, headers: {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=${accessToken}"
});
final List body = json.decode(utf8.decode(response.bodyBytes));
return body.map((e) => Events.fromJson(e)).toList();
2024-06-23 20:57:54 +02:00
}
return body;
2024-06-23 17:23:26 +02:00
}
Future<void> searchSuggestions(String input) async {
await dotenv.load(fileName: ".env"); // Load .env file
final mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '';
final url =
2024-11-04 23:53:24 +01:00
'https://api.mapbox.com/geocoding/v5/mapbox.places/${input}.json?access_token=${mapboxAccessToken}&proximity=ip';
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final data = json.decode(response.body);
setState(() {
suggestions = (data['features'] as List)
2024-11-04 23:53:24 +01:00
.map((feature) => {
'place_name': feature['place_name'],
'geometry': feature[
'geometry'], // Include geometry for latitude/longitude
})
.toList();
2024-10-23 22:43:03 +02:00
});
} else {
throw Exception('Failed to load suggestions');
2024-10-23 22:43:03 +02:00
}
2024-07-06 17:29:41 +02:00
}
2024-11-04 23:53:24 +01:00
Future<void> fetchPostsByLocation(double latitude, double longitude) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
if (accessToken.isNotEmpty) {
// Calculate the boundaries
double radiusInKm = 50;
double latDistance = radiusInKm / 111.0;
double lonDistance = radiusInKm / (111.0 * cos(latitude * pi / 180));
double minLat = latitude - latDistance;
double maxLat = latitude + latDistance;
double minLon = longitude - lonDistance;
double maxLon = longitude + lonDistance;
var url = Uri.parse("${globals.api}/events/search"
"?min_lat=$minLat&max_lat=$maxLat"
"&min_lon=$minLon&max_lon=$maxLon");
final response = await http.get(url, headers: {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=$accessToken"
});
if (response.statusCode == 200) {
final List<dynamic> body = json.decode(utf8.decode(response.bodyBytes));
setState(() {
filteredPosts = body
.map((e) => Events.fromJson(e as Map<String, dynamic>))
.toList();
});
} else {
throw Exception('Failed to load posts');
}
}
}
2024-10-28 22:52:00 +01:00
Padding _buildGeographicalZoneSearchField() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
TextField(
controller: inputGeo,
decoration: InputDecoration(
labelText: 'Search by geographical zone',
border: OutlineInputBorder(),
2024-11-04 23:53:24 +01:00
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: () {
setState(() {
inputGeo.clear(); // Clear the text field
geographicalZone = ''; // Reset the geographical zone state
suggestions.clear(); // Optionally clear suggestions
});
},
),
),
onChanged: (value) {
setState(() {
geographicalZone = value;
searchSuggestions(value);
});
},
),
if (suggestions.isNotEmpty)
Container(
height: 200,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue), // Add a border color
borderRadius:
BorderRadius.circular(8), // Optional: rounded corners
),
child: ListView.builder(
2024-11-04 23:53:24 +01:00
shrinkWrap: true,
itemCount: suggestions.length,
itemBuilder: (context, index) {
return ListTile(
2024-11-04 23:53:24 +01:00
title: Text(suggestions[index]['place_name']),
onTap: () async {
final latitude =
suggestions[index]['geometry']['coordinates'][1];
final longitude =
suggestions[index]['geometry']['coordinates'][0];
setState(() {
2024-11-04 23:53:24 +01:00
geographicalZone = suggestions[index]['place_name'];
inputGeo.text = geographicalZone;
suggestions.clear();
});
2024-11-04 23:53:24 +01:00
await fetchPostsByLocation(latitude, longitude);
},
);
},
),
),
],
2024-10-28 22:52:00 +01:00
),
);
}
2024-06-23 17:23:26 +02:00
@override
Widget build(BuildContext context) {
return Scaffold(
2024-10-23 22:43:03 +02:00
appBar: AppBar(
title: const Text("Item list menu"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
showSearch(
context: context,
delegate: SearchDelegateExample(),
);
},
),
],
),
body: Column(
children: [
2024-10-28 22:52:00 +01:00
_buildGeographicalZoneSearchField(),
Expanded(
child: FutureBuilder<List<Events>>(
future: postsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
2024-10-28 22:52:00 +01:00
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
final posts = snapshot.data!;
final displayedPosts =
filteredPosts.isEmpty ? posts : filteredPosts;
return buildPosts(displayedPosts);
} else {
return const Text("No data available");
}
},
),
),
],
2024-06-23 17:23:26 +02:00
),
);
}
// Function to display fetched data on screen
2024-06-24 23:57:43 +02:00
Widget buildPosts(List<Events> posts) {
2024-10-28 22:52:00 +01:00
return ListView.separated(
itemCount: posts.length,
itemBuilder: (context, index) {
final post = posts[index];
final startDate = DateTime.parse(post.startDate!);
final date = DateFormat.yMd().format(startDate);
final time = DateFormat.Hm().format(startDate);
return ListTile(
title: Text('${post.name!}'),
subtitle: Text('${post.place!}\n${date} ${time}'),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) => ItemMenu(title: post.id!)));
});
},
separatorBuilder: (context, index) {
return Divider();
},
2024-06-26 23:10:16 +02:00
);
2024-06-23 17:23:26 +02:00
}
}
2024-10-23 22:43:03 +02:00
class SearchDelegateExample extends SearchDelegate {
@override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
query = '';
},
),
];
}
@override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
close(context, null);
},
);
}
@override
Widget buildResults(BuildContext context) {
// Perform the search and return the results
return FutureBuilder<List<Events>>(
future: searchPosts(query),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
final posts = snapshot.data!;
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
final post = posts[index];
return ListTile(
title: Text(post.name!),
subtitle: Text(post.place!),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ItemMenu(title: post.id!),
),
);
},
);
},
);
} else {
return const Center(child: Text("No results found"));
}
},
);
}
@override
Widget buildSuggestions(BuildContext context) {
return Container(); // Implement suggestions if needed
}
Future<List<Events>> searchPosts(String query) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
final List<Events> body = [];
if (accessToken.isNotEmpty) {
var url = Uri.parse("${globals.api}/events/search?item=$query");
final response = await http.get(url, headers: {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=${accessToken}"
});
final List body = json.decode(utf8.decode(response.bodyBytes));
return body.map((e) => Events.fromJson(e)).toList();
}
return body;
}
}