Added ExpandableText and made some adjustments.
parent
9a7a8f9f39
commit
c0f971c0d3
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -460,27 +472,36 @@ class UserMatchingPageState extends State<UserMatchingPage> {
|
|||
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<UserMatchingPage> {
|
|||
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: () {
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue