From c0f971c0d3827c3f07eb694f56c05dcb503cd028 Mon Sep 17 00:00:00 2001 From: Rafael <1024481@stud.hs-mannheim.de> Date: Sat, 15 Jun 2024 01:25:09 +0200 Subject: [PATCH] Added ExpandableText and made some adjustments. --- lib/components/location_selector.dart | 49 +++++++++++--------- lib/components/text_with_bold.dart | 26 +++++++++++ lib/components/user_tile_likes.dart | 8 ++-- lib/constants.dart | 2 +- lib/pages/chat_page.dart | 19 ++++---- lib/pages/liked_users_page.dart | 36 ++++++--------- lib/pages/user_matching_page.dart | 64 ++++++++++++++++++--------- lib/pages/user_profile_page.dart | 13 +++++- pubspec.lock | 8 ++++ pubspec.yaml | 1 + 10 files changed, 147 insertions(+), 79 deletions(-) create mode 100644 lib/components/text_with_bold.dart diff --git a/lib/components/location_selector.dart b/lib/components/location_selector.dart index 93e3dc6..544a184 100644 --- a/lib/components/location_selector.dart +++ b/lib/components/location_selector.dart @@ -32,28 +32,28 @@ class LocationSelectorState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + ElevatedButton.icon( + icon: const Icon(Icons.my_location), + onPressed: _getCurrentLocation, + label: const Text('Current Position'), + ), + const SizedBox(height: 12), TextField( - // onSubmitted: (loc) => {_searchLocation2(loc)}, controller: _locationController, decoration: InputDecoration( - labelText: 'Enter location', + labelText: 'Search location', suffixIcon: IconButton( icon: const Icon(Icons.search), onPressed: _searchLocation, ), ), ), + const SizedBox(height: 6), Text( errorText != null ? '$errorText' : '', style: const TextStyle(color: Colors.red), ), const SizedBox(height: 20), - ElevatedButton.icon( - icon: const Icon(Icons.my_location), - onPressed: _getCurrentLocation, - label: const Text('Current Position'), - ), - const SizedBox(height: 20), Text('Country: $_country'), Text('City: $_city'), Text('Postal Code: ${_postalCode ?? ''}'), @@ -121,8 +121,6 @@ class LocationSelectorState extends State { nameDetails: true, ); - debugPrint(searchResult.single.address.toString()); // TODO remove - Map? addressData = searchResult.single.address; if (addressData != null) { String street = _getStreetInfo(addressData); @@ -195,10 +193,15 @@ class LocationSelectorState extends State { // Test if location services are enabled. serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { - // Location services are not enabled don't continue + // 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.'); + setState(() { + errorText = 'Location services are disabled. ' + 'Enable location services and try again.'; + }); + + return; } permission = await Geolocator.checkPermission(); @@ -210,17 +213,23 @@ class LocationSelectorState extends State { // Android's shouldShowRequestPermissionRationale // returned true. According to Android guidelines // your App should show an explanatory UI now. - return Future.error('Location permissions are denied'); + setState(() { + errorText = 'Location permissions are denied'; + }); + return; } } if (permission == LocationPermission.deniedForever) { // Permissions are denied forever, handle appropriately. - return Future.error( - 'Location permissions are permanently denied, we cannot request permissions.'); + setState(() { + errorText = + 'Location permissions are permanently denied, cannot request permissions.'; + }); + return; } } catch (e) { - debugPrint('Error getting location permission: $e'); + errorText = 'Error getting location permission: $e'; } try { @@ -229,8 +238,6 @@ class LocationSelectorState extends State { Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high); - debugPrint(position.toString()); // TODO remove - if (isMobile) { // using package geocoding which works for Android and iOS only List placeMarks = await placemarkFromCoordinates( @@ -242,8 +249,10 @@ class LocationSelectorState extends State { _street = placeMark.street ?? ''; _country = placeMark.country ?? ''; _city = placeMark.locality ?? ''; + _administrativeArea = placeMark.administrativeArea; _latitude = position.latitude; _longitude = position.longitude; + errorText = null; }); triggerCallback(); } @@ -285,11 +294,11 @@ class LocationSelectorState extends State { triggerCallback(); } } catch (e) { - debugPrint('Error looking up current location: $e'); + errorText = 'Error looking up current location: $e'; } } } catch (e) { - debugPrint('Error getting current location: $e'); + errorText = 'Error getting current location: $e'; } } diff --git a/lib/components/text_with_bold.dart b/lib/components/text_with_bold.dart new file mode 100644 index 0000000..9bac8a4 --- /dev/null +++ b/lib/components/text_with_bold.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class TextWithBold extends StatelessWidget { + final String? leadingText; + final String boldText; + final String? trailingText; + + const TextWithBold( + {super.key, this.leadingText, required this.boldText, this.trailingText}); + + @override + Widget build(BuildContext context) { + return Text.rich( + TextSpan( + children: [ + TextSpan(text: leadingText), + TextSpan( + text: boldText, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + TextSpan(text: trailingText), + ], + ), + ); + } +} diff --git a/lib/components/user_tile_likes.dart b/lib/components/user_tile_likes.dart index f0a6903..4854b83 100644 --- a/lib/components/user_tile_likes.dart +++ b/lib/components/user_tile_likes.dart @@ -7,7 +7,7 @@ class UserTileLikes extends StatelessWidget { final DocumentSnapshot user; final bool hasMatch; final VoidCallback onUnlike; - final VoidCallback onShowMatchMessage; + final VoidCallback onShowChat; final VoidCallback onViewInfo; const UserTileLikes({ @@ -15,7 +15,7 @@ class UserTileLikes extends StatelessWidget { required this.user, required this.hasMatch, required this.onUnlike, - required this.onShowMatchMessage, + required this.onShowChat, required this.onViewInfo, }); @@ -62,9 +62,9 @@ class UserTileLikes extends StatelessWidget { ), IconButton( icon: hasMatch - ? const Icon(Icons.lock_outline) + ? const Icon(Icons.chat_outlined) : const Icon(Icons.delete_outline, color: Colors.red), - onPressed: hasMatch ? onShowMatchMessage : onUnlike, + onPressed: hasMatch ? onShowChat : onUnlike, ), ], ), diff --git a/lib/constants.dart b/lib/constants.dart index 6de1556..b6a2023 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -4,7 +4,7 @@ class Constants { /// Title of the app static const String appTitle = 'Cofounderella'; static const String appVersion = '1.0.0'; - static const String appCompilationDate = '2024-06-13'; + static const String appCompilationDate = '2024-06-14'; static const String dbCollectionFeedbacks = 'feedbacks'; static const String dbCollectionUsers = 'Users'; diff --git a/lib/pages/chat_page.dart b/lib/pages/chat_page.dart index 10ddc30..d9996eb 100644 --- a/lib/pages/chat_page.dart +++ b/lib/pages/chat_page.dart @@ -24,10 +24,8 @@ class ChatPage extends StatefulWidget { } class _ChatPageState extends State { - // text controller final TextEditingController _messageController = TextEditingController(); - // chat and auth services final ChatService _chatService = ChatService(); final AuthService _authService = AuthService(); @@ -79,7 +77,9 @@ class _ChatPageState extends State { // send message if not empty if (_messageController.text.isNotEmpty) { await _chatService.sendMessage( - widget.receiverID, _messageController.text); + widget.receiverID, + _messageController.text, + ); // clear text controller _messageController.clear(); @@ -95,10 +95,7 @@ class _ChatPageState extends State { body: Column( children: [ // display all messages - Expanded( - child: _buildMessageList(), - ), - + Expanded(child: _buildMessageList()), // user input _buildUserInput(), ], @@ -113,12 +110,12 @@ class _ChatPageState extends State { builder: (context, snapshot) { // errors if (snapshot.hasError) { - return Text("Error: ${snapshot.error}"); + return Text('Error: ${snapshot.error}'); } // loading if (snapshot.connectionState == ConnectionState.waiting) { - return const Text("Loading.."); + return const Text('Loading..'); } // return list view @@ -155,8 +152,8 @@ class _ChatPageState extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 25.0), child: Text( - "${msgDate[0]} ${msgDate[1].substring(0, 5)}", - style: const TextStyle(color: Colors.grey), + '${msgDate[0]} ${msgDate[1].substring(0, 5)}', + style: const TextStyle(color: Colors.grey, fontSize: 10), ), ), ChatBubble( diff --git a/lib/pages/liked_users_page.dart b/lib/pages/liked_users_page.dart index 95993ff..6bb180e 100644 --- a/lib/pages/liked_users_page.dart +++ b/lib/pages/liked_users_page.dart @@ -7,6 +7,7 @@ import '../enumerations.dart'; import '../models/swipe.dart'; import '../services/auth/auth_service.dart'; import '../utils/helper_dialogs.dart'; +import 'chat_page.dart'; import 'user_profile_page.dart'; class LikedUsersPage extends StatefulWidget { @@ -100,26 +101,6 @@ class LikedUsersPageState extends State { } } - void _showMatchMessage() { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('Match Exists'), - content: const Text( - 'You cannot unlike a user with whom you have a match.', - ), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Close'), - ), - ], - ); - }, - ); - } - Future _showConfirmationDialog(String userId, String userName) async { bool? confirm = await showDialog( context: context, @@ -350,8 +331,19 @@ class LikedUsersPageState extends State { setState(() {}); } } - }, //_unlikeUser(user.id), - onShowMatchMessage: _showMatchMessage, + }, + onShowChat: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatPage( + receiverEmail: user[Constants.dbFieldUsersEmail], + receiverID: user[Constants.dbFieldUsersID], + chatTitle: user[Constants.dbFieldUsersName], + ), + ), + ); + }, onViewInfo: () { Navigator.push( context, diff --git a/lib/pages/user_matching_page.dart b/lib/pages/user_matching_page.dart index 02eab74..2701ed4 100644 --- a/lib/pages/user_matching_page.dart +++ b/lib/pages/user_matching_page.dart @@ -6,6 +6,7 @@ import 'package:swipable_stack/swipable_stack.dart'; import '../components/card_overlay.dart'; import '../components/language_list.dart'; +import '../components/text_with_bold.dart'; import '../constants.dart'; import '../forms/matched_screen.dart'; import '../models/language.dart'; @@ -411,6 +412,14 @@ class UserMatchingPageState extends State { String pronoun = getPronoun(userProfile.gender); + String location = + userProfile.locations[Constants.dbDocMainLocation]?.toString() ?? 'N/A'; + if (userProfile.locations.containsKey(Constants.dbDocSecondLocation) && + userProfile.locations[Constants.dbDocSecondLocation] != null) { + location = + '$location and ${userProfile.locations[Constants.dbDocSecondLocation]?.toString() ?? 'N/A'}'; + } + double shortDist = shortestDistanceBetweenUsers(currentUserProfile!, userProfile); double matchScore = calculateMatchScore(currentUserProfile!, userProfile); @@ -435,10 +444,13 @@ class UserMatchingPageState extends State { header: Text( "${matchScore.toStringAsFixed(2)}%", style: const TextStyle( - fontWeight: FontWeight.bold, fontSize: 16.0), + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), ), circularStrokeCap: CircularStrokeCap.round, progressColor: _getProgressColor(matchScore), + backgroundWidth: 2, ), Positioned( bottom: 5, // Manually adjusted avatar position @@ -460,27 +472,36 @@ class UserMatchingPageState extends State { const SizedBox(height: 8), Center( child: Text( - '${userProfile.name} ${ageInfo(userProfile.born)}'.trim(), - style: const TextStyle(fontSize: 24)), + '${userProfile.name} ${ageInfo(userProfile.born)}'.trim(), + style: const TextStyle(fontSize: 24), + ), ), const SizedBox(height: 8), - Text( - 'Would like to team up with someone who has experience in ' - '${userProfile.skillsSought.map((x) => x.displayName).join(', ')}.', + TextWithBold( + leadingText: + 'Lives in $location which is ${shortDist <= 20 ? 'only ' : ''}about ', + boldText: '${shortDist.toStringAsFixed(0)} km', + trailingText: ' away from you.', ), - Text( - '$pronoun brings skills and experience in ' - '${userProfile.skills.map((x) => x.displayName).join(', ')}', + const SizedBox(height: 6), + TextWithBold( + leadingText: + 'Would like to team up with someone who has experience in ', + boldText: userProfile.skillsSought + .map((x) => x.displayName) + .join(', '), + trailingText: '.', + ), + const SizedBox(height: 8), + TextWithBold( + leadingText: '$pronoun brings skills and experience in ', + boldText: + userProfile.skills.map((x) => x.displayName).join(', '), ), Text( 'and is willing to commit in ' '${userProfile.availability.commitmentText}.', ), - Text( - 'Lives in ${userProfile.locations[Constants.dbDocMainLocation]?.toString() ?? 'N/A'}' - ' and ${userProfile.locations[Constants.dbDocSecondLocation]?.toString() ?? 'N/A'}' - ' which is ${shortDist <= 20 ? 'only ' : ''}about ${shortDist.toStringAsFixed(0)} km away from you.', - ), const SizedBox(height: 8), const Row( children: [ @@ -510,14 +531,17 @@ class UserMatchingPageState extends State { children: [ const Icon(Icons.person_search, size: 64), const SizedBox(height: 20), - const Text('You\'ve viewed all available profiles.', - textAlign: TextAlign.center, - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), + const Text( + 'You\'ve viewed all available profiles.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), const SizedBox(height: 60), const Text( - 'Would you like to do another run and see the remaining profiles again?', - textAlign: TextAlign.center, - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), + 'Would you like to do another run and see the remaining profiles again?', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), const SizedBox(height: 20), ElevatedButton( onPressed: () { diff --git a/lib/pages/user_profile_page.dart b/lib/pages/user_profile_page.dart index 4400037..8d64b0a 100644 --- a/lib/pages/user_profile_page.dart +++ b/lib/pages/user_profile_page.dart @@ -1,3 +1,4 @@ +import 'package:expandable_text/expandable_text.dart'; import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; @@ -633,7 +634,17 @@ class _UserProfilePageState extends State { color: Theme.of(context).colorScheme.primary, ), ), - Text(myData.bio ?? 'n/a', style: const TextStyle(fontSize: 16)), + ExpandableText( + linkEllipsis: false, + collapseOnTextTap: true, + expandOnTextTap: true, + myData.bio ?? 'n/a', + expandText: '[Expand]', + collapseText: '[Collapse]', + maxLines: 3, + linkColor: Colors.blue, + style: const TextStyle(fontSize: 16), + ), ], ), ), diff --git a/pubspec.lock b/pubspec.lock index f6719aa..6150b29 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -105,6 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + expandable_text: + dependency: "direct main" + description: + name: expandable_text + sha256: "7d03ea48af6987b20ece232678b744862aa3250d4a71e2aaf1e4af90015d76b1" + url: "https://pub.dev" + source: hosted + version: "2.3.0" fake_async: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 10ac70a..d594718 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: image_cropper: ^6.0.0 percent_indicator: ^4.2.3 osm_nominatim: ^3.0.0 + expandable_text: ^2.3.0 dev_dependencies: flutter_test: