add typing indicator widget in example messagelist
This commit is contained in:
+61
-61
@@ -1,61 +1,61 @@
|
||||
include: package:pedantic/analysis_options.yaml
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
- lib/**/*.g.dart
|
||||
- example/*
|
||||
|
||||
linter:
|
||||
rules:
|
||||
# these rules are documented on and in the same order as
|
||||
# the Dart Lint rules page to make maintenance easier
|
||||
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
|
||||
# - always_declare_return_types
|
||||
# - always_specify_types
|
||||
# - annotate_overrides
|
||||
# - avoid_as
|
||||
- avoid_empty_else
|
||||
- avoid_init_to_null
|
||||
- avoid_return_types_on_setters
|
||||
- avoid_web_libraries_in_flutter
|
||||
- await_only_futures
|
||||
- camel_case_types
|
||||
- cancel_subscriptions
|
||||
- close_sinks
|
||||
# - comment_references # we do not presume as to what people want to reference in their dartdocs
|
||||
# - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
|
||||
- control_flow_in_finally
|
||||
- empty_constructor_bodies
|
||||
- empty_statements
|
||||
- hash_and_equals
|
||||
- implementation_imports
|
||||
# - invariant_booleans
|
||||
# - iterable_contains_unrelated_type
|
||||
- library_names
|
||||
# - library_prefixes
|
||||
# - list_remove_unrelated_type
|
||||
# - literal_only_boolean_expressions
|
||||
- non_constant_identifier_names
|
||||
# - one_member_abstracts
|
||||
# - only_throw_errors
|
||||
# - overridden_fields
|
||||
# - package_api_docs
|
||||
- package_names
|
||||
- package_prefixed_library_names
|
||||
- prefer_is_not_empty
|
||||
# - prefer_mixin # https://github.com/dart-lang/language/issues/32
|
||||
# - public_member_api_docs
|
||||
- slash_for_doc_comments
|
||||
# - sort_constructors_first
|
||||
# - sort_unnamed_constructors_first
|
||||
# - super_goes_last # no longer needed w/ Dart 2
|
||||
- test_types_in_equals
|
||||
- throw_in_finally
|
||||
# - type_annotate_public_apis # subset of always_specify_types
|
||||
- type_init_formals
|
||||
# - unawaited_futures
|
||||
- unnecessary_brace_in_string_interps
|
||||
- unnecessary_getters_setters
|
||||
- unnecessary_statements
|
||||
- unrelated_type_equality_checks
|
||||
- valid_regexps
|
||||
#include: package:pedantic/analysis_options.yaml
|
||||
#
|
||||
#analyzer:
|
||||
# exclude:
|
||||
# - lib/**/*.g.dart
|
||||
# - example/*
|
||||
#
|
||||
#linter:
|
||||
# rules:
|
||||
# # these rules are documented on and in the same order as
|
||||
# # the Dart Lint rules page to make maintenance easier
|
||||
# # https://github.com/dart-lang/linter/blob/master/example/all.yaml
|
||||
# # - always_declare_return_types
|
||||
# # - always_specify_types
|
||||
# # - annotate_overrides
|
||||
# # - avoid_as
|
||||
# - avoid_empty_else
|
||||
# - avoid_init_to_null
|
||||
# - avoid_return_types_on_setters
|
||||
# - avoid_web_libraries_in_flutter
|
||||
# - await_only_futures
|
||||
# - camel_case_types
|
||||
# - cancel_subscriptions
|
||||
# - close_sinks
|
||||
# # - comment_references # we do not presume as to what people want to reference in their dartdocs
|
||||
# # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
|
||||
# - control_flow_in_finally
|
||||
# - empty_constructor_bodies
|
||||
# - empty_statements
|
||||
# - hash_and_equals
|
||||
# - implementation_imports
|
||||
# # - invariant_booleans
|
||||
# # - iterable_contains_unrelated_type
|
||||
# - library_names
|
||||
# # - library_prefixes
|
||||
# # - list_remove_unrelated_type
|
||||
# # - literal_only_boolean_expressions
|
||||
# - non_constant_identifier_names
|
||||
# # - one_member_abstracts
|
||||
# # - only_throw_errors
|
||||
# # - overridden_fields
|
||||
## - package_api_docs
|
||||
# - package_names
|
||||
# - package_prefixed_library_names
|
||||
# - prefer_is_not_empty
|
||||
# # - prefer_mixin # https://github.com/dart-lang/language/issues/32
|
||||
## - public_member_api_docs
|
||||
# - slash_for_doc_comments
|
||||
# # - sort_constructors_first
|
||||
# # - sort_unnamed_constructors_first
|
||||
# # - super_goes_last # no longer needed w/ Dart 2
|
||||
# - test_types_in_equals
|
||||
# - throw_in_finally
|
||||
# # - type_annotate_public_apis # subset of always_specify_types
|
||||
# - type_init_formals
|
||||
# # - unawaited_futures
|
||||
# - unnecessary_brace_in_string_interps
|
||||
# - unnecessary_getters_setters
|
||||
# - unnecessary_statements
|
||||
# - unrelated_type_equality_checks
|
||||
# - valid_regexps
|
||||
|
||||
@@ -72,18 +72,17 @@ class ChannelListPage extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _channelPreviewBuilder(BuildContext context, Channel channel) {
|
||||
final lastMessage = channel.state.messages.reversed
|
||||
.firstWhere((message) => message.type != "deleted");
|
||||
final lastMessage = channel.state.messages.reversed.firstWhere(
|
||||
(message) => message.type != "deleted",
|
||||
orElse: () => null,
|
||||
);
|
||||
|
||||
final subtitle = (lastMessage == null ? "nothing yet" : lastMessage.text);
|
||||
final opacity = channel.state.unreadCount > .0 ? 1.0 : 0.5;
|
||||
|
||||
return ListTile(
|
||||
leading: ChannelImage(
|
||||
channel: channel,
|
||||
),
|
||||
leading: ChannelImage(),
|
||||
title: ChannelName(
|
||||
channel: channel,
|
||||
textStyle:
|
||||
StreamChatTheme.of(context).channelPreviewTheme.title.copyWith(
|
||||
color: Colors.black.withOpacity(opacity),
|
||||
|
||||
+26
-9
@@ -65,12 +65,28 @@ class ChannelPage extends StatelessWidget {
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: MessageListView(
|
||||
threadBuilder: (_, parentMessage) {
|
||||
return ThreadPage(
|
||||
parent: parentMessage,
|
||||
);
|
||||
},
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
MessageListView(
|
||||
threadBuilder: (_, parentMessage) {
|
||||
return ThreadPage(
|
||||
parent: parentMessage,
|
||||
);
|
||||
},
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 4,
|
||||
),
|
||||
child: TypingIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
MessageInput(),
|
||||
@@ -101,9 +117,10 @@ class ThreadPage extends StatelessWidget {
|
||||
parentMessage: parent,
|
||||
),
|
||||
),
|
||||
MessageInput(
|
||||
parentMessage: parent,
|
||||
),
|
||||
if (parent.type != 'deleted')
|
||||
MessageInput(
|
||||
parentMessage: parent,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -4,12 +4,11 @@ description: A new Flutter project.
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.1.0 <3.0.0"
|
||||
sdk: ">=2.2.2 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
cupertino_icons: ^0.1.2
|
||||
stream_chat_flutter:
|
||||
path: ../
|
||||
|
||||
|
||||
@@ -78,7 +78,9 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
padding: const EdgeInsets.only(right: 10.0),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Center(child: ChannelImage(channel: channel)),
|
||||
Center(
|
||||
child: ChannelImage(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -88,7 +90,6 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
ChannelName(
|
||||
channel: channel,
|
||||
textStyle: StreamChatTheme.of(context)
|
||||
.channelTheme
|
||||
.channelHeaderTheme
|
||||
|
||||
@@ -45,7 +45,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
class ChannelImage extends StatelessWidget {
|
||||
const ChannelImage({
|
||||
Key key,
|
||||
@required this.channel,
|
||||
this.channel,
|
||||
this.size = 40,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -57,6 +57,7 @@ class ChannelImage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final channel = this.channel ?? StreamChannel.of(context).channel;
|
||||
return StreamBuilder<Map<String, dynamic>>(
|
||||
stream: channel.extraDataStream,
|
||||
initialData: channel.extraData,
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
|
||||
import 'stream_channel.dart';
|
||||
|
||||
/// It shows the current [Channel] name using a [Text] widget.
|
||||
///
|
||||
/// The widget uses a [StreamBuilder] to render the channel information image as soon as it updates.
|
||||
class ChannelName extends StatelessWidget {
|
||||
const ChannelName({
|
||||
Key key,
|
||||
@required this.channel,
|
||||
this.channel,
|
||||
this.textStyle,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -19,6 +21,7 @@ class ChannelName extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final channel = this.channel ?? StreamChannel.of(context).channel;
|
||||
return StreamBuilder<Map<String, dynamic>>(
|
||||
stream: channel.extraDataStream,
|
||||
initialData: channel.extraData,
|
||||
|
||||
@@ -37,14 +37,11 @@ class ChannelPreview extends StatelessWidget {
|
||||
onTap: () {
|
||||
onTap(channel);
|
||||
},
|
||||
leading: ChannelImage(
|
||||
channel: channel,
|
||||
),
|
||||
leading: ChannelImage(),
|
||||
title: ChannelName(
|
||||
textStyle: StreamChatTheme.of(context).channelPreviewTheme.title,
|
||||
channel: channel,
|
||||
),
|
||||
subtitle: _buildSubtitle(),
|
||||
subtitle: _buildSubtitle(context),
|
||||
trailing: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
@@ -81,29 +78,23 @@ class ChannelPreview extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubtitle() {
|
||||
return StreamBuilder<List<User>>(
|
||||
stream: channel.state.typingEventsStream,
|
||||
initialData: channel.state.typingEvents,
|
||||
builder: (context, snapshot) {
|
||||
final typings = snapshot.data;
|
||||
final opacity = channel.state.unreadCount > 0 ? 1.0 : 0.5;
|
||||
return typings.isNotEmpty
|
||||
? TypingIndicator(
|
||||
typings: typings,
|
||||
style: StreamChatTheme.of(context)
|
||||
Widget _buildSubtitle(BuildContext context) {
|
||||
final opacity = channel.state.unreadCount > 0 ? 1.0 : 0.5;
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TypingIndicator(
|
||||
channel: channel,
|
||||
alternativeWidget: _buildLastMessage(context, opacity),
|
||||
style:
|
||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.subtitle
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.subtitle
|
||||
.color
|
||||
.withOpacity(opacity),
|
||||
),
|
||||
)
|
||||
: _buildLastMessage(context, opacity);
|
||||
});
|
||||
.color
|
||||
.withOpacity(opacity),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLastMessage(BuildContext context, double opacity) {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_channel.dart';
|
||||
|
||||
/// Widget to show the current list of typing users
|
||||
class TypingIndicator extends StatelessWidget {
|
||||
const TypingIndicator({
|
||||
Key key,
|
||||
@required this.typings,
|
||||
this.channel,
|
||||
this.alternativeWidget = const SizedBox(),
|
||||
this.style,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -13,14 +15,32 @@ class TypingIndicator extends StatelessWidget {
|
||||
final TextStyle style;
|
||||
|
||||
/// List of typing users
|
||||
final List<User> typings;
|
||||
final Channel channel;
|
||||
|
||||
/// Widget built when no typings is happening
|
||||
final Widget alternativeWidget;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
'${typings.map((u) => u.name).join(',')} ${typings.length == 1 ? 'is' : 'are'} typing...',
|
||||
maxLines: 1,
|
||||
style: style,
|
||||
final channelState =
|
||||
channel?.state ?? StreamChannel.of(context).channel.state;
|
||||
return StreamBuilder<List<User>>(
|
||||
initialData: channelState.typingEvents,
|
||||
stream: channelState.typingEventsStream,
|
||||
builder: (context, snapshot) {
|
||||
return AnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 300),
|
||||
child: snapshot.data.isNotEmpty
|
||||
? Text(
|
||||
'${snapshot.data.map((u) => u.name).join(',')} ${snapshot.data.length == 1 ? 'is' : 'are'} typing...',
|
||||
maxLines: 1,
|
||||
style: style,
|
||||
)
|
||||
: Container(
|
||||
child: alternativeWidget,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -4,18 +4,18 @@ description: Stream Chat official Flutter SDK. Build your own chat experience us
|
||||
version: 0.1.8
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.2 <3.0.0"
|
||||
sdk: ">=2.3.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
rxdart: ^0.23.1
|
||||
flutter_widgets: ^0.1.10
|
||||
flutter_widgets: ^0.1.11
|
||||
jiffy: ^3.0.0
|
||||
cached_network_image: ^2.0.0
|
||||
flutter_markdown: ^0.3.3
|
||||
url_launcher: ^5.4.1
|
||||
video_player: ^0.10.7
|
||||
flutter_markdown: ^0.3.4
|
||||
url_launcher: ^5.4.2
|
||||
video_player: ^0.10.8+1
|
||||
chewie: ^0.9.8+1
|
||||
file_picker: ^1.4.3+2
|
||||
image_picker: ^0.6.3+4
|
||||
@@ -23,4 +23,4 @@ dependencies:
|
||||
stream_chat: ^0.1.17
|
||||
|
||||
dev_dependencies:
|
||||
pedantic: ^1.8.0+1
|
||||
pedantic: ^1.9.0
|
||||
|
||||
Reference in New Issue
Block a user