Merge branch 'feature/new-ui' into feature/new-chat-screens

This commit is contained in:
Salvatore Giordano
2020-11-23 10:41:59 +01:00
8 changed files with 661 additions and 503 deletions
+2
View File
@@ -13,6 +13,8 @@ jobs:
channel: 'stable'
- name: Get dependencies
run: flutter pub get
- name: Run formatter
run: flutter format -n --set-exit-if-changed .
- name: Coverage fix
run: |
file=test/coverage_helper_test.dart
+105 -102
View File
@@ -94,127 +94,130 @@ class ChooseUserPage extends StatelessWidget {
),
),
Expanded(
child: ListView.separated(
separatorBuilder: (context, i) {
return Container(
width: double.infinity,
color: Colors.black12,
height: 1,
);
},
itemCount: users.length + 1,
itemBuilder: (context, i) {
return [
...users.entries.map((entry) {
final token = entry.key;
final user = entry.value;
return ListTile(
onTap: () async {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => Center(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.white,
),
height: 100,
width: 100,
child: Center(
child: CircularProgressIndicator(),
child: Padding(
padding: const EdgeInsets.only(top: 32),
child: ListView.separated(
separatorBuilder: (context, i) {
return Container(
width: double.infinity,
color: Colors.black12,
height: 1,
);
},
itemCount: users.length + 1,
itemBuilder: (context, i) {
return [
...users.entries.map((entry) {
final token = entry.key;
final user = entry.value;
return ListTile(
onTap: () async {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => Center(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.white,
),
height: 100,
width: 100,
child: Center(
child: CircularProgressIndicator(),
),
),
),
);
final secureStorage = FlutterSecureStorage();
final client = StreamChat.of(context).client;
await client.setUser(
user,
token,
);
secureStorage.write(
key: kStreamApiKey,
value: kDefaultStreamApiKey,
);
secureStorage.write(
key: kStreamUserId,
value: user.id,
);
secureStorage.write(
key: kStreamToken,
value: token,
);
if (!kIsWeb) {
initNotifications(client);
}
Navigator.pop(context);
await Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) {
return StreamChat(
client: client,
child: ChannelListPage(),
);
},
),
);
},
leading: UserAvatar(
user: user,
constraints: BoxConstraints.tight(
Size.fromRadius(20),
),
);
final secureStorage = FlutterSecureStorage();
final client = StreamChat.of(context).client;
await client.setUser(
user,
token,
);
secureStorage.write(
key: kStreamApiKey,
value: kDefaultStreamApiKey,
);
secureStorage.write(
key: kStreamUserId,
value: user.id,
);
secureStorage.write(
key: kStreamToken,
value: token,
);
if (!kIsWeb) {
initNotifications(client);
}
Navigator.pop(context);
await Navigator.pushReplacement(
),
title: Text(
user.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text('Stream test account'),
trailing: SvgPicture.asset(
'assets/icon_arrow_right.svg',
height: 24,
width: 24,
),
);
}),
ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return StreamChat(
client: client,
child: ChannelListPage(),
);
},
builder: (context) => AdvancedOptionsPage(),
),
);
},
leading: UserAvatar(
user: user,
constraints: BoxConstraints.tight(
Size.fromRadius(20),
leading: CircleAvatar(
child: Icon(
StreamIcons.settings,
color: Colors.black,
),
backgroundColor:
StreamChatTheme.of(context).secondaryColor,
),
title: Text(
user.name,
'Advanced Options',
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text('Stream test account'),
subtitle: Text('Custom settings'),
trailing: SvgPicture.asset(
'assets/icon_arrow_right.svg',
height: 24,
width: 24,
clipBehavior: Clip.none,
),
);
}),
ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AdvancedOptionsPage(),
),
);
},
leading: CircleAvatar(
child: Icon(
StreamIcons.settings,
color: Colors.black,
),
backgroundColor:
StreamChatTheme.of(context).secondaryColor,
),
title: Text(
'Advanced Options',
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text('Custom settings'),
trailing: SvgPicture.asset(
'assets/icon_arrow_right.svg',
height: 24,
width: 24,
clipBehavior: Clip.none,
),
),
][i];
},
][i];
},
),
),
),
StreamVersion(),
+110 -99
View File
@@ -1,14 +1,14 @@
import 'dart:io';
import 'package:example/choose_user_page.dart';
import 'package:example/new_chat_screen.dart';
import 'package:example/new_group_chat_screen.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'new_chat_screen.dart';
import 'new_group_chat_screen.dart';
import 'notifications_service.dart';
void main() async {
@@ -48,6 +48,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
//TODO change to system once dark theme is implemented
@@ -75,105 +76,15 @@ class ChannelListPage extends StatelessWidget {
style: TextStyle(color: Colors.black),
),
),
drawer: Drawer(
child: SafeArea(
child: Padding(
padding: EdgeInsets.only(
top: MediaQuery.of(context).viewPadding.top + 8,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(
bottom: 20.0,
left: 8,
),
child: Row(
children: [
UserAvatar(
user: user,
showOnlineStatus: false,
constraints: BoxConstraints.tight(Size.fromRadius(20)),
),
Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Text(
user.name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (_) => NewChatScreen()),
);
},
leading: Icon(StreamIcons.edit),
title: Text(
'New direct message',
style: TextStyle(
fontSize: 14.5,
),
),
),
ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (_) => NewGroupChatScreen()),
);
},
leading: Icon(StreamIcons.group),
title: Text(
'New group',
style: TextStyle(
fontSize: 14.5,
),
),
),
Expanded(
child: Container(
alignment: Alignment.bottomCenter,
child: ListTile(
onTap: () async {
await StreamChat.of(context).client.disconnect();
final secureStorage = FlutterSecureStorage();
await secureStorage.deleteAll();
Navigator.pop(context);
await Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => ChooseUserPage(),
),
);
},
leading: Icon(StreamIcons.user),
title: Text(
'Sign out',
style: TextStyle(
fontSize: 14.5,
),
),
),
),
),
],
),
),
),
),
drawer: _buildDrawer(context, user),
drawerEdgeDragWidth: 50,
body: ChannelsBloc(
child: ChannelListView(
onStartChatPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return NewChatScreen();
}));
},
swipeToAction: true,
filter: {
'members': {
@@ -194,6 +105,106 @@ class ChannelListPage extends StatelessWidget {
),
);
}
Drawer _buildDrawer(BuildContext context, User user) {
return Drawer(
child: SafeArea(
child: Padding(
padding: EdgeInsets.only(
top: MediaQuery.of(context).viewPadding.top + 8,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(
bottom: 20.0,
left: 8,
),
child: Row(
children: [
UserAvatar(
user: user,
showOnlineStatus: false,
constraints: BoxConstraints.tight(Size.fromRadius(20)),
),
Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Text(
user.name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
ListTile(
leading: Icon(StreamIcons.edit),
onTap: () {
Navigator.of(context)
..pop()
..push(MaterialPageRoute(builder: (context) {
return NewChatScreen();
}));
},
title: Text(
'New direct message',
style: TextStyle(
fontSize: 14.5,
),
),
),
ListTile(
leading: Icon(StreamIcons.group),
onTap: () {
Navigator.of(context)
..pop()
..push(MaterialPageRoute(builder: (context) {
return NewGroupChatScreen();
}));
},
title: Text(
'New group',
style: TextStyle(
fontSize: 14.5,
),
),
),
Expanded(
child: Container(
alignment: Alignment.bottomCenter,
child: ListTile(
onTap: () async {
await StreamChat.of(context).client.disconnect();
final secureStorage = FlutterSecureStorage();
await secureStorage.deleteAll();
Navigator.pop(context);
await Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => ChooseUserPage(),
),
);
},
leading: Icon(StreamIcons.user),
title: Text(
'Sign out',
style: TextStyle(
fontSize: 14.5,
),
),
),
),
),
],
),
),
),
);
}
}
class ChannelPage extends StatelessWidget {
+1 -1
View File
@@ -1,6 +1,6 @@
name: example
description: A new Flutter project.
version: 1.0.65+67
version: 1.0.66+68
environment:
sdk: ">=2.2.2 <3.0.0"
+253 -108
View File
@@ -4,6 +4,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:shimmer/shimmer.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/channels_bloc.dart';
import 'package:stream_chat_flutter/src/utils.dart';
@@ -69,6 +70,7 @@ class ChannelListView extends StatefulWidget {
this.errorBuilder,
this.emptyBuilder,
this.onImageTap,
this.onStartChatPressed,
this.swipeToAction = false,
this.pullToRefresh = true,
}) : super(key: key);
@@ -128,6 +130,9 @@ class ChannelListView extends StatefulWidget {
/// Set it to false to disable the pull-to-refresh widget
final bool pullToRefresh;
/// Callback used in the default empty list widget
final VoidCallback onStartChatPressed;
@override
_ChannelListViewState createState() => _ChannelListViewState();
}
@@ -163,127 +168,265 @@ class _ChannelListViewState extends State<ChannelListView>
return StreamBuilder<List<Channel>>(
stream: channelsBlocState.channelsStream,
builder: (context, snapshot) {
var child;
if (snapshot.hasError) {
if (snapshot.error is Error) {
print((snapshot.error as Error).stackTrace);
child = _buildErrorWidget(
snapshot,
context,
channelsBlocState,
);
} else if (!snapshot.hasData) {
child = _buildLoadingWidget();
} else {
final channels = snapshot.data;
if (channels.isEmpty && widget.emptyBuilder != null) {
child = widget.emptyBuilder(context);
}
if (widget.errorBuilder != null) {
return widget.errorBuilder(snapshot.error);
}
var message = snapshot.error.toString();
if (snapshot.error is DioError) {
final dioError = snapshot.error as DioError;
if (dioError.type == DioErrorType.RESPONSE) {
message = dioError.message;
} else {
message = 'Check your connection and retry';
}
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text.rich(
TextSpan(
if (channels.isEmpty && widget.emptyBuilder == null) {
child = LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Stack(
children: [
WidgetSpan(
child: Padding(
padding: const EdgeInsets.only(
right: 2.0,
),
child: Icon(Icons.error_outline),
ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
StreamIcons.message,
size: 136,
color: Color(0xffDBDBDB),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Lets start chatting!',
style: TextStyle(
fontSize: 16,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 52,
),
child: Text(
'How about sending your first message to a friend?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Color(0xff7A7A7A),
),
),
),
],
),
),
TextSpan(text: 'Error loading channels'),
if (widget.onStartChatPressed != null)
Positioned(
right: 0,
left: 0,
bottom: 32,
child: Center(
child: FlatButton(
onPressed: widget.onStartChatPressed,
child: Text(
'Start a chat',
style: TextStyle(
color:
StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold,
),
),
),
),
),
],
),
style: Theme.of(context).textTheme.headline6,
),
Padding(
padding: const EdgeInsets.only(
top: 16.0,
),
child: Text(message),
),
FlatButton(
onPressed: () {
channelsBlocState.queryChannels(
filter: widget.filter,
sortOptions: widget.sort,
paginationParams: widget.pagination,
options: widget.options,
);
},
child: Text('Retry'),
),
],
),
);
);
},
);
}
if (channels.isNotEmpty) {
child = ListView.custom(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
return _itemBuilder(context, i, channels);
},
childCount: (channels.length * 2) + 1,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key;
final index = channels.indexWhere(
(channel) => 'CHANNEL-${channel.id}' == valueKey.value);
return index != -1 ? (index * 2) : null;
},
),
);
}
}
if (!snapshot.hasData) {
return LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Center(
child: CircularProgressIndicator(),
),
),
);
},
);
}
final channels = snapshot.data;
if (channels.isEmpty && widget.emptyBuilder != null) {
return widget.emptyBuilder(context);
}
if (channels.isEmpty && widget.emptyBuilder == null) {
return LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Center(
child: Text('You have no channels currently'),
),
),
);
},
);
}
return ListView.custom(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
return _itemBuilder(context, i, channels);
},
childCount: (channels.length * 2) + 1,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key;
final index = channels.indexWhere(
(channel) => 'CHANNEL-${channel.id}' == valueKey.value);
return index != -1 ? (index * 2) : null;
},
),
return AnimatedSwitcher(
child: child,
duration: Duration(milliseconds: 500),
);
});
}
Widget _buildLoadingWidget() {
return ListView(
physics: AlwaysScrollableScrollPhysics(),
children: List.generate(
25,
(i) {
if (i % 2 != 0) {
if (widget.separatorBuilder != null) {
return widget.separatorBuilder(context, i);
}
return _separatorBuilder(context, i);
}
return _buildLoadingItem();
},
),
);
}
Shimmer _buildLoadingItem() {
return Shimmer.fromColors(
baseColor: Color(0xffE5E5E5),
highlightColor: Color(0xffffffff),
child: ListTile(
leading: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
constraints: BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
title: Align(
alignment: Alignment.centerLeft,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(11),
),
constraints: BoxConstraints.tightFor(
height: 16,
width: 82,
),
),
),
subtitle: Row(
children: [
Align(
alignment: Alignment.centerLeft,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(11),
),
constraints: BoxConstraints.tightFor(
height: 16,
width: 238,
),
),
),
Container(
margin: const EdgeInsets.only(left: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(11),
),
constraints: BoxConstraints.tightFor(
height: 16,
width: 42,
),
),
],
),
),
);
}
Widget _buildErrorWidget(
AsyncSnapshot<List<Channel>> snapshot,
BuildContext context,
ChannelsBlocState channelsBlocState,
) {
if (snapshot.error is Error) {
print((snapshot.error as Error).stackTrace);
}
if (widget.errorBuilder != null) {
return widget.errorBuilder(snapshot.error);
}
var message = snapshot.error.toString();
if (snapshot.error is DioError) {
final dioError = snapshot.error as DioError;
if (dioError.type == DioErrorType.RESPONSE) {
message = dioError.message;
} else {
message = 'Check your connection and retry';
}
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text.rich(
TextSpan(
children: [
WidgetSpan(
child: Padding(
padding: const EdgeInsets.only(
right: 2.0,
),
child: Icon(Icons.error_outline),
),
),
TextSpan(text: 'Error loading channels'),
],
),
style: Theme.of(context).textTheme.headline6,
),
Padding(
padding: const EdgeInsets.only(
top: 16.0,
),
child: Text(message),
),
FlatButton(
onPressed: () {
channelsBlocState.queryChannels(
filter: widget.filter,
sortOptions: widget.sort,
paginationParams: widget.pagination,
options: widget.options,
);
},
child: Text('Retry'),
),
],
),
);
}
Widget _itemBuilder(context, int i, List<Channel> channels) {
if (i % 2 != 0) {
if (widget.separatorBuilder != null) {
@@ -441,7 +584,9 @@ class _ChannelListViewState extends State<ChannelListView>
}
Widget _buildQueryProgressIndicator(
context, ChannelsBlocState channelsProvider) {
context,
ChannelsBlocState channelsProvider,
) {
return StreamBuilder<bool>(
stream: channelsProvider.queryChannelsLoading,
initialData: false,
+188 -193
View File
@@ -41,211 +41,206 @@ class GiphyAttachment extends StatelessWidget {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
DecoratedBox(
decoration: BoxDecoration(),
child: Card(
elevation: 2,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(16.0),
bottomRight: Radius.circular(0.0),
topLeft: Radius.circular(16.0),
bottomLeft: Radius.circular(16.0),
),
Card(
elevation: 2,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(16.0),
bottomRight: Radius.circular(0.0),
topLeft: Radius.circular(16.0),
bottomLeft: Radius.circular(16.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) {
return FullScreenImage(
url: attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl,
);
}));
},
child: CachedNetworkImage(
height: size?.height,
width: size?.width,
placeholder: (_, __) {
return Container(
width: size?.width,
height: size?.height,
child: Center(
child: CircularProgressIndicator(),
),
);
},
imageUrl: attachment.thumbUrl ??
attachment.imageUrl ??
attachment.assetUrl,
errorWidget: (context, url, error) => AttachmentError(
attachment: attachment,
size: size,
),
fit: BoxFit.cover,
),
),
),
Positioned(
left: 0,
top: 0,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(16.0),
)),
child: Padding(
padding: const EdgeInsets.only(
left: 8.0,
right: 8.0,
top: 8.0,
bottom: 4.0,
),
child: Row(
children: [
Icon(
StreamIcons.lightning,
color: StreamChatTheme.of(context).accentColor,
size: 16.0,
),
Text(
'GIPHY',
style: TextStyle(
color:
StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold,
fontSize: 11.0,
),
),
],
),
),
),
),
],
),
if (attachment.title != null)
Container(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Card(
elevation: 2,
child: IconButton(
padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tight(Size(32, 32)),
icon: Icon(
StreamIcons.left,
size: 24.0,
),
splashRadius: 16,
onPressed: () {
streamChannel.channel.sendAction(message, {
'image_action': 'shuffle',
});
},
),
shape: CircleBorder(),
),
Expanded(
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return FullScreenImage(
url: attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl,
);
}));
},
child: CachedNetworkImage(
height: size?.height,
width: size?.width,
placeholder: (_, __) {
return Container(
width: size?.width,
height: size?.height,
child: Center(
child: Text(
'"${attachment.title}"',
style: TextStyle(
fontStyle: FontStyle.italic,
),
),
child: CircularProgressIndicator(),
),
),
Card(
elevation: 2,
child: IconButton(
padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tight(Size(32, 32)),
icon: Icon(
StreamIcons.right,
size: 24.0,
),
splashRadius: 16,
onPressed: () {
streamChannel.channel.sendAction(message, {
'image_action': 'shuffle',
});
},
),
shape: CircleBorder(),
),
],
);
},
imageUrl: attachment.thumbUrl ??
attachment.imageUrl ??
attachment.assetUrl,
errorWidget: (context, url, error) => AttachmentError(
attachment: attachment,
size: size,
),
fit: BoxFit.cover,
),
),
),
SizedBox(
height: 4.0,
),
Container(
color: Colors.black.withOpacity(0.2),
width: double.infinity,
height: 0.5,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: FlatButton(
height: 50,
onPressed: () {
streamChannel.channel.sendAction(message, {
'image_action': 'cancel',
});
},
child: Text(
'Cancel',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black.withOpacity(0.5)),
Positioned(
left: 0,
top: 0,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(16.0),
)),
child: Padding(
padding: const EdgeInsets.only(
left: 8.0,
right: 8.0,
top: 8.0,
bottom: 4.0,
),
),
),
Container(
width: 0.5,
color: Colors.black.withOpacity(0.2),
height: 50.0,
),
Expanded(
child: FlatButton(
height: 50,
onPressed: () {
streamChannel.channel.sendAction(message, {
'image_action': 'send',
});
},
child: Text(
'Send',
style: TextStyle(
child: Row(
children: [
Icon(
StreamIcons.lightning,
color: StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold),
size: 16.0,
),
Text(
'GIPHY',
style: TextStyle(
color: StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold,
fontSize: 11.0,
),
),
],
),
),
),
],
),
],
),
if (attachment.title != null)
Container(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Card(
elevation: 2,
child: IconButton(
padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tight(Size(32, 32)),
icon: Icon(
StreamIcons.left,
size: 24.0,
),
splashRadius: 16,
onPressed: () {
streamChannel.channel.sendAction(message, {
'image_action': 'shuffle',
});
},
),
shape: CircleBorder(),
),
Expanded(
child: Center(
child: Text(
'"${attachment.title}"',
style: TextStyle(
fontStyle: FontStyle.italic,
),
),
),
),
Card(
elevation: 2,
child: IconButton(
padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tight(Size(32, 32)),
icon: Icon(
StreamIcons.right,
size: 24.0,
),
splashRadius: 16,
onPressed: () {
streamChannel.channel.sendAction(message, {
'image_action': 'shuffle',
});
},
),
shape: CircleBorder(),
),
],
),
),
),
],
),
SizedBox(
height: 4.0,
),
Container(
color: Colors.black.withOpacity(0.2),
width: double.infinity,
height: 0.5,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: FlatButton(
height: 50,
onPressed: () {
streamChannel.channel.sendAction(message, {
'image_action': 'cancel',
});
},
child: Text(
'Cancel',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black.withOpacity(0.5)),
),
),
),
Container(
width: 0.5,
color: Colors.black.withOpacity(0.2),
height: 50.0,
),
Expanded(
child: FlatButton(
height: 50,
onPressed: () {
streamChannel.channel.sendAction(message, {
'image_action': 'send',
});
},
child: Text(
'Send',
style: TextStyle(
color: StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold),
),
),
),
],
),
],
),
),
SizedBox(
+1
View File
@@ -619,6 +619,7 @@ class _MessageWidgetState extends State<MessageWidget> {
child: Material(
clipBehavior: Clip.hardEdge,
shape: attachmentShape,
type: MaterialType.transparency,
child: Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
+1
View File
@@ -19,6 +19,7 @@ dependencies:
flutter_svg: ^0.19.1
flutter_portal: ^0.3.0
cached_network_image: ^2.2.0+1
shimmer: ^1.1.2
flutter_markdown: ^0.5.0
url_launcher: ^5.4.11
emojis: ^0.9.3