Added ExpandableText and made some adjustments.

master
Rafael 2024-06-15 01:25:09 +02:00
parent 9a7a8f9f39
commit c0f971c0d3
10 changed files with 147 additions and 79 deletions

View File

@ -32,28 +32,28 @@ class LocationSelectorState extends State<LocationSelector> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ElevatedButton.icon(
icon: const Icon(Icons.my_location),
onPressed: _getCurrentLocation,
label: const Text('Current Position'),
),
const SizedBox(height: 12),
TextField( TextField(
// onSubmitted: (loc) => {_searchLocation2(loc)},
controller: _locationController, controller: _locationController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Enter location', labelText: 'Search location',
suffixIcon: IconButton( suffixIcon: IconButton(
icon: const Icon(Icons.search), icon: const Icon(Icons.search),
onPressed: _searchLocation, onPressed: _searchLocation,
), ),
), ),
), ),
const SizedBox(height: 6),
Text( Text(
errorText != null ? '$errorText' : '', errorText != null ? '$errorText' : '',
style: const TextStyle(color: Colors.red), style: const TextStyle(color: Colors.red),
), ),
const SizedBox(height: 20), 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('Country: $_country'),
Text('City: $_city'), Text('City: $_city'),
Text('Postal Code: ${_postalCode ?? ''}'), Text('Postal Code: ${_postalCode ?? ''}'),
@ -121,8 +121,6 @@ class LocationSelectorState extends State<LocationSelector> {
nameDetails: true, nameDetails: true,
); );
debugPrint(searchResult.single.address.toString()); // TODO remove
Map<String, dynamic>? addressData = searchResult.single.address; Map<String, dynamic>? addressData = searchResult.single.address;
if (addressData != null) { if (addressData != null) {
String street = _getStreetInfo(addressData); String street = _getStreetInfo(addressData);
@ -195,10 +193,15 @@ class LocationSelectorState extends State<LocationSelector> {
// Test if location services are enabled. // Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled(); serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) { 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 // accessing the position and request users of the
// App to enable the location services. // 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(); permission = await Geolocator.checkPermission();
@ -210,17 +213,23 @@ class LocationSelectorState extends State<LocationSelector> {
// Android's shouldShowRequestPermissionRationale // Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines // returned true. According to Android guidelines
// your App should show an explanatory UI now. // 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) { if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately. // Permissions are denied forever, handle appropriately.
return Future.error( setState(() {
'Location permissions are permanently denied, we cannot request permissions.'); errorText =
'Location permissions are permanently denied, cannot request permissions.';
});
return;
} }
} catch (e) { } catch (e) {
debugPrint('Error getting location permission: $e'); errorText = 'Error getting location permission: $e';
} }
try { try {
@ -229,8 +238,6 @@ class LocationSelectorState extends State<LocationSelector> {
Position position = await Geolocator.getCurrentPosition( Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high); desiredAccuracy: LocationAccuracy.high);
debugPrint(position.toString()); // TODO remove
if (isMobile) { if (isMobile) {
// using package geocoding which works for Android and iOS only // using package geocoding which works for Android and iOS only
List<Placemark> placeMarks = await placemarkFromCoordinates( List<Placemark> placeMarks = await placemarkFromCoordinates(
@ -242,8 +249,10 @@ class LocationSelectorState extends State<LocationSelector> {
_street = placeMark.street ?? ''; _street = placeMark.street ?? '';
_country = placeMark.country ?? ''; _country = placeMark.country ?? '';
_city = placeMark.locality ?? ''; _city = placeMark.locality ?? '';
_administrativeArea = placeMark.administrativeArea;
_latitude = position.latitude; _latitude = position.latitude;
_longitude = position.longitude; _longitude = position.longitude;
errorText = null;
}); });
triggerCallback(); triggerCallback();
} }
@ -285,11 +294,11 @@ class LocationSelectorState extends State<LocationSelector> {
triggerCallback(); triggerCallback();
} }
} catch (e) { } catch (e) {
debugPrint('Error looking up current location: $e'); errorText = 'Error looking up current location: $e';
} }
} }
} catch (e) { } catch (e) {
debugPrint('Error getting current location: $e'); errorText = 'Error getting current location: $e';
} }
} }

View File

@ -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),
],
),
);
}
}

View File

@ -7,7 +7,7 @@ class UserTileLikes extends StatelessWidget {
final DocumentSnapshot user; final DocumentSnapshot user;
final bool hasMatch; final bool hasMatch;
final VoidCallback onUnlike; final VoidCallback onUnlike;
final VoidCallback onShowMatchMessage; final VoidCallback onShowChat;
final VoidCallback onViewInfo; final VoidCallback onViewInfo;
const UserTileLikes({ const UserTileLikes({
@ -15,7 +15,7 @@ class UserTileLikes extends StatelessWidget {
required this.user, required this.user,
required this.hasMatch, required this.hasMatch,
required this.onUnlike, required this.onUnlike,
required this.onShowMatchMessage, required this.onShowChat,
required this.onViewInfo, required this.onViewInfo,
}); });
@ -62,9 +62,9 @@ class UserTileLikes extends StatelessWidget {
), ),
IconButton( IconButton(
icon: hasMatch icon: hasMatch
? const Icon(Icons.lock_outline) ? const Icon(Icons.chat_outlined)
: const Icon(Icons.delete_outline, color: Colors.red), : const Icon(Icons.delete_outline, color: Colors.red),
onPressed: hasMatch ? onShowMatchMessage : onUnlike, onPressed: hasMatch ? onShowChat : onUnlike,
), ),
], ],
), ),

View File

