diff --git a/README.md b/README.md index d19d7061..0e601f45 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ Make sure Node.js, Flutter, and Docker are installed! npm run setup ``` +### API Keys for Flutter +``` +To access the Google Maps in the CornellGo app, you need two Google Maps API Keys. One is for Android and one is for iOS. Ask a TPM for the .env file with the keys and the instructions to add it into the app. +``` + ### Run project in container ``` diff --git a/game/.gitignore b/game/.gitignore index 952722ca..ab7fa15a 100644 --- a/game/.gitignore +++ b/game/.gitignore @@ -44,3 +44,6 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# Secret API Keys +*.env \ No newline at end of file diff --git a/game/android/app/build.gradle b/game/android/app/build.gradle index 1ad2bd46..be8576be 100644 --- a/game/android/app/build.gradle +++ b/game/android/app/build.gradle @@ -31,6 +31,8 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'com.google.gms.google-services' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +apply from: project(':flutter_config').projectDir.getPath() + "/dotenv.gradle" + android { compileSdkVersion 33 diff --git a/game/android/app/src/main/AndroidManifest.xml b/game/android/app/src/main/AndroidManifest.xml index 9c839461..852baa93 100644 --- a/game/android/app/src/main/AndroidManifest.xml +++ b/game/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,12 @@ + + + + + \ No newline at end of file diff --git a/game/assets/icons/userlocation.png b/game/assets/icons/userlocation.png new file mode 100644 index 00000000..9193d251 Binary files /dev/null and b/game/assets/icons/userlocation.png differ diff --git a/game/ios/Runner/AppDelegate.swift b/game/ios/Runner/AppDelegate.swift index 716e80f7..1d7f1447 100644 --- a/game/ios/Runner/AppDelegate.swift +++ b/game/ios/Runner/AppDelegate.swift @@ -1,6 +1,8 @@ import UIKit import Flutter import Firebase +import GoogleMaps +import flutter_config @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { @@ -10,6 +12,7 @@ import Firebase ) -> Bool { GeneratedPluginRegistrant.register(with: self) FirebaseApp.configure() + GMSServices.provideAPIKey(FlutterConfigPlugin.env(for: "IOS_MAP_API_KEY")) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } diff --git a/game/lib/api/geopoint.dart b/game/lib/api/geopoint.dart index b2d6ef95..bb0b90ad 100644 --- a/game/lib/api/geopoint.dart +++ b/game/lib/api/geopoint.dart @@ -1,19 +1,28 @@ +import 'dart:async'; +import 'dart:io' show Platform; import 'package:geolocator/geolocator.dart'; class GeoPoint { static bool didMakeRequest = false; double _lat = 0; double _long = 0; + double _heading = 0; double get lat => _lat; double get long => _long; + double get heading => _heading; static bool _isRequestingLocationPermissions = false; static bool _isRequestingLocation = false; - GeoPoint(double lat, double long) { + GeoPoint( + double lat, + double long, + double heading, + ) { _lat = lat; _long = long; + _heading = heading; } static Future current() async { @@ -37,7 +46,7 @@ class GeoPoint { if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { - return Future.error('Location permissions are denied'); + return Future.error('Location services are disabled.'); } } if (permission == LocationPermission.deniedForever) { @@ -46,7 +55,7 @@ class GeoPoint { 'Location permissions are permanently denied, we cannot request permissions.'); } final pos = await Geolocator.getCurrentPosition(); - return GeoPoint(pos.latitude, pos.longitude); + return GeoPoint(pos.latitude, pos.longitude, pos.heading); } catch (e) { return Future.error(e.toString()); } @@ -59,4 +68,40 @@ class GeoPoint { double bearingTo(GeoPoint other) { return Geolocator.bearingBetween(_lat, _long, other._lat, other._long); } + + static LocationSettings getLocationSettings() { + late LocationSettings locationSettings; + + if (Platform.isAndroid) { + locationSettings = AndroidSettings( + accuracy: LocationAccuracy.high, + distanceFilter: 100, + forceLocationManager: true, + intervalDuration: const Duration(seconds: 10), + //(Optional) Set foreground notification config to keep the app alive + //when going to the background + foregroundNotificationConfig: const ForegroundNotificationConfig( + notificationText: + "CornellGO will continue to receive your location even when you aren't using it", + notificationTitle: "Running in Background", + enableWakeLock: true, + )); + } else if (Platform.isIOS || Platform.isMacOS) { + locationSettings = AppleSettings( + accuracy: LocationAccuracy.high, + activityType: ActivityType.fitness, + distanceFilter: 100, + pauseLocationUpdatesAutomatically: true, + // Only set to true if our app will be started up in the background. + showBackgroundLocationIndicator: false, + ); + } else { + locationSettings = LocationSettings( + accuracy: LocationAccuracy.high, + distanceFilter: 100, + ); + } + + return locationSettings; + } } diff --git a/game/lib/challenges/challenges_page.dart b/game/lib/challenges/challenges_page.dart index 051fae02..262fd4b8 100644 --- a/game/lib/challenges/challenges_page.dart +++ b/game/lib/challenges/challenges_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/src/foundation/key.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'challenge_cell.dart'; +import 'package:game/journeys/filter_form.dart'; class ChallengesPage extends StatefulWidget { const ChallengesPage({Key? key}) : super(key: key); @@ -12,6 +13,17 @@ class ChallengesPage extends StatefulWidget { } class _ChallengesPageState extends State { + void openFilter() { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: ( + BuildContext context, + ) { + return FilterForm(); + }); + } + /* Dummy code, to be replaced */ final cells = [ ChallengeCell( @@ -93,25 +105,33 @@ class _ChallengesPageState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( - padding: EdgeInsets.zero, - color: Color.fromARGB(76, 217, 217, 217), + height: 30, child: TextButton.icon( - onPressed: () {}, - icon: Icon( - Icons.tune, - color: Color.fromARGB(204, 0, 0, 0), - size: 12, - ), - label: Text( - "filter", - style: TextStyle( - color: Color.fromARGB(204, 0, 0, 0), - fontSize: 12, - fontFamily: 'Lato', - fontWeight: FontWeight.w600, + onPressed: openFilter, + icon: Icon( + Icons.filter_list_rounded, + color: Color.fromARGB(255, 0, 0, 0), + size: 20.0, + ), + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Color.fromARGB(153, 217, 217, 217)), + padding: MaterialStateProperty.all( + EdgeInsets.only(right: 16.0, left: 16.0), + ), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), ), - ), - ), + label: Text( + "Filter By", + style: TextStyle( + color: Color.fromARGB(255, 0, 0, 0), + fontSize: 15, + fontFamily: 'Inter', + ), + )), ), ], ), diff --git a/game/lib/gameplay/gameplay_map.dart b/game/lib/gameplay/gameplay_map.dart index 49780cb8..40cf373a 100644 --- a/game/lib/gameplay/gameplay_map.dart +++ b/game/lib/gameplay/gameplay_map.dart @@ -1,6 +1,10 @@ -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:latlong2/latlong.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:game/api/geopoint.dart'; +import 'package:geolocator/geolocator.dart'; +import 'dart:async'; class GameplayMap extends StatefulWidget { const GameplayMap({Key? key}) : super(key: key); @@ -10,20 +14,278 @@ class GameplayMap extends StatefulWidget { } class _GameplayMapState extends State { - final mapController = MapController(); + late Completer mapCompleter = Completer(); + late StreamSubscription positionStream; + @override - Widget build(BuildContext context) { - return FlutterMap( - options: MapOptions( - center: LatLng(51.509364, -0.128928), - zoom: 9.2, + void initState() { + setCustomMarkerIcon(); + startPositionStream(); + super.initState(); + } + + // User is by default centered around some location on Cornell's campus. + // User should only be at these coords briefly before map is moved to user's + // current location. + final LatLng _center = const LatLng(42.447, -76.4875); + + // User's current location will fall back to _center when current location + // cannot be found + GeoPoint? currentLocation; + + // hard coded target for testing + // TODO: connect target location and radius to backend + GeoPoint targetLocation = GeoPoint(42.4475, -76.4879, 0); + double arrivalRadius = 10.0; + + // whether the picture is expanded over the map + bool isExpanded = false; + + Future _onMapCreated(GoogleMapController controller) async { + mapCompleter.complete(controller); + } + + /** + * Starts the user's current location streaming upon state initialization + * Sets the camera to center on user's location by default + */ + void startPositionStream() async { + GoogleMapController googleMapController = await mapCompleter.future; + + GeoPoint.current().then( + (location) { + currentLocation = location; + }, + ); + + positionStream = Geolocator.getPositionStream( + locationSettings: GeoPoint.getLocationSettings()) + .listen((Position? newPos) { + // prints user coordinates - useful for debugging + // print(newPos == null + // ? 'Unknown' + // : '${newPos.latitude.toString()}, ${newPos.longitude.toString()}'); + + // putting the animate camera logic in here seems to not work + // could be useful to debug later? + currentLocation = newPos == null + ? GeoPoint(_center.latitude, _center.longitude, 0) + : GeoPoint(newPos.latitude, newPos.longitude, newPos.heading); + }); + + positionStream.onData((newPos) { + currentLocation = + GeoPoint(newPos.latitude, newPos.longitude, newPos.heading); + + // upon new user location data, moves map camera to be centered around + // new position and sets zoom. + googleMapController.animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition( + target: LatLng(newPos.latitude, newPos.longitude), + zoom: 16.5, + ), + ), + ); + setState(() {}); + }); + } + + /** + * Recenters the camera onto the user's current location and will keep + * camera centered until position stream's event handler is replaced + */ + void recenterCamera() async { + GoogleMapController googleMapController = await mapCompleter.future; + + googleMapController.animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition( + target: LatLng(currentLocation!.lat, currentLocation!.long), + zoom: 16.5, + ), ), - children: [ - TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'dev.fleaflet.flutter_map.example', + ); + + positionStream.onData((newPos) { + currentLocation = + GeoPoint(newPos.latitude, newPos.longitude, newPos.heading); + + // upon new user location data, moves map camera to be centered around + // new position and sets zoom. + googleMapController.animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition( + target: LatLng(newPos.latitude, newPos.longitude), + zoom: 16.5, + ), ), - ], + ); + setState(() {}); + }); + } + + /** + * Replaces the position stream event handler to stop recentering the + * camera on the user's location + */ + void cancelRecenterCamera() async { + positionStream.onData((newPos) { + currentLocation = + GeoPoint(newPos.latitude, newPos.longitude, newPos.heading); + setState(() {}); + }); + } + + BitmapDescriptor currentLocationIcon = BitmapDescriptor.defaultMarker; + void setCustomMarkerIcon() { + BitmapDescriptor.fromAssetImage( + ImageConfiguration.empty, "assets/icons/userlocation.png") + .then( + (icon) { + currentLocationIcon = icon; + }, + ); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeData( + useMaterial3: true, + colorSchemeSeed: Colors.green[700], + ), + home: Scaffold( + body: Listener( + onPointerDown: (e) { + cancelRecenterCamera(); + }, + child: GoogleMap( + onMapCreated: _onMapCreated, + compassEnabled: false, + myLocationButtonEnabled: false, + zoomControlsEnabled: false, + myLocationEnabled: false, + mapToolbarEnabled: false, + mapType: MapType.normal, + initialCameraPosition: CameraPosition( + target: currentLocation == null + ? _center + : LatLng(currentLocation!.lat, currentLocation!.lat), + zoom: 11, + ), + markers: { + Marker( + markerId: const MarkerId("currentLocation"), + icon: currentLocationIcon, + position: currentLocation == null + ? _center + : LatLng(currentLocation!.lat, currentLocation!.long), + rotation: + currentLocation == null ? 0 : currentLocation!.heading, + ), + }, + circles: { + Circle( + circleId: CircleId("hintCircle"), + center: LatLng(targetLocation.lat, targetLocation.long), + radius: 100, + strokeColor: Color.fromARGB(80, 30, 41, 143), + strokeWidth: 2, + fillColor: Color.fromARGB(80, 83, 134, 237), + ) + }, + ), + ), + floatingActionButton: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: EdgeInsets.only(bottom: 15.0), + child: FloatingActionButton.extended( + onPressed: recenterCamera, + label: Icon(Icons.lightbulb, + color: Color.fromARGB(255, 223, 84, 84)), + backgroundColor: Color.fromARGB(255, 255, 255, 255), + shape: CircleBorder(), + ), + ), + Padding( + padding: EdgeInsets.only(bottom: 150.0), + child: FloatingActionButton.extended( + onPressed: recenterCamera, + label: Icon(Icons.location_on, + color: Color.fromARGB(255, 223, 84, 84)), + backgroundColor: Color.fromARGB(255, 255, 255, 255), + shape: CircleBorder(), + ), + ), + ], + ), + Padding( + padding: EdgeInsets.only(left: 24.0, top: 40.0), + child: isExpanded + ? Container( + width: MediaQuery.of(context).size.width * 0.9, + height: MediaQuery.of(context).size.height * 0.9, + alignment: Alignment.topCenter, + child: Stack( + alignment: Alignment.topRight, + children: [ + Image.asset('assets/images/main-bg.jpeg'), + Padding( + padding: EdgeInsets.all(8.0), + child: FloatingActionButton( + onPressed: () { + setState(() { + isExpanded = false; + }); + }, + child: Icon(Icons.close, + color: Color.fromARGB(255, 223, 84, 84)), + backgroundColor: + Color.fromARGB(255, 255, 255, 255), + shape: CircleBorder(), + ), + ) + ], + ), + ) + : GestureDetector( + onTap: () { + setState(() { + isExpanded = true; + }); + }, + child: Stack( + alignment: Alignment.topRight, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Image.asset( + 'assets/images/main-bg.jpeg', + fit: BoxFit.cover, + width: 100, + height: 120, + ), + ), + Padding( + padding: EdgeInsets.all(4.0), + child: Icon(Icons.circle, + size: 50, + color: Color.fromARGB(255, 255, 255, 255)), + ), + ], + ), + ), + ), + ], + )), ); } } diff --git a/game/lib/gameplay/gameplay_page.dart b/game/lib/gameplay/gameplay_page.dart index d88258b9..daebe849 100644 --- a/game/lib/gameplay/gameplay_page.dart +++ b/game/lib/gameplay/gameplay_page.dart @@ -122,40 +122,31 @@ class _GameplayPageState extends State { child: Stack( alignment: Alignment.bottomCenter, children: [ - //Event Picture and google map - Stack(alignment: Alignment.topRight, children: [ - Container( - height: double.infinity, - width: double.infinity, - //Change to challenge picture - child: Image.network( - 'https://picsum.photos/500/1500', - fit: BoxFit.fitWidth, - ), - ), - Container( - height: 100, - width: 100, - padding: EdgeInsets.all(10), - margin: EdgeInsets.only(top: 15, right: 15), - child: Text( - "MAP", - style: TextStyle(fontSize: 21, color: Color(0xFFED5656)), - ), - decoration: BoxDecoration( - color: Color.fromARGB(255, 255, 255, 255), - borderRadius: BorderRadius.all(Radius.circular(10)), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 1, - blurRadius: 1, - offset: Offset(0, 3), // shadow direction: bottom - ), - ], - ), - ), - ]), + Padding( + padding: EdgeInsets.only(top: 20), child: GameplayMap()), + // //Event Picture + // Container( + // height: 100, + // width: 100, + // padding: EdgeInsets.all(10), + // margin: EdgeInsets.only(top: 15, right: 15), + // child: Text( + // "MAP", + // style: TextStyle(fontSize: 21, color: Color(0xFFED5656)), + // ), + // decoration: BoxDecoration( + // color: Color.fromARGB(255, 255, 255, 255), + // borderRadius: BorderRadius.all(Radius.circular(10)), + // boxShadow: [ + // BoxShadow( + // color: Colors.grey.withOpacity(0.5), + // spreadRadius: 1, + // blurRadius: 1, + // offset: Offset(0, 3), // shadow direction: bottom + // ), + // ], + // ), + // ), Container( margin: EdgeInsets.only(bottom: 70), child: ElevatedButton( diff --git a/game/lib/main.dart b/game/lib/main.dart index cbeaabeb..3b7f40a7 100644 --- a/game/lib/main.dart +++ b/game/lib/main.dart @@ -1,8 +1,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:flutter_config/flutter_config.dart'; + +// imports for google maps +import 'dart:io' show Platform; +import 'dart:async'; +import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +// api and widget imports import 'package:game/api/game_api.dart'; import 'package:game/gameplay/gameplay_page.dart'; +import 'package:game/gameplay/gameplay_map.dart'; import 'package:game/global_leaderboard/global_leaderboard_widget.dart'; import 'package:game/journeys/journeys_page.dart'; import 'package:game/model/challenge_model.dart'; @@ -29,11 +39,47 @@ final LOOPBACK = (Platform.isAndroid ? "http://10.0.2.2:8080" : "http://0.0.0.0:8080"); final API_URL = ENV_URL == "" ? LOOPBACK : ENV_URL; -void main() { +void main() async { print(API_URL); + final GoogleMapsFlutterPlatform platform = GoogleMapsFlutterPlatform.instance; + // should only apply to Android - needs to be tested for iOS + if (platform is GoogleMapsFlutterAndroid) { + (platform as GoogleMapsFlutterAndroid).useAndroidViewSurface = true; + initializeMapRenderer(); + } + // load environment variables + WidgetsFlutterBinding.ensureInitialized(); // Required by FlutterConfig + await FlutterConfig.loadEnvVariables(); + runApp(MyApp()); } +Completer? _initializedRendererCompleter; + +/// Initializes map renderer to the `latest` renderer type. +/// +/// The renderer must be requested before creating GoogleMap instances, +/// as the renderer can be initialized only once per application context. +Future initializeMapRenderer() async { + if (_initializedRendererCompleter != null) { + return _initializedRendererCompleter!.future; + } + + final Completer completer = + Completer(); + _initializedRendererCompleter = completer; + + WidgetsFlutterBinding.ensureInitialized(); + + final GoogleMapsFlutterPlatform platform = GoogleMapsFlutterPlatform.instance; + unawaited((platform as GoogleMapsFlutterAndroid) + .initializeWithRenderer(AndroidMapRenderer.latest) + .then((AndroidMapRenderer initializedRenderer) => + completer.complete(initializedRenderer))); + + return completer.future; +} + final client = ApiClient(storage, API_URL); class MyApp extends StatelessWidget { @@ -76,6 +122,7 @@ class MyApp extends StatelessWidget { theme: ThemeData( fontFamily: 'Poppins', primarySwatch: ColorPalette.BigRed), home: SplashPageWidget(), + // home: GameplayMap(), ))); } } diff --git a/game/lib/preview/preview.dart b/game/lib/preview/preview.dart index fd630947..57a6e51e 100644 --- a/game/lib/preview/preview.dart +++ b/game/lib/preview/preview.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:game/gameplay/gameplay_page.dart'; enum previewType { challenge, journey } @@ -393,6 +394,10 @@ class _PreviewState extends State { side: BorderSide(color: backgroundRed)))), onPressed: () { print("Unimplemented. Starting Challenge!"); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => GameplayPage())); }, child: Text( "Continue exploring", diff --git a/game/lib/profile/achievement_cell.dart b/game/lib/profile/achievement_cell.dart index 9e643935..cd8396f6 100644 --- a/game/lib/profile/achievement_cell.dart +++ b/game/lib/profile/achievement_cell.dart @@ -14,8 +14,8 @@ Widget achievementCell( return Padding( padding: const EdgeInsets.all(8.0), child: Container( - width: 380, - height: 100, + width: 345, + height: 88, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10.0), @@ -29,8 +29,7 @@ Widget achievementCell( child: Row( children: [ Padding( - padding: const EdgeInsets.only( - left: 18.0, top: 15, bottom: 15, right: 15), + padding: const EdgeInsets.all(10), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Image( @@ -49,7 +48,7 @@ Widget achievementCell( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( - width: 250, + width: 215, child: Text( name, overflow: TextOverflow.ellipsis, @@ -66,8 +65,8 @@ Widget achievementCell( children: [ Stack(children: [ Container( - width: 200, - height: 20, + width: 170, + height: 13, alignment: Alignment.centerLeft, child: Container( decoration: new BoxDecoration( @@ -81,8 +80,8 @@ Widget achievementCell( Container( width: (totalTasks > 0 ? tasksFinished / totalTasks : 0) * - 200, - height: 20, + 170, + height: 13, alignment: Alignment.centerLeft, child: Container( decoration: new BoxDecoration( diff --git a/game/lib/profile/completed_cell.dart b/game/lib/profile/completed_cell.dart index c7b141ae..025607e1 100644 --- a/game/lib/profile/completed_cell.dart +++ b/game/lib/profile/completed_cell.dart @@ -17,8 +17,8 @@ Widget completedCell(String name, String picture, String type, String date, return Padding( padding: const EdgeInsets.all(8.0), child: Container( - width: 380, - height: 100, + width: 345, + height: 88, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10.0), @@ -32,8 +32,7 @@ Widget completedCell(String name, String picture, String type, String date, child: Row( children: [ Padding( - padding: const EdgeInsets.only( - left: 18.0, top: 15, bottom: 15, right: 15), + padding: const EdgeInsets.all(10), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Image( @@ -63,7 +62,7 @@ Widget completedCell(String name, String picture, String type, String date, children: [ Text( "From " + type + " - ", - style: TextStyle(color: Colors.grey), + style: TextStyle(color: Colors.grey, fontSize: 12), ), Text(date) ], diff --git a/game/lib/profile/profile_page.dart b/game/lib/profile/profile_page.dart index f3584264..23c8a08b 100644 --- a/game/lib/profile/profile_page.dart +++ b/game/lib/profile/profile_page.dart @@ -19,35 +19,24 @@ class _ProfilePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Color.fromARGB(255, 255, 245, 234), - body: Container( - child: Column( - children: [ - Align( - alignment: Alignment.topRight, - child: Padding( - padding: const EdgeInsets.only(top: 35, right: 20), - child: IconButton( - alignment: Alignment.topRight, - icon: Icon(Icons.settings, size: 40), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SettingsPage())); - }), - ), - ), - Container( - height: 120, - child: Stack( - children: [ - Center( - child: Image( - image: AssetImage("assets/images/user_2@2x.png"), + backgroundColor: Color.fromARGB(255, 255, 245, 234), + body: SafeArea( + child: Container( + child: Column( + children: [ + Stack(fit: StackFit.passthrough, children: [ + Center( + child: Container( + height: 140, + padding: EdgeInsets.only(top: 30), + child: Stack( + children: [ + Center( + child: Image( + image: AssetImage("assets/images/user_2@2x.png"), + ), ), - ), - Align( + Align( alignment: Alignment.bottomCenter, child: Container( height: 30, @@ -66,84 +55,102 @@ class _ProfilePageState extends State { ), ), ), - )), - ], + ), + ), + ], + ), ), ), - Padding( - padding: const EdgeInsets.all(5.0), - child: Text( - "Hanan Abraha", - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + Positioned.directional( + textDirection: TextDirection.rtl, + start: 10, + // top: 22, + child: Padding( + padding: const EdgeInsets.only(top: 10, right: 20), + child: IconButton( + alignment: Alignment.topRight, + icon: Icon(Icons.settings, size: 40), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SettingsPage())); + }), ), ), - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Text("@Hanan Abraha"), + ]), + Padding( + padding: const EdgeInsets.all(5.0), + child: Text( + "Hanan Abraha", + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), - Padding( - padding: const EdgeInsets.only(left: 20, right: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text("Achievements", - style: TextStyle( - fontSize: 20, fontWeight: FontWeight.bold)), - TextButton( - onPressed: () { - // Handle button press, e.g., navigate to details page - print('View Details button pressed'); - }, - child: Text( - 'View Details →', - style: TextStyle( - color: Colors.black, - fontSize: 16.0, - ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text("@Hanan Abraha"), + ), + Padding( + padding: const EdgeInsets.only(left: 24, right: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text("Achievements", + style: + TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + TextButton( + onPressed: () { + // Handle button press, e.g., navigate to details page + print('View Details button pressed'); + }, + child: Text( + 'View Details →', + style: TextStyle( + color: Colors.black, + fontSize: 14.0, ), - ) - ], - ), + ), + ) + ], ), - achievementCell("Complete three challenges on the Arts quad", 4, - 6, locationImage), - achievementCell( - "Complete three challenges on the Engineering quad", - 4, - 6, - locationImage), - Padding( - padding: const EdgeInsets.only(left: 20, right: 20.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text("Completed", - style: TextStyle( - fontSize: 20, fontWeight: FontWeight.bold)), - TextButton( - onPressed: () { - // Handle button press, e.g., navigate to details page - print('View Details button pressed'); - }, - child: Text( - 'View More →', - style: TextStyle( - color: Colors.black, - fontSize: 16.0, - ), + ), + achievementCell("Complete three challenges on the Arts quad", 4, 6, + locationImage), + achievementCell("Complete three challenges on the Engineering quad", + 4, 6, locationImage), + Padding( + padding: const EdgeInsets.only(left: 24, right: 24.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text("Completed", + style: + TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + TextButton( + onPressed: () { + // Handle button press, e.g., navigate to details page + print('View Details button pressed'); + }, + child: Text( + 'View More →', + style: TextStyle( + color: Colors.black, + fontSize: 14.0, ), - ) - ], - ), + ), + ) + ], ), - completedCell("Cornell Cafes", locationImage, "Journeys", - "January 19, 2023", "Arts Quad", "Easy", 120), - completedCell("Cornell Cafes", locationImage, "Journeys", - "January 19, 2023", "Arts Quad", "Easy", 120), - ], - ), - )); + ), + completedCell("Cornell Cafes", locationImage, "Journeys", + "January 19, 2023", "Arts Quad", "Easy", 120), + completedCell("Cornell Cafes", locationImage, "Journeys", + "January 19, 2023", "Arts Quad", "Easy", 120), + ], + ), + )), + ); } } diff --git a/game/lib/widget/game_widget.dart b/game/lib/widget/game_widget.dart index b274fb05..b9efb02a 100644 --- a/game/lib/widget/game_widget.dart +++ b/game/lib/widget/game_widget.dart @@ -99,9 +99,9 @@ class _GameWidgetState extends State { curChallenge != null && requiredSizeMet) { final chalLoc = - GeoPoint(curChallenge.lat, curChallenge.long); - final location = GeoPoint( - snapshot.data!.latitude, snapshot.data!.longitude); + GeoPoint(curChallenge.lat, curChallenge.long, 0); + final location = GeoPoint(snapshot.data!.latitude, + snapshot.data!.longitude, snapshot.data!.heading); final distance = location.distanceTo(chalLoc); gameModel.walkingTime = diff --git a/game/pubspec.yaml b/game/pubspec.yaml index 09c2d22b..1a2d1350 100644 --- a/game/pubspec.yaml +++ b/game/pubspec.yaml @@ -50,6 +50,11 @@ dependencies: latlong2: ^0.9.0 device_info_plus: ^9.1.0 flutter_svg: ^2.0.8 + google_maps_flutter: ^2.5.3 + google_maps_flutter_android: ^2.4.2 + google_maps_flutter_platform_interface: ^2.4.3 + location: ^5.0.0 + flutter_config: ^2.0.2 flutter_icons: android: true