feat(chat): hero animation + chat select scren

This commit is contained in:
mso7nueh
2024-06-25 15:26:03 +03:00
parent 05ccfd289c
commit 2337bdd368
12 changed files with 211 additions and 200 deletions

View File

@@ -202,16 +202,14 @@ class AppRouter {
path: '/chat-select-screen',
name: 'chatSelectScreen',
builder: (context, state) => ChatSelectScreen(),
routes: [
GoRoute(
path: 'chat-screen',
name: 'chatScreen',
builder: (context, state) => ChatScreen(chatId: state.extra as String),
),
],
),
],
),
GoRoute(
path: '/chat-screen',
name: 'chatScreen',
builder: (context, state) => ChatScreen(chatId: state.extra as String),
),
GoRoute(
path: '/my-settings-screen',
name: 'mySettingsScreen',

View File

@@ -38,6 +38,9 @@ class InitializationCubit extends Cubit<InitializationState> {
await pusher.connect();
// Channel channel = pusher.subscribe('private-direct-chat.');
// channel.registerListener(classId, method);
emit(const InitializationState.success());
} catch (e) {
emit(InitializationState.error(errorMessage: e.toString()));

View File

@@ -81,119 +81,121 @@ class ChatScreen extends StatelessWidget {
],
),
),
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Container(
color: Color(0xFFF0F2F5),
width: double.maxFinite,
child: CustomScrollView(
reverse: true,
slivers: [
SliverPadding(
padding: EdgeInsets.symmetric(vertical: 16),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ChatBubbleWidget(
text: state.chatRoomModel.messages[state.chatRoomModel.messages.length - index - 1].data,
isCurrentUser: state.chatRoomModel.users['to_chat_user']!.id !=
state.chatRoomModel.messages[state.chatRoomModel.messages.length - index - 1].authorId,
mediaData: state.chatRoomModel.messages[state.chatRoomModel.messages.length - index - 1].mediaData,
body: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Container(
color: Color(0xFFF0F2F5),
width: double.maxFinite,
child: CustomScrollView(
reverse: true,
slivers: [
SliverPadding(
padding: EdgeInsets.symmetric(vertical: 16),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ChatBubbleWidget(
text: state.chatRoomModel.messages[state.chatRoomModel.messages.length - index - 1].data,
isCurrentUser: state.chatRoomModel.users['to_chat_user']!.id !=
state.chatRoomModel.messages[state.chatRoomModel.messages.length - index - 1].authorId,
mediaData: state.chatRoomModel.messages[state.chatRoomModel.messages.length - index - 1].mediaData,
),
childCount: state.chatRoomModel.messages.length,
),
childCount: state.chatRoomModel.messages.length,
),
),
],
),
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 8),
InkWell(
onTap: () {},
borderRadius: BorderRadiusStyle.circleBorder17,
child: Ink(
height: 32,
width: 32,
child: Icon(
Icons.attach_file_rounded,
size: 28,
color: Color(0xFF858E99),
),
),
),
],
),
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 8),
InkWell(
onTap: () {},
borderRadius: BorderRadiusStyle.circleBorder17,
child: Ink(
height: 32,
width: 32,
child: Icon(
Icons.attach_file_rounded,
size: 28,
color: Color(0xFF858E99),
),
),
),
SizedBox(width: 8),
SizedBox(
width: mediaQueryData.size.width - 96,
child: TextFormField(
maxLines: 5,
minLines: 1,
keyboardType: TextInputType.multiline,
style: TextStyle(
fontSize: 16,
fontFamily: 'Mulish',
fontWeight: FontWeight.w400,
color: Colors.black,
),
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
hintText: 'Сообщение',
hintStyle: TextStyle(
SizedBox(width: 8),
SizedBox(
width: mediaQueryData.size.width - 96,
child: TextFormField(
maxLines: 5,
minLines: 1,
keyboardType: TextInputType.multiline,
style: TextStyle(
fontSize: 16,
fontFamily: 'Mulish',
fontWeight: FontWeight.w400,
color: Color(0xFFAEAEB2),
color: Colors.black,
),
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadiusStyle.circleBorder17,
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
hintText: 'Сообщение',
hintStyle: TextStyle(
fontSize: 16,
fontFamily: 'Mulish',
fontWeight: FontWeight.w400,
color: Color(0xFFAEAEB2),
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadiusStyle.circleBorder17,
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadiusStyle.circleBorder17,
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadiusStyle.circleBorder17,
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadiusStyle.circleBorder17,
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadiusStyle.circleBorder17,
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
),
),
),
),
SizedBox(width: 8),
InkWell(
borderRadius: BorderRadiusStyle.circleBorder17,
onTap: () {},
child: Ink(
height: 32,
width: 32,
child: Icon(
Icons.send_outlined,
size: 28,
color: Color(0xFF858E99),
SizedBox(width: 8),
InkWell(
borderRadius: BorderRadiusStyle.circleBorder17,
onTap: () {},
child: Ink(
height: 32,
width: 32,
child: Icon(
Icons.send_outlined,
size: 28,
color: Color(0xFF858E99),
),
),
),
),
SizedBox(width: 8),
],
SizedBox(width: 8),
],
),
),
),
],
],
),
),
);
}

View File

@@ -27,17 +27,22 @@ class ChatSelectBloc extends Bloc<ChatSelectEvent, ChatSelectState> {
List<ChatItemModel> chats = [];
for (int i = 0; i < chatRooms.length; i++) {
String text = chatRooms[i]['last_message']['data'];
if (chatRooms[i]['last_message']['is_author_from_user']) {
if (chatRooms[i]['last_message']['is_author_message_auth_user'] != null) {
text = "Вы: " + text;
}
chats.add(ChatItemModel(
chats.add(
ChatItemModel(
id: chatRooms[i]['id'],
userImage: chatRooms[i]['users']['to_chat_user']
['profile_photo_path'],
userImage: chatRooms[i]['users']['to_chat_user']['profile_photo_path'],
username: chatRooms[i]['users']['to_chat_user']['name'],
dateText: DateFormat('dd.MM').format(
DateTime.parse(chatRooms[i]['last_message']['updated_at'])),
greetingText: text));
DateTime.parse(
chatRooms[i]['last_message']['updated_at'],
),
),
greetingText: text,
),
);
}
return chats;
}
@@ -75,8 +80,7 @@ class ChatSelectBloc extends Bloc<ChatSelectEvent, ChatSelectState> {
}
} else {
// Один или несколько ключевых полей отсутствуют, обрабатываем ошибку
emit(ChatSelectErrorState(
'Некоторые необходимые данные отсутствуют.'));
emit(ChatSelectErrorState('Некоторые необходимые данные отсутствуют.'));
}
} else {
// Статус ответа не равен 200, обрабатываем ошибку

View File

@@ -64,7 +64,10 @@ class ChatSelectScreen extends StatelessWidget {
),
);
} else if (state is ChatSelectErrorState) {
return Center(child: Text(state.errorMessage));
return Padding(
padding: const EdgeInsets.all(16),
child: Center(child: Text(state.errorMessage)),
);
} else {
return Scaffold(
body: Center(

View File

@@ -4,26 +4,23 @@ import 'package:eatvio_app/core/app_export.dart';
// ignore_for_file: must_be_immutable
class ChatItemModel {
ChatItemModel(
{this.userImage,
this.username,
this.dateText,
this.greetingText,
required this.id}) {
String? userImage;
String? username;
String? dateText;
String? greetingText;
String id;
ChatItemModel({
this.userImage,
this.username,
this.dateText,
this.greetingText,
required this.id,
}) {
userImage = userImage ?? ImageConstant.imageNotFound;
username = username;
dateText = dateText;
greetingText = greetingText;
id = id;
}
String? userImage;
String? username;
String? dateText;
String? greetingText;
String id;
}

View File

@@ -5,9 +5,11 @@ import 'package:equatable/equatable.dart';
/// This class defines the variables used in the [chat_select_screen],
/// and is typically used to hold data that is passed between different parts of the application.
class ChatSelectModel extends Equatable {
ChatSelectModel({this.chatList = const []});
List<ChatItemModel> chatList;
ChatSelectModel copyWith({List<ChatItemModel>? chatList}) {
return ChatSelectModel(
chatList: chatList ?? this.chatList,

View File

@@ -15,12 +15,13 @@ class ChatEntryWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => GoRouter.of(context).goNamed(
return InkWell(
borderRadius: BorderRadiusStyle.circleBorder8,
onTap: () => GoRouter.of(context).pushNamed(
'chatScreen',
extra: chatEntryObj.id.toString(),
),
child: Container(
child: Ink(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadiusStyle.circleBorder8,

View File

@@ -1,6 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:photo_view/photo_view.dart';
@@ -34,37 +35,32 @@ class _PhotoViewScreenState extends State<PhotoViewScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
centerTitle: true,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.black,
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.light,
return DismissiblePage(
minScale: 1,
onDismissed: () => GoRouter.of(context).pop(),
direction: DismissiblePageDismissDirection.vertical,
isFullScreen: true,
child: PhotoViewGallery.builder(
backgroundDecoration: BoxDecoration(
color: Colors.transparent,
),
backgroundColor: Colors.black,
foregroundColor: Colors.white,
title: Text(
(imageIndex + 1).toString() + ' из ' + widget.imageUrls.length.toString(),
),
),
body: Dismissible(
direction: DismissDirection.vertical,
key: const Key('key'),
onDismissed: (_) => GoRouter.of(context).pop(),
resizeDuration: Duration(microseconds: 1),
child: Container(
child: PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
builder: (BuildContext context, int index) {
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(widget.imageUrls[index]),
initialScale: PhotoViewComputedScale.contained,
minScale: PhotoViewComputedScale.contained,
heroAttributes: PhotoViewHeroAttributes(tag: widget.imageUrls[index]),
errorBuilder: (context, object, stackTrace) {
return Column(
scrollPhysics: const BouncingScrollPhysics(),
builder: (BuildContext context, int index) {
return PhotoViewGalleryPageOptions(
imageProvider: CachedNetworkImageProvider(
widget.imageUrls[index],
),
initialScale: PhotoViewComputedScale.contained,
minScale: PhotoViewComputedScale.contained,
heroAttributes: PhotoViewHeroAttributes(
tag: widget.imageUrls[index],
transitionOnUserGestures: true,
),
errorBuilder: (context, object, stackTrace) {
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: Column(
children: [
SizedBox(height: 32),
Text('Файл'),
@@ -96,28 +92,28 @@ class _PhotoViewScreenState extends State<PhotoViewScreen> {
),
),
],
);
},
),
),
);
},
itemCount: widget.imageUrls.length,
loadingBuilder: (context, event) => Center(
child: Container(
width: 20.0,
height: 20.0,
child: CircularProgressIndicator(
value: event == null ? 0 : event.cumulativeBytesLoaded / event.expectedTotalBytes!,
),
),
);
},
itemCount: widget.imageUrls.length,
loadingBuilder: (context, event) => Center(
child: Container(
width: 20.0,
height: 20.0,
child: CircularProgressIndicator(
value: event == null ? 0 : event.cumulativeBytesLoaded / event.expectedTotalBytes!,
),
pageController: pageController,
onPageChanged: (page) {
setState(() {
imageIndex = page;
});
},
),
),
pageController: pageController,
onPageChanged: (page) {
setState(() {
imageIndex = page;
});
},
),
);
}

View File

@@ -1,8 +1,9 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart';
import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart';
import 'package:eatvio_app/theme/app_decoration.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:url_launcher/url_launcher.dart';
class ChatBubbleWidget extends StatelessWidget {
@@ -71,18 +72,21 @@ class ChatBubbleWidget extends StatelessWidget {
}
return GestureDetector(
onTap: () {
GoRouter.of(context).pushNamed(
'photoViewScreen',
extra: mediaData!.map((e) => dotenv.env['STORAGE_URL']! + e.toString()).toList(),
pathParameters: {
'index': index.toString(),
},
context.pushTransparentRoute(
PhotoViewScreen(
imageUrls: mediaData!.map((e) => dotenv.env['STORAGE_URL']! + e.toString()).toList(),
index: index,
),
);
},
child: ClipRRect(
borderRadius: BorderRadiusStyle.roundedBorder8,
child: CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + '${mediaData![index]}',
child: Hero(
tag: dotenv.env['STORAGE_URL']! + '${mediaData![index]}',
transitionOnUserGestures: true,
child: ClipRRect(
borderRadius: BorderRadiusStyle.roundedBorder8,
child: CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + '${mediaData![index]}',
),
),
),
);

View File

@@ -175,13 +175,13 @@ class MyProfileScreen extends StatelessWidget {
}
addImage(BuildContext context) async {
final picker = ImagePicker();
List<XFile>? images = await picker.pickMultiImage();
if (images.isNotEmpty) {
for (var image in images) {
context.read<MyProfileBloc>().add(UploadAvatar(image));
}
final picker = ImagePicker();
List<XFile>? images = await picker.pickMultiImage();
if (images.isNotEmpty) {
for (var image in images) {
context.read<MyProfileBloc>().add(UploadAvatar(image));
}
}
}
onTapSettings(BuildContext context) {
@@ -192,7 +192,7 @@ class MyProfileScreen extends StatelessWidget {
_buildCarousel(BuildContext contex, MyProfileLoadedState state) {
return CarouselSlider(
options: CarouselOptions(height: 320.0),
options: CarouselOptions(height: 340),
items: [1, 2, 3].map((chartType) {
return Builder(
builder: (BuildContext context) {

View File

@@ -3,7 +3,7 @@ description: A new Flutter project.
version: 0.0.0+1
environment:
sdk: '>=3.3.4 <4.0.0'
sdk: ">=3.3.4 <4.0.0"
dependencies:
flutter:
@@ -60,8 +60,9 @@ dependencies:
dio: ^5.4.3+1
# websocket
pusher_client: ^2.0.0
# photo gallery viewer
# photo gallery viewer + dismissible page
photo_view: ^0.15.0
dismissible_page: ^1.0.2
#url launcher
url_launcher: ^6.3.0
@@ -94,4 +95,4 @@ flutter:
- asset: assets/fonts/MulishRegular.ttf
weight: 400
- asset: assets/fonts/MulishSemiBold.ttf
weight: 600
weight: 600