@ -4,7 +4,7 @@ class Constants {
/// Title of the app /// Title of the app
static const String appTitle = 'Cofounderella'; static const String appTitle = 'Cofounderella';
static const String appVersion = '1.0.0'; 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 dbCollectionFeedbacks = 'feedbacks';
static const String dbCollectionUsers = 'Users'; static const String dbCollectionUsers = 'Users';

View File

@ -24,10 +24,8 @@ class ChatPage extends StatefulWidget {
} }
class _ChatPageState extends State<ChatPage> { class _ChatPageState extends State<ChatPage> {
// text controller
final TextEditingController _messageController = TextEditingController(); final TextEditingController _messageController = TextEditingController();
// chat and auth services
final ChatService _chatService = ChatService(); final ChatService _chatService = ChatService();
final AuthService _authService = AuthService(); final AuthService _authService = AuthService();
@ -79,7 +77,9 @@ class _ChatPageState extends State<ChatPage> {
// send message if not empty // send message if not empty
if (_messageController.text.isNotEmpty) { if (_messageController.text.isNotEmpty) {
await _chatService.sendMessage( await _chatService.sendMessage(
widget.receiverID, _messageController.text); widget.receiverID,
_messageController.text,
);
// clear text controller // clear text controller
_messageController.clear(); _messageController.clear();
@ -95,10 +95,7 @@ class _ChatPageState extends State<ChatPage> {
body: Column( body: Column(
children: [ children: [
// display all messages // display all messages
Expanded( Expanded(child: _buildMessageList()),
child: _buildMessageList(),
),
// user input // user input
_buildUserInput(), _buildUserInput(),
], ],
@ -113,12 +110,12 @@ class _ChatPageState extends State<ChatPage> {
builder: (context, snapshot) { builder: (context, snapshot) {
// errors // errors
if (snapshot.hasError) { if (snapshot.hasError) {
return Text("Error: ${snapshot.error}"); return Text('Error: ${snapshot.error}');
} }
// loading // loading
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading.."); return const Text('Loading..');
} }
// return list view // return list view
@ -155,8 +152,8 @@ class _ChatPageState extends State<ChatPage> {
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0), padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Text( child: Text(
"${msgDate[0]} ${msgDate[1].substring(0, 5)}", '${msgDate[0]} ${msgDate[1].substring(0, 5)}',
style: const TextStyle(color: Colors.grey), style: const TextStyle(color: Colors.grey, fontSize: 10),
), ),
), ),
ChatBubble( ChatBubble(

View File

@ -7,6 +7,7 @@ import '../enumerations.dart';
import '../models/swipe.dart'; import '../models/swipe.dart';
import '../services/auth/auth_service.dart'; import '../services/auth/auth_service.dart';
import '../utils/helper_dialogs.dart'; import '../utils/helper_dialogs.dart';
import 'chat_page.dart';
import 'user_profile_page.dart'; import 'user_profile_page.dart';
class LikedUsersPage extends StatefulWidget { class LikedUsersPage extends StatefulWidget {
@ -100,26 +101,6 @@ class LikedUsersPageState extends State<LikedUsersPage> {
} }
} }
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<bool?> _showConfirmationDialog(String userId, String userName) async { Future<bool?> _showConfirmationDialog(String userId, String userName) async {
bool? confirm = await showDialog<bool>( bool? confirm = await showDialog<bool>(
context: context, context: context,
@ -350,8 +331,19 @@ class LikedUsersPageState extends State<LikedUsersPage> {
setState(() {}); 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: () { onViewInfo: () {
Navigator.push( Navigator.push(
context, context,

View File

@ -6,6 +6,7 @@ import 'package:swipable_stack/swipable_stack.dart';
import '../components/card_overlay.dart'; import '../components/card_overlay.dart';
import '../components/language_list.dart'; import '../components/language_list.dart';
import '../components/text_with_bold.dart';
import '../constants.dart'; import '../constants.dart';
import '../forms/matched_screen.dart'; import '../forms/matched_screen.dart';
import '../models/language.dart'; import '../models/language.dart';
@ -411,6 +412,14 @@ class UserMatchingPageState extends State<UserMatchingPage> {
String pronoun = getPronoun(userProfile.gender); 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 = double shortDist =
shortestDistanceBetweenUsers(currentUserProfile!, userProfile); shortestDistanceBetweenUsers(currentUserProfile!, userProfile);
double matchScore = calculateMatchScore(currentUserProfile!, userProfile); double matchScore = calculateMatchScore(currentUserProfile!, userProfile);
@ -435,10 +444,13 @@ class UserMatchingPageState extends State<UserMatchingPage> {
header: Text( header: Text(
"${matchScore.toStringAsFixed(2)}%", "${matchScore.toStringAsFixed(2)}%",
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0), fontWeight: FontWeight.bold,
fontSize: 16.0,
),
), ),
circularStrokeCap: CircularStrokeCap.round, circularStrokeCap: CircularStrokeCap.round,
progressColor: _getProgressColor(matchScore), progressColor: _getProgressColor(matchScore),
backgroundWidth: 2,
), ),
Positioned( Positioned(
bottom: 5, // Manually adjusted avatar position bottom: 5, // Manually adjusted avatar position
@ -460,27 +472,36 @@ class UserMatchingPageState extends State<UserMatchingPage> {
const SizedBox(height: 8), const SizedBox(height: 8),
Center( Center(
child: Text( child: Text(
'${userProfile.name} ${ageInfo(userProfile.born)}'.trim(), '${userProfile.name} ${ageInfo(userProfile.born)}'.trim(),
style: const TextStyle(fontSize: 24)), style: const TextStyle(fontSize: 24),
),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( TextWithBold(
'Would like to team up with someone who has experience in ' leadingText:
'${userProfile.skillsSought.map((x) => x.displayName).join(', ')}.', 'Lives in $location which is ${shortDist <= 20 ? 'only ' : ''}about ',
boldText: '${shortDist.toStringAsFixed(0)} km',
trailingText: ' away from you.',
), ),
Text( const SizedBox(height: 6),
'$pronoun brings skills and experience in ' TextWithBold(
'${userProfile.skills.map((x) => x.displayName).join(', ')}', 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( Text(
'and is willing to commit in ' 'and is willing to commit in '
'${userProfile.availability.commitmentText}.', '${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 SizedBox(height: 8),
const Row( const Row(
children: [ children: [
@ -510,14 +531,17 @@ class UserMatchingPageState extends State<UserMatchingPage> {
children: [ children: [
const Icon(Icons.person_search, size: 64), const Icon(Icons.person_search, size: 64),
const SizedBox(height: 20), const SizedBox(height: 20),
const Text('You\'ve viewed all available profiles.', const Text(
textAlign: TextAlign.center, 'You\'ve viewed all available profiles.',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), textAlign: TextAlign.center,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 60), const SizedBox(height: 60),
const Text( const Text(
'Would you like to do another run and see the remaining profiles again?', 'Would you like to do another run and see the remaining profiles again?',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20), const SizedBox(height: 20),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {

View File

@ -1,3 +1,4 @@
import 'package:expandable_text/expandable_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_auth/firebase_auth.dart';
@ -633,7 +634,17 @@ class _UserProfilePageState extends State<UserProfilePage> {
color: Theme.of(context).colorScheme.primary, 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),
),
], ],
), ),
), ),

View File

@ -105,6 +105,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" 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: fake_async:
dependency: transitive dependency: transitive
description: description:

View File

@ -49,6 +49,7 @@ dependencies:
image_cropper: ^6.0.0 image_cropper: ^6.0.0
percent_indicator: ^4.2.3 percent_indicator: ^4.2.3
osm_nominatim: ^3.0.0 osm_nominatim: ^3.0.0
expandable_text: ^2.3.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: