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' channel: 'stable'
- name: Get dependencies - name: Get dependencies
run: flutter pub get run: flutter pub get
- name: Run formatter
run: flutter format -n --set-exit-if-changed .
- name: Coverage fix - name: Coverage fix
run: | run: |
file=test/coverage_helper_test.dart file=test/coverage_helper_test.dart
+3
View File
@@ -94,6 +94,8 @@ class ChooseUserPage extends StatelessWidget {
), ),
), ),
Expanded( Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 32),
child: ListView.separated( child: ListView.separated(
separatorBuilder: (context, i) { separatorBuilder: (context, i) {
return Container( return Container(
@@ -217,6 +219,7 @@ class ChooseUserPage extends StatelessWidget {
}, },
), ),
), ),
),
StreamVersion(), StreamVersion(),
], ],
), ),
+49 -38
View File
@@ -1,14 +1,14 @@
import 'dart:io'; import 'dart:io';
import 'package:example/choose_user_page.dart'; 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/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.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'; import 'notifications_service.dart';
void main() async { void main() async {
@@ -48,6 +48,7 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(), theme: ThemeData.light(),
darkTheme: ThemeData.dark(), darkTheme: ThemeData.dark(),
//TODO change to system once dark theme is implemented //TODO change to system once dark theme is implemented
@@ -75,7 +76,38 @@ class ChannelListPage extends StatelessWidget {
style: TextStyle(color: Colors.black), style: TextStyle(color: Colors.black),
), ),
), ),
drawer: Drawer( drawer: _buildDrawer(context, user),
drawerEdgeDragWidth: 50,
body: ChannelsBloc(
child: ChannelListView(
onStartChatPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return NewChatScreen();
}));
},
swipeToAction: true,
filter: {
'members': {
'\$in': [user.id],
},
'draft': {
r'$ne': true,
},
},
options: {
'presence': true,
},
pagination: PaginationParams(
limit: 20,
),
channelWidget: ChannelPage(),
),
),
);
}
Drawer _buildDrawer(BuildContext context, User user) {
return Drawer(
child: SafeArea( child: SafeArea(
child: Padding( child: Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
@@ -109,14 +141,14 @@ class ChannelListPage extends StatelessWidget {
), ),
), ),
ListTile( ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (_) => NewChatScreen()),
);
},
leading: Icon(StreamIcons.edit), leading: Icon(StreamIcons.edit),
onTap: () {
Navigator.of(context)
..pop()
..push(MaterialPageRoute(builder: (context) {
return NewChatScreen();
}));
},
title: Text( title: Text(
'New direct message', 'New direct message',
style: TextStyle( style: TextStyle(
@@ -125,14 +157,14 @@ class ChannelListPage extends StatelessWidget {
), ),
), ),
ListTile( ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (_) => NewGroupChatScreen()),
);
},
leading: Icon(StreamIcons.group), leading: Icon(StreamIcons.group),
onTap: () {
Navigator.of(context)
..pop()
..push(MaterialPageRoute(builder: (context) {
return NewGroupChatScreen();
}));
},
title: Text( title: Text(
'New group', 'New group',
style: TextStyle( style: TextStyle(
@@ -171,27 +203,6 @@ class ChannelListPage extends StatelessWidget {
), ),
), ),
), ),
),
body: ChannelsBloc(
child: ChannelListView(
swipeToAction: true,
filter: {
'members': {
'\$in': [user.id],
},
'draft': {
r'$ne': true,
},
},
options: {
'presence': true,
},
pagination: PaginationParams(
limit: 20,
),
channelWidget: ChannelPage(),
),
),
); );
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
name: example name: example
description: A new Flutter project. description: A new Flutter project.
version: 1.0.65+67 version: 1.0.66+68
environment: environment:
sdk: ">=2.2.2 <3.0.0" sdk: ">=2.2.2 <3.0.0"
+207 -62
View File
@@ -4,6 +4,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:shimmer/shimmer.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/channels_bloc.dart'; import 'package:stream_chat_flutter/src/channels_bloc.dart';
import 'package:stream_chat_flutter/src/utils.dart'; import 'package:stream_chat_flutter/src/utils.dart';
@@ -69,6 +70,7 @@ class ChannelListView extends StatefulWidget {
this.errorBuilder, this.errorBuilder,
this.emptyBuilder, this.emptyBuilder,
this.onImageTap, this.onImageTap,
this.onStartChatPressed,
this.swipeToAction = false, this.swipeToAction = false,
this.pullToRefresh = true, this.pullToRefresh = true,
}) : super(key: key); }) : super(key: key);
@@ -128,6 +130,9 @@ class ChannelListView extends StatefulWidget {
/// Set it to false to disable the pull-to-refresh widget /// Set it to false to disable the pull-to-refresh widget
final bool pullToRefresh; final bool pullToRefresh;
/// Callback used in the default empty list widget
final VoidCallback onStartChatPressed;
@override @override
_ChannelListViewState createState() => _ChannelListViewState(); _ChannelListViewState createState() => _ChannelListViewState();
} }
@@ -163,7 +168,206 @@ class _ChannelListViewState extends State<ChannelListView>
return StreamBuilder<List<Channel>>( return StreamBuilder<List<Channel>>(
stream: channelsBlocState.channelsStream, stream: channelsBlocState.channelsStream,
builder: (context, snapshot) { builder: (context, snapshot) {
var child;
if (snapshot.hasError) { if (snapshot.hasError) {
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 (channels.isEmpty && widget.emptyBuilder == null) {
child = LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
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(
'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),
),
),
),
],
),
),
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,
),
),
),
),
),
],
),
);
},
);
}
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;
},
),
);
}
}
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) { if (snapshot.error is Error) {
print((snapshot.error as Error).stackTrace); print((snapshot.error as Error).stackTrace);
} }
@@ -223,67 +427,6 @@ class _ChannelListViewState extends State<ChannelListView>
); );
} }
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;
},
),
);
});
}
Widget _itemBuilder(context, int i, List<Channel> channels) { Widget _itemBuilder(context, int i, List<Channel> channels) {
if (i % 2 != 0) { if (i % 2 != 0) {
if (widget.separatorBuilder != null) { if (widget.separatorBuilder != null) {
@@ -441,7 +584,9 @@ class _ChannelListViewState extends State<ChannelListView>
} }
Widget _buildQueryProgressIndicator( Widget _buildQueryProgressIndicator(
context, ChannelsBlocState channelsProvider) { context,
ChannelsBlocState channelsProvider,
) {
return StreamBuilder<bool>( return StreamBuilder<bool>(
stream: channelsProvider.queryChannelsLoading, stream: channelsProvider.queryChannelsLoading,
initialData: false, initialData: false,
+3 -8
View File
@@ -41,9 +41,7 @@ class GiphyAttachment extends StatelessWidget {
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
DecoratedBox( Card(
decoration: BoxDecoration(),
child: Card(
elevation: 2, elevation: 2,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@@ -64,8 +62,7 @@ class GiphyAttachment extends StatelessWidget {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
Navigator.push(context, Navigator.push(context, MaterialPageRoute(builder: (_) {
MaterialPageRoute(builder: (_) {
return FullScreenImage( return FullScreenImage(
url: attachment.imageUrl ?? url: attachment.imageUrl ??
attachment.assetUrl ?? attachment.assetUrl ??
@@ -122,8 +119,7 @@ class GiphyAttachment extends StatelessWidget {
Text( Text(
'GIPHY', 'GIPHY',
style: TextStyle( style: TextStyle(
color: color: StreamChatTheme.of(context).accentColor,
StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 11.0, fontSize: 11.0,
), ),
@@ -247,7 +243,6 @@ class GiphyAttachment extends StatelessWidget {
], ],
), ),
), ),
),
SizedBox( SizedBox(
height: 4.0, height: 4.0,
), ),
+1
View File
@@ -619,6 +619,7 @@ class _MessageWidgetState extends State<MessageWidget> {
child: Material( child: Material(
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
shape: attachmentShape, shape: attachmentShape,
type: MaterialType.transparency,
child: Transform( child: Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0), transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center, alignment: Alignment.center,
+1
View File
@@ -19,6 +19,7 @@ dependencies:
flutter_svg: ^0.19.1 flutter_svg: ^0.19.1
flutter_portal: ^0.3.0 flutter_portal: ^0.3.0
cached_network_image: ^2.2.0+1 cached_network_image: ^2.2.0+1
shimmer: ^1.1.2
flutter_markdown: ^0.5.0 flutter_markdown: ^0.5.0
url_launcher: ^5.4.11 url_launcher: ^5.4.11
emojis: ^0.9.3 emojis: ^0.9.3