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(
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<LocationSelector> {
nameDetails: true,
);
debugPrint(searchResult.single.address.toString()); // TODO remove
Map<String, dynamic>? addressData = searchResult.single.address;
if (addressData != null) {
String street = _getStreetInfo(addressData);
@ -195,10 +193,15 @@ class LocationSelectorState extends State<LocationSelector> {
// 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<LocationSelector> {
// 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<LocationSelector> {
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<Placemark> placeMarks = await placemarkFromCoordinates(
@ -242,8 +249,10 @@ class LocationSelectorState extends State<LocationSelector> {
_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<LocationSelector> {
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';
}
}

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 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,
),
],
),

View File

@ -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';

View File

@ -24,10 +24,8 @@ class ChatPage extends StatefulWidget {
}
class _ChatPageState extends State<ChatPage> {
// 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<ChatPage> {
// 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<ChatPage> {
body: Column(
children: [
// display all messages
Expanded(
child: _buildMessageList(),
),
Expanded(child: _buildMessageList()),
// user input
_buildUserInput(),
],
@ -113,12 +110,12 @@ class _ChatPageState extends State<ChatPage> {
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<ChatPage> {
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(

View File

@ -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<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 {
bool? confirm = await showDialog<bool>(
context: context,
@ -350,8 +331,19 @@ class LikedUsersPageState extends State<LikedUsersPage> {
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,

View File

@ -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<UserMatchingPage> {
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<UserMatchingPage> {
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
@ -461,26 +473,35 @@ class UserMatchingPageState extends State<UserMatchingPage> {
Center(
child: Text(
'${userProfile.name} ${ageInfo(userProfile.born)}'.trim(),
style: const TextStyle(fontSize: 24)),
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<UserMatchingPage> {
children: [
const Icon(Icons.person_search, size: 64),
const SizedBox(height: 20),
const Text('You\'ve viewed all available profiles.',
const Text(
'You\'ve viewed all available profiles.',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
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)),
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {

View File

@ -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<UserProfilePage> {
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"
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:

View File

@ -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: