diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id index 8aba7787..20c7c514 100644 --- a/example/ios/Flutter/.last_build_id +++ b/example/ios/Flutter/.last_build_id @@ -1 +1 @@ -bb5f9103d9045cd6244bcae1f5f343e5 \ No newline at end of file +c3e639ccf9b069e37a7d1345194e6b99 \ No newline at end of file diff --git a/example/lib/choose_user_page.dart b/example/lib/choose_user_page.dart index 30205088..4583a2a6 100644 --- a/example/lib/choose_user_page.dart +++ b/example/lib/choose_user_page.dart @@ -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(), diff --git a/example/lib/main.dart b/example/lib/main.dart index c09030f3..d0b39d98 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -21,7 +21,7 @@ void main() async { logLevel: Level.INFO, showLocalNotification: (!kIsWeb && Platform.isAndroid) ? showLocalNotification : null, - persistenceEnabled: false, + persistenceEnabled: true, ); if (userId != null) { @@ -43,6 +43,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 @@ -63,89 +64,9 @@ class ChannelListPage extends StatelessWidget { Widget build(BuildContext context) { final user = StreamChat.of(context).user; return Scaffold( - 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( - leading: Icon(StreamIcons.edit), - title: Text( - 'New direct message', - style: TextStyle( - fontSize: 14.5, - ), - ), - ), - ListTile( - 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, - ), - ), - ), - ), - ), - ], - ), - ), - ), - ), + drawerEnableOpenDragGesture: true, + drawerEdgeDragWidth: 50, + drawer: _buildDrawer(context, user), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: () { @@ -156,6 +77,11 @@ class ChannelListPage extends StatelessWidget { ), body: ChannelsBloc( child: ChannelListView( + onStartChatPressed: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) { + return CreateChannelPage(); + })); + }, swipeToAction: true, filter: { 'members': { @@ -174,6 +100,92 @@ 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), + title: Text( + 'New direct message', + style: TextStyle( + fontSize: 14.5, + ), + ), + ), + ListTile( + 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, + ), + ), + ), + ), + ), + ], + ), + ), + ), + ); + } } class ChannelPage extends StatelessWidget { @@ -418,6 +430,11 @@ class _CreateChannelPageState extends State { limit: 25, offset: offset, ), + filter: { + 'id': { + r'$ne': client.state.user.id, + } + }, sort: [ SortOption( 'name', diff --git a/example/pubspec.yaml b/example/pubspec.yaml index fca549d2..0fe6a0ad 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: example description: A new Flutter project. -version: 1.0.60+62 +version: 1.0.62+64 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/lib/src/channel_list_view.dart b/lib/src/channel_list_view.dart index 8b66db34..9dae4213 100644 --- a/lib/src/channel_list_view.dart +++ b/lib/src/channel_list_view.dart @@ -70,6 +70,7 @@ class ChannelListView extends StatefulWidget { this.errorBuilder, this.emptyBuilder, this.onImageTap, + this.onStartChatPressed, this.swipeToAction = false, this.pullToRefresh = true, }) : super(key: key); @@ -129,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(); } @@ -185,13 +189,69 @@ class _ChannelListViewState extends State builder: (context, viewportConstraints) { return SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Text('You have no channels currently'), - ), + child: Stack( + children: [ + 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( + 'Let’s 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), + ), + ), + ), + ], + ), + ), + 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, + ), + ), + ), + ), + ), + ], ), ); }, @@ -542,11 +602,13 @@ class _ChannelListViewState extends State ), ); } - return snapshot.data - ? _buildLoadingItem() - : Container( - height: 70, - ); + return Container( + height: 100, + padding: EdgeInsets.all(32), + child: Center( + child: snapshot.data ? CircularProgressIndicator() : Container(), + ), + ); }); } diff --git a/lib/src/giphy_attachment.dart b/lib/src/giphy_attachment.dart index 8cec9ce5..df4708c5 100644 --- a/lib/src/giphy_attachment.dart +++ b/lib/src/giphy_attachment.dart @@ -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: [ - 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: [ + 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( diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 4f551655..86479ebe 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -619,6 +619,7 @@ class _MessageWidgetState extends State { child: Material( clipBehavior: Clip.hardEdge, shape: attachmentShape, + type: MaterialType.transparency, child: Transform( transform: Matrix4.rotationY(widget.reverse ? pi : 0), alignment: Alignment.center,