import 'package:flutter/material.dart'; import 'package:flutter_applicationdemo/bottom_nav_page.dart'; import 'package:google_fonts/google_fonts.dart'; import 'dart:async'; import 'login/user.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:flutter_google_places/flutter_google_places.dart'; import 'package:location/location.dart'; import 'package:flutter_applicationdemo/login/user.dart'; import 'settings_page.dart'; import 'venue_page.dart'; import 'venue.dart'; import 'globals.dart' as globals; import 'package:syncfusion_flutter_sliders/sliders.dart'; import 'feedback_page.dart'; import 'login/create_account_page.dart'; import 'login/sign_in_page.dart'; class Map extends StatefulWidget { @override State createState() => MapState(); } const kGoogleApiKey = "AIzaSyAUmhd6Xxud8SwgDxJ4LlYlcntm01FGoSk"; final homeScaffoldKey = GlobalKey(); late CameraPosition _currentCameraPosition; class MapState extends State { bool _bottomSheetIsOpen = false; final Completer _controller = Completer(); bool? _barFilterValue = true; bool? _restaurantFilterValue = true; bool? _cafeFilterValue = true; dynamic _priceFilterValue = 3; LocationData? _currentPosition; final TextEditingController _searchController = TextEditingController(); static const CameraPosition _stockholmCity = CameraPosition( target: LatLng(59.325027, 18.068516), zoom: 14.4746, ); List markersList = []; List closeByMarkersList = []; List hiddenVenues = []; List closeByVenues = []; @override void initState() { initialize(); _getUserLocation(); super.initState(); } initialize() { hiddenVenues.addAll(globals.VENUES); } Future _getLocationPermission() async { Location location = Location(); bool _serviceEnabled; PermissionStatus _permissionGranted; LocationData _locationData; _serviceEnabled = await location.serviceEnabled(); if (!_serviceEnabled) { _serviceEnabled = await location.requestService(); if (!_serviceEnabled) { return Future.error('Service not enable'); } } _permissionGranted = await location.hasPermission(); if (_permissionGranted == PermissionStatus.denied) { _permissionGranted = await location.requestPermission(); if (_permissionGranted != PermissionStatus.granted) { return Future.error('Permission Denied'); } } _locationData = await location.getLocation(); return _locationData; } _getUserLocation() async { _currentPosition = await _getLocationPermission(); _goToCurrentPosition( LatLng(_currentPosition!.latitude!, _currentPosition!.longitude!)); } late GoogleMapController googleMapController; final Mode _mode = Mode.fullscreen; int currentIndex = 0; @override Widget build(BuildContext context) { _currentCameraPosition = _stockholmCity; return Scaffold( appBar: AppBar( centerTitle: true, title: const Text("Sun chasers"), key: homeScaffoldKey, actions: [createFilterMenuButton()], backgroundColor: const Color.fromARGB(255, 190, 146, 160), ), drawer: Drawer( child: Container( child: globals.LOGGED_IN_USER.userID == 0 ? buildDrawerSignedOut(context) : buildDrawerSignedIn(context), ), ), body: Stack( children: [ GoogleMap( cameraTargetBounds: CameraTargetBounds(LatLngBounds( northeast: const LatLng(59.3474696569038, 18.1001602476002147), southwest: const LatLng(59.297332547922636, 17.999522500277884))), minMaxZoomPreference: MinMaxZoomPreference(12.5, 18.5), onCameraMove: (CameraPosition camera) { _currentCameraPosition = camera; }, onCameraIdle: () { (context as Element).reassemble(); removeMarkersOutOfRange(); addMarkersInRange(); }, mapType: MapType.normal, myLocationEnabled: true, initialCameraPosition: _stockholmCity, compassEnabled: true, onMapCreated: (GoogleMapController controller) { setAllMarkersAsInvisible(); addMarkersInRange(); _controller.complete(controller); }, markers: closeByMarkersList.map((e) => e).toSet(), onTap: (LatLng) { closeBottomSheetIfOpen(); }, ), ], ), floatingActionButton: Padding( padding: const EdgeInsets.only(top: 100.0), child: FloatingActionButton( onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) => const SettingsPage())); }, backgroundColor: Colors.purple, child: const Icon(Icons.filter_alt), ), ), floatingActionButtonLocation: FloatingActionButtonLocation.endTop, ); } final screens = [ Map(), ]; PopupMenuButton createFilterMenuButton() { return PopupMenuButton( icon: Icon(Icons.filter_list), iconSize: 40, itemBuilder: (context) => [ const PopupMenuItem( child: Text( "Filters", style: TextStyle( fontSize: 20, ), ), padding: EdgeInsets.only(left: 60), ), createCheckBoxes(), createPriceSlider(), PopupMenuItem( child: ButtonBar( alignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: null, // TODO: Fixa så att kartan filtreras när man klickar på 'Apply Filters' child: Text( "Apply Filters", style: TextStyle(color: Colors.black), ), style: ButtonStyle( backgroundColor: MaterialStateProperty.all( globals.BUTTONCOLOR)), ), ], )) ]); } // Creates the checkboxes for the filter menu PopupMenuItem createCheckBoxes() { return PopupMenuItem( child: Padding( padding: const EdgeInsets.all(8), child: Expanded( child: Column( children: [ Divider( color: Colors.black, ), StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CheckboxListTile( value: _barFilterValue, onChanged: (bool? newValue) { setState(() { _barFilterValue = newValue; }); }, title: const Icon( Icons.sports_bar, color: Colors.orange, )); }), StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CheckboxListTile( value: _restaurantFilterValue, onChanged: (bool? newValue) { setState(() { _restaurantFilterValue = newValue; }); }, title: Icon( Icons.restaurant, color: Colors.blueGrey[200], ), ); }), //Cafe checkbox StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return CheckboxListTile( value: _cafeFilterValue, onChanged: (bool? newValue) { setState(() => _cafeFilterValue = newValue); }, title: Icon( Icons.coffee, color: Colors.brown[400], )); }), ], ), ))); } PopupMenuItem createPriceSlider() { return PopupMenuItem( child: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return SfSlider( value: _priceFilterValue, onChanged: (dynamic newValue) { setState((() => _priceFilterValue = newValue)); }, min: 1, max: 3, showTicks: true, interval: 1, activeColor: Colors.blue, showLabels: true, stepSize: 1.0, labelFormatterCallback: (dynamic value, String formattedText) { switch (value) { case 1: return '\$'; case 2: return '\$\$'; case 3: return '\$\$\$'; } return value.toString(); }); }), ); } Future _gotoLocation(double lat, double lng) async { final GoogleMapController controller = await _controller.future; controller.animateCamera(CameraUpdate.newCameraPosition( CameraPosition(target: LatLng(lat, lng), zoom: 14.5))); } Future _goToCurrentPosition(LatLng latlng) async { final GoogleMapController controller = await _controller.future; controller.animateCamera(CameraUpdate.newCameraPosition(CameraPosition( bearing: 0, target: LatLng(latlng.latitude, latlng.longitude), //tilt: 59.440717697143555, zoom: 15.4746))); } void removeMarkersOutOfRange() { for (int i = 0; i < closeByMarkersList.length; i++) { Marker marker = closeByMarkersList[i]; globals.venueAlreadyAdded( globals.getVenueByID(int.parse(marker.markerId.value))!.venueName); if (marker.position.longitude - _currentCameraPosition.target.longitude > 0.02 || marker.position.latitude - _currentCameraPosition.target.latitude > 0.02) { closeByMarkersList.remove(marker); globals.getVenueByID(int.parse(marker.markerId.value))?.isShownOnMap = false; i--; } } } void addMarkersInRange() { for (int i = 0; i < globals.VENUES.length; i++) { if (!globals.VENUES[i].isShownOnMap && (globals.VENUES[i].position.longitude - _currentCameraPosition.target.longitude < 0.02 && globals.VENUES[i].position.latitude - _currentCameraPosition.target.latitude < 0.02)) { Marker marker = Marker( markerId: MarkerId(globals.VENUES[i].venueID.toString()), position: globals.VENUES[i].position, onTap: () => createBottomSheet(globals.VENUES[i]), icon: globals.VENUES[i].drawIconColor()); globals.VENUES[i].isShownOnMap = true; closeByMarkersList.add(marker); } } } createBottomSheet(Venue venue) async { _bottomSheetIsOpen = true; // Scaffold.of(context).showBottomSheet(((context) { showModalBottomSheet( context: context, builder: (BuildContext context) { return InkWell( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => VenuePage(venue)), ); }, child: Container( height: 175, color: const Color(0xFFF5F5F5), child: Center( child: Column( children: [ bottomSheetWidgetContainer(venue, context), ], ), ), ), ); }); } Container bottomSheetWidgetContainer(Venue venue, BuildContext context) { return Container( margin: const EdgeInsets.all(16), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, // mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ columnCoveringNameAndAddress(venue), ], ), // columnCoveringRating(), ], ), const SizedBox( height: 20, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( children: const [ WeatherIconRow(), WeatherStatusRow(), ], ), columnHandlingReadMoreButton(context, venue), ], ) ], ), ); } Column columnHandlingReadMoreButton(BuildContext context, Venue venue) { return Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(8), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ readMoreButton(context, venue), ], ), ), ], ); } ElevatedButton readMoreButton(BuildContext context, Venue venue) { return ElevatedButton( child: const Text( 'Read More', style: TextStyle(fontSize: 16), ), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => VenuePage(venue))); _bottomSheetIsOpen = false; }, style: ElevatedButton.styleFrom( primary: globals.BUTTONCOLOR, ), ); } Column columnCoveringNameAndAddress(Venue venue) { return Column( children: [ Text( venue.venueName, style: GoogleFonts.roboto( textStyle: const TextStyle( color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 20, )), ), Text( venue.venueAddress + ' ' + venue.venueStreetNo, style: GoogleFonts.roboto( textStyle: const TextStyle( color: Colors.black, fontWeight: FontWeight.w300, fontSize: 14, )), ) ], ); } closeBottomSheetIfOpen() { if (_bottomSheetIsOpen) { Navigator.pop(context); _bottomSheetIsOpen = false; } } void setAllMarkersAsInvisible() { for (Venue venue in hiddenVenues) { venue.isShownOnMap = false; } } } class WeatherIconRow extends StatelessWidget { const WeatherIconRow({ Key? key, }) : super(key: key); @override Widget build(BuildContext context) { return Row( children: [ const Text( 'Weather: \t\t', style: TextStyle(fontSize: 18), ), globals.forecast.getCurrentWeatherIcon(), ], ); } } class WeatherStatusRow extends StatelessWidget { const WeatherStatusRow({ Key? key, }) : super(key: key); @override Widget build(BuildContext context) { return Row( children: [ Text( '– ' + globals.forecast.getCurrentWeatherStatus(), style: const TextStyle( fontSize: 16, fontStyle: FontStyle.italic, ), ), ], ); } } Widget buildDrawerSignedIn(BuildContext context) { return Drawer( child: ListView( padding: EdgeInsets.zero, children: [ DrawerHeader( decoration: const BoxDecoration(color: Color.fromARGB(255, 190, 146, 160)), child: Column( children: const [ Text( 'Sun Chaser', style: TextStyle(fontSize: 32), ), SizedBox(height: 30), Icon(Icons.account_box_rounded), ], ), ), ListTile( leading: Icon(Icons.logout), title: Text('Sign out'), onTap: () { globals.LOGGED_IN_USER = User(0, "", ""); Navigator.push( context, MaterialPageRoute( builder: (context) => BottomNavPage()), //Replace Container() with call to Map-page. ); }, ), giveFeedbackTile(context), settingsTile(context), ], ), ); } Widget buildDrawerSignedOut(BuildContext context) { return Drawer( child: ListView( padding: EdgeInsets.zero, children: [ drawerHeader(), createAccountTile(context), logInTile(context), giveFeedbackTile(context), settingsTile(context), ], ), ); } DrawerHeader drawerHeader() { return DrawerHeader( decoration: const BoxDecoration(color: Color.fromARGB(255, 190, 146, 160)), child: Column( children: const [ Text( 'Sun Chaser', style: TextStyle(fontSize: 32), ), SizedBox(height: 30), ], ), ); } ListTile settingsTile(BuildContext context) { return ListTile( leading: Icon(Icons.settings), title: Text('Settings'), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => SettingsPage(), ), ); }, ); } ListTile giveFeedbackTile(BuildContext context) { return ListTile( leading: Icon(Icons.thumb_up_alt), title: Text('Give feedback'), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => FormForFeedback(), ), ); }, ); } ListTile logInTile(BuildContext context) { return ListTile( leading: Icon(Icons.login), title: Text('Sign in'), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => SignInPage(), ), ); }, ); } ListTile createAccountTile(BuildContext context) { return ListTile( leading: Icon(Icons.account_box_rounded), title: Text('Create account'), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => CreateAccountPage(), ), ); }, ); }