Merge branch 'feature/new-ui' of github.com:GetStream/stream-chat-flutter into feat/messg-reply
Conflicts: example/ios/Flutter/.last_build_id example/lib/main.dart lib/src/file_attachment.dart lib/src/message_actions_modal.dart lib/src/message_input.dart lib/src/message_widget.dart
This commit is contained in:
+61
-61
@@ -1,63 +1,63 @@
|
||||
include: package:pedantic/analysis_options.yaml
|
||||
|
||||
analyzer:
|
||||
enable-experiment:
|
||||
- extension-methods
|
||||
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
|
||||
#include: package:pedantic/analysis_options.yaml
|
||||
#
|
||||
#analyzer:
|
||||
# enable-experiment:
|
||||
# - extension-methods
|
||||
# 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
|
||||
|
||||
@@ -1 +1 @@
|
||||
c3e639ccf9b069e37a7d1345194e6b99
|
||||
c770358a10272b4ee1cdc2eb432445f6
|
||||
@@ -3,12 +3,12 @@ import 'dart:io';
|
||||
import 'package:example/stream_version.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 'choose_user_page.dart';
|
||||
import 'main.dart';
|
||||
import 'notifications_service.dart';
|
||||
import 'choose_user_page.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
class AdvancedOptionsPage extends StatefulWidget {
|
||||
@override
|
||||
@@ -34,20 +34,23 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
resizeToAvoidBottomPadding: false,
|
||||
appBar: AppBar(
|
||||
backgroundColor: StreamChatTheme.of(context).primaryColor,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
elevation: 1,
|
||||
centerTitle: true,
|
||||
brightness: Theme.of(context).brightness,
|
||||
title: Text(
|
||||
'Advanced Options',
|
||||
style: Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.headlineBold
|
||||
.copyWith(color: StreamChatTheme.of(context).colorTheme.black),
|
||||
),
|
||||
leading: IconButton(
|
||||
icon: StreamSvgIcon.left(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
@@ -87,7 +90,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
errorStyle: TextStyle(fontSize: 0),
|
||||
@@ -95,14 +98,18 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _apiKeyError != null
|
||||
? Color(0xffff3742)
|
||||
: Colors.black.withOpacity(.5),
|
||||
? StreamChatTheme.of(context).colorTheme.accentRed
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
border: UnderlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
fillColor: Color(0xffF5F5F5),
|
||||
fillColor:
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
filled: true,
|
||||
labelText:
|
||||
'Chat API Key ${_apiKeyError != null ? ':$_apiKeyError' : ''}',
|
||||
@@ -131,7 +138,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
decoration: InputDecoration(
|
||||
@@ -140,14 +147,18 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
color: _userIdError != null
|
||||
? Color(0xffff3742)
|
||||
: Colors.black.withOpacity(.5),
|
||||
? StreamChatTheme.of(context).colorTheme.accentRed
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
border: UnderlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
fillColor: Color(0xffF5F5F5),
|
||||
fillColor:
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
filled: true,
|
||||
labelText:
|
||||
'User ID ${_userIdError != null ? ':$_userIdError' : ''}',
|
||||
@@ -176,7 +187,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
decoration: InputDecoration(
|
||||
@@ -185,14 +196,18 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
color: _userTokenError != null
|
||||
? Color(0xffff3742)
|
||||
: Colors.black.withOpacity(.5),
|
||||
? StreamChatTheme.of(context).colorTheme.accentRed
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
border: UnderlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
fillColor: Color(0xffF5F5F5),
|
||||
fillColor:
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
filled: true,
|
||||
labelText:
|
||||
'User Token ${_userTokenError != null ? ':$_userTokenError' : ''}',
|
||||
@@ -208,13 +223,17 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black.withOpacity(.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
border: UnderlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
fillColor: Color(0xffF5F5F5),
|
||||
fillColor:
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
filled: true,
|
||||
labelText: 'Username (optional)',
|
||||
),
|
||||
@@ -224,13 +243,14 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: FlatButton(
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
minWidth: double.infinity,
|
||||
height: 48,
|
||||
child: Text(
|
||||
'Login',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
@@ -255,7 +275,9 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
),
|
||||
height: 100,
|
||||
width: 100,
|
||||
|
||||
@@ -66,7 +66,7 @@ class ChipInputTextFieldState<T> extends State<ChipsInputTextField<T>> {
|
||||
onTap: _pauseItemAddition ? resumeItemAddition : null,
|
||||
child: Material(
|
||||
elevation: 1,
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: Container(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 16),
|
||||
@@ -78,10 +78,14 @@ class ChipInputTextFieldState<T> extends State<ChipsInputTextField<T>> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Text(
|
||||
'TO:',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
@@ -111,10 +115,14 @@ class ChipInputTextFieldState<T> extends State<ChipsInputTextField<T>> {
|
||||
disabledBorder: InputBorder.none,
|
||||
contentPadding: const EdgeInsets.only(top: 4.0),
|
||||
hintText: widget.hint,
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
fontSize: 14,
|
||||
),
|
||||
hintStyle: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.body
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -126,11 +134,17 @@ class ChipInputTextFieldState<T> extends State<ChipsInputTextField<T>> {
|
||||
child: IconButton(
|
||||
icon: _chips.isEmpty
|
||||
? StreamSvgIcon.user(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
size: 24,
|
||||
)
|
||||
: StreamSvgIcon.userAdd(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
size: 24,
|
||||
),
|
||||
onPressed: resumeItemAddition,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'package:example/advanced_options_page.dart';
|
||||
import 'package:example/main.dart';
|
||||
import 'package:example/stream_version.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -60,6 +58,7 @@ class ChooseUserPage extends StatelessWidget {
|
||||
(_, value) => value..extraData['image'] = getRandomPicUrl(value));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@@ -80,19 +79,12 @@ class ChooseUserPage extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(bottom: 13.0),
|
||||
child: Text(
|
||||
'Welcome to Stream Chat',
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.title,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Select a user to try the Flutter SDK:',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
color: Colors.black,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.body,
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
@@ -120,7 +112,9 @@ class ChooseUserPage extends StatelessWidget {
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
),
|
||||
height: 100,
|
||||
width: 100,
|
||||
@@ -169,9 +163,20 @@ class ChooseUserPage extends StatelessWidget {
|
||||
),
|
||||
title: Text(
|
||||
user.name,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
style:
|
||||
StreamChatTheme.of(context).textTheme.bodyBold,
|
||||
),
|
||||
subtitle: Text(
|
||||
'Stream test account',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.grey,
|
||||
),
|
||||
),
|
||||
subtitle: Text('Stream test account'),
|
||||
trailing: SvgPicture.asset(
|
||||
'assets/icon_arrow_right.svg',
|
||||
height: 24,
|
||||
@@ -185,16 +190,26 @@ class ChooseUserPage extends StatelessWidget {
|
||||
},
|
||||
leading: CircleAvatar(
|
||||
child: StreamSvgIcon.settings(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).secondaryColor,
|
||||
backgroundColor: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.greyWhisper,
|
||||
),
|
||||
title: Text(
|
||||
'Advanced Options',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||
),
|
||||
subtitle: Text(
|
||||
'Custom settings',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
),
|
||||
subtitle: Text('Custom settings'),
|
||||
trailing: SvgPicture.asset(
|
||||
'assets/icon_arrow_right.svg',
|
||||
height: 24,
|
||||
|
||||
@@ -50,9 +50,10 @@ class MyApp extends StatelessWidget {
|
||||
client: client,
|
||||
streamChatThemeData: StreamChatThemeData.fromTheme(theme).copyWith(
|
||||
ownMessageTheme: MessageTheme(
|
||||
messageBackgroundColor: Colors.black,
|
||||
messageBackgroundColor:
|
||||
StreamChatTheme.of(context).colorTheme.black,
|
||||
messageText: TextStyle(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
|
||||
@@ -84,10 +84,12 @@ class ChannelListPage extends StatelessWidget {
|
||||
channel: channel,
|
||||
),
|
||||
title: ChannelName(
|
||||
channel: channel,
|
||||
textStyle:
|
||||
StreamChatTheme.of(context).channelPreviewTheme.title.copyWith(
|
||||
color: Colors.black.withOpacity(opacity),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(opacity),
|
||||
),
|
||||
),
|
||||
subtitle: Text(subtitle),
|
||||
|
||||
@@ -127,7 +127,6 @@ class ChannelPage extends StatelessWidget {
|
||||
showTimestamp: !details.isNextUser,
|
||||
showUsername: !details.isNextUser,
|
||||
showReactions: false,
|
||||
showReplyIndicator: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,15 +59,16 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
return false;
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
leading: const StreamBackButton(),
|
||||
title: Text(
|
||||
'Name of Group Chat',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
@@ -82,7 +83,10 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
'NAME',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
@@ -99,7 +103,11 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
hintText: 'Choose a group chat name',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14, color: Colors.black.withOpacity(.5)),
|
||||
fontSize: 14,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -113,7 +121,9 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
padding: const EdgeInsets.all(0),
|
||||
icon: StreamSvgIcon.check(
|
||||
size: 24,
|
||||
color: _isGroupNameEmpty ? Colors.grey : Color(0xFF006CFF),
|
||||
color: _isGroupNameEmpty
|
||||
? Colors.grey
|
||||
: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
onPressed: _isGroupNameEmpty
|
||||
? null
|
||||
@@ -144,7 +154,16 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
children: [
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
color: Colors.grey.shade50,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
@@ -167,16 +186,28 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withOpacity(0.1)
|
||||
: Colors.black.withOpacity(0.1),
|
||||
? StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white
|
||||
.withOpacity(0.1)
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.1),
|
||||
),
|
||||
itemBuilder: (_, index) {
|
||||
if (index == _selectedUsers.length) {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withOpacity(0.1)
|
||||
: Colors.black.withOpacity(0.1),
|
||||
? StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white
|
||||
.withOpacity(0.1)
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.1),
|
||||
);
|
||||
}
|
||||
final user = _selectedUsers[index];
|
||||
@@ -200,7 +231,7 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
trailing: IconButton(
|
||||
icon: Icon(
|
||||
Icons.clear_rounded,
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
splashRadius: 24,
|
||||
|
||||
+136
-112
@@ -1,8 +1,7 @@
|
||||
import 'dart:async';
|
||||
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';
|
||||
@@ -13,7 +12,6 @@ import 'notifications_service.dart';
|
||||
import 'routes/app_routes.dart';
|
||||
import 'routes/routes.dart';
|
||||
import 'search_text_field.dart';
|
||||
import 'dart:async';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -28,7 +26,7 @@ void main() async {
|
||||
logLevel: Level.INFO,
|
||||
showLocalNotification:
|
||||
(!kIsWeb && Platform.isAndroid) ? showLocalNotification : null,
|
||||
persistenceEnabled: true,
|
||||
persistenceEnabled: false,
|
||||
);
|
||||
|
||||
if (userId != null) {
|
||||
@@ -52,18 +50,20 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
child: MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData.light(),
|
||||
darkTheme: ThemeData.dark(),
|
||||
//TODO change to system once dark theme is implemented
|
||||
themeMode: ThemeMode.light,
|
||||
onGenerateRoute: AppRoutes.generateRoute,
|
||||
initialRoute:
|
||||
client.state.user == null ? Routes.CHOOSE_USER : Routes.HOME,
|
||||
),
|
||||
return MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: client,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData.light(),
|
||||
darkTheme: ThemeData.dark(),
|
||||
themeMode: ThemeMode.dark,
|
||||
onGenerateRoute: AppRoutes.generateRoute,
|
||||
initialRoute:
|
||||
client.state.user == null ? Routes.CHOOSE_USER : Routes.HOME,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,9 @@ class _HomePageState extends State<HomePage> {
|
||||
overflow: Overflow.visible,
|
||||
children: [
|
||||
StreamSvgIcon.message(
|
||||
color: _isSelected(0) ? Colors.black : Colors.grey,
|
||||
color: _isSelected(0)
|
||||
? StreamChatTheme.of(context).colorTheme.black
|
||||
: Colors.grey,
|
||||
),
|
||||
Positioned(
|
||||
top: -3,
|
||||
@@ -101,7 +103,9 @@ class _HomePageState extends State<HomePage> {
|
||||
overflow: Overflow.visible,
|
||||
children: [
|
||||
StreamSvgIcon.mentions(
|
||||
color: _isSelected(1) ? Colors.black : Colors.grey,
|
||||
color: _isSelected(1)
|
||||
? StreamChatTheme.of(context).colorTheme.black
|
||||
: Colors.grey,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -114,6 +118,7 @@ class _HomePageState extends State<HomePage> {
|
||||
Widget build(BuildContext context) {
|
||||
final user = StreamChat.of(context).user;
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: ChannelListHeader(
|
||||
onNewChatButtonTap: () {
|
||||
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
||||
@@ -122,10 +127,14 @@ class _HomePageState extends State<HomePage> {
|
||||
drawer: _buildDrawer(context, user),
|
||||
drawerEdgeDragWidth: 50,
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
currentIndex: _currentIndex,
|
||||
items: _navBarItems,
|
||||
selectedLabelStyle: StreamChatTheme.of(context).textTheme.footnoteBold,
|
||||
unselectedLabelStyle:
|
||||
StreamChatTheme.of(context).textTheme.footnoteBold,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
selectedItemColor: Colors.black,
|
||||
selectedItemColor: StreamChatTheme.of(context).colorTheme.black,
|
||||
unselectedItemColor: Colors.grey,
|
||||
onTap: (index) {
|
||||
setState(() => _currentIndex = index);
|
||||
@@ -143,104 +152,116 @@ class _HomePageState extends State<HomePage> {
|
||||
|
||||
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: Container(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
UserAvatar(
|
||||
user: user,
|
||||
showOnlineStatus: false,
|
||||
constraints: BoxConstraints.tight(Size.fromRadius(20)),
|
||||
ListTile(
|
||||
leading: StreamSvgIcon.penWrite(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.popAndPushNamed(
|
||||
context,
|
||||
Routes.NEW_CHAT,
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
'New direct message',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Text(
|
||||
user.name,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: StreamSvgIcon.contacts(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.popAndPushNamed(
|
||||
context,
|
||||
Routes.NEW_GROUP_CHAT,
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
'New group',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: ListTile(
|
||||
onTap: () async {
|
||||
Navigator.pop(context);
|
||||
|
||||
final secureStorage = FlutterSecureStorage();
|
||||
await secureStorage.deleteAll();
|
||||
|
||||
StreamChat.of(context).client.disconnect(
|
||||
clearUser: true,
|
||||
);
|
||||
|
||||
await Navigator.pushReplacementNamed(
|
||||
context,
|
||||
Routes.CHOOSE_USER,
|
||||
);
|
||||
},
|
||||
leading: StreamSvgIcon.user(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
title: Text(
|
||||
'Sign out',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: StreamSvgIcon.penWrite(
|
||||
color: Colors.black.withOpacity(.5),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.popAndPushNamed(
|
||||
context,
|
||||
Routes.NEW_CHAT,
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
'New direct message',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: StreamSvgIcon.contacts(
|
||||
color: Colors.black.withOpacity(.5),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.popAndPushNamed(
|
||||
context,
|
||||
Routes.NEW_GROUP_CHAT,
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
'New group',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: ListTile(
|
||||
onTap: () async {
|
||||
Navigator.pop(context);
|
||||
|
||||
final secureStorage = FlutterSecureStorage();
|
||||
await secureStorage.deleteAll();
|
||||
|
||||
StreamChat.of(context).client.disconnect(
|
||||
clearUser: true,
|
||||
);
|
||||
|
||||
await Navigator.pushReplacementNamed(
|
||||
context,
|
||||
Routes.CHOOSE_USER,
|
||||
);
|
||||
},
|
||||
leading: StreamSvgIcon.user(
|
||||
color: Colors.black.withOpacity(.5),
|
||||
),
|
||||
title: Text(
|
||||
'Sign out',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -436,7 +457,7 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: ChannelHeader(
|
||||
showTypingIndicator: false,
|
||||
),
|
||||
@@ -463,7 +484,10 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
right: 0,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
color: Color(0xffFCFCFC).withOpacity(.9),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.whiteSnow
|
||||
.withOpacity(.9),
|
||||
child: TypingIndicator(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -501,7 +525,7 @@ class ThreadPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: ThreadHeader(
|
||||
parent: parent,
|
||||
),
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'chips_input_text_field.dart';
|
||||
import 'main.dart';
|
||||
import 'new_group_chat_screen.dart';
|
||||
import 'routes/routes.dart';
|
||||
|
||||
class NewChatScreen extends StatefulWidget {
|
||||
@@ -37,8 +36,6 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
|
||||
bool _showUserList = true;
|
||||
|
||||
bool _channelExisted = false;
|
||||
|
||||
void _userNameListener() {
|
||||
if (_debounce?.isActive ?? false) _debounce.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 350), () {
|
||||
@@ -122,17 +119,18 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
leading: const StreamBackButton(),
|
||||
title: Text(
|
||||
'New Chat',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.headlineBold
|
||||
.copyWith(color: StreamChatTheme.of(context).colorTheme.black),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
@@ -157,7 +155,10 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
@@ -165,7 +166,10 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
padding: const EdgeInsets.fromLTRB(8, 4, 12, 4),
|
||||
child: Text(
|
||||
user.name,
|
||||
style: TextStyle(color: Colors.black),
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -182,7 +186,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
),
|
||||
Positioned(
|
||||
child: StreamSvgIcon.close(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -212,7 +216,9 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
StreamNeumorphicButton(
|
||||
child: Center(
|
||||
child: StreamSvgIcon.contacts(
|
||||
color: Color(0xFF006CFF),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
@@ -220,10 +226,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'Create a Group',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -238,8 +241,14 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
Colors.black.withOpacity(0.02),
|
||||
Colors.white.withOpacity(0.05),
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.02),
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white
|
||||
.withOpacity(0.05),
|
||||
],
|
||||
stops: [0, 1],
|
||||
),
|
||||
@@ -250,13 +259,17 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Text(
|
||||
_isSearchActive
|
||||
? "Matches for \"$_userNameQuery\""
|
||||
: 'On the platform',
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
_isSearchActive
|
||||
? "Matches for \"$_userNameQuery\""
|
||||
: 'On the platform',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5))),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@@ -316,7 +329,17 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'No user matches these keywords...'),
|
||||
'No user matches these keywords...',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(
|
||||
context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -340,7 +363,10 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
'No chats here yet...',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black.withOpacity(.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -3,9 +3,8 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'group_chat_details_screen.dart';
|
||||
import 'search_text_field.dart';
|
||||
import 'routes/routes.dart';
|
||||
import 'search_text_field.dart';
|
||||
|
||||
class NewGroupChatScreen extends StatefulWidget {
|
||||
@override
|
||||
@@ -51,23 +50,24 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
|
||||
body: NestedScrollView(
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
body: SafeArea(
|
||||
child: NestedScrollView(
|
||||
floatHeaderSlivers: true,
|
||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||
return <Widget>[
|
||||
SliverAppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
pinned: true,
|
||||
forceElevated: true,
|
||||
elevation: 1,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
leading: const StreamBackButton(),
|
||||
title: Text(
|
||||
'Add Group Members',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
@@ -76,7 +76,8 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
if (_selectedUsers.isNotEmpty)
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.arrow_right(
|
||||
color: Color(0xFF006CFF),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
onPressed: () async {
|
||||
final updatedList = await Navigator.pushNamed(
|
||||
@@ -137,18 +138,21 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.grey.shade100,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.whiteSnow,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(0.0),
|
||||
child: StreamSvgIcon.close(
|
||||
color: Colors.black,
|
||||
size: 24,
|
||||
),
|
||||
child: StreamSvgIcon.close(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -180,8 +184,14 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
Colors.black.withOpacity(0.02),
|
||||
Colors.white.withOpacity(0.05),
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.02),
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white
|
||||
.withOpacity(0.05),
|
||||
],
|
||||
stops: [0, 1],
|
||||
),
|
||||
@@ -196,7 +206,10 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
? 'Matches for \"$_userNameQuery\"'
|
||||
: 'On the platform',
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -261,7 +274,17 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Text('No user matches these keywords...'),
|
||||
Text(
|
||||
'No user matches these keywords...',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -292,7 +315,7 @@ class _HeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
Widget build(
|
||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ class SearchTextField extends StatelessWidget {
|
||||
return Container(
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
border: Border.all(
|
||||
color: Colors.grey.shade300,
|
||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
@@ -40,6 +40,7 @@ class SearchTextField extends StatelessWidget {
|
||||
controller: controller,
|
||||
onChanged: onChanged,
|
||||
decoration: InputDecoration(
|
||||
prefixText: ' ',
|
||||
prefixIconConstraints: BoxConstraints.tight(Size(40, 24)),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
@@ -47,15 +48,16 @@ class SearchTextField extends StatelessWidget {
|
||||
right: 8,
|
||||
),
|
||||
child: StreamSvgIcon.search(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
hintText: hintText,
|
||||
hintStyle: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
fontSize: 14,
|
||||
),
|
||||
hintStyle: StreamChatTheme.of(context).textTheme.body.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
class StreamVersion extends StatelessWidget {
|
||||
const StreamVersion({
|
||||
@@ -28,7 +29,8 @@ class StreamVersion extends StatelessWidget {
|
||||
'Stream SDK v ${streamChatDep}',
|
||||
style: TextStyle(
|
||||
fontSize: 14.5,
|
||||
color: Colors.black.withOpacity(.13),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.13),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: example
|
||||
description: A new Flutter project.
|
||||
version: 1.0.94+97
|
||||
version: 1.0.102+105
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.2 <3.0.0"
|
||||
|
||||
@@ -3,6 +3,8 @@ import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
|
||||
import '../stream_chat_flutter.dart';
|
||||
|
||||
class AttachmentError extends StatelessWidget {
|
||||
final Attachment attachment;
|
||||
final Size size;
|
||||
@@ -23,12 +25,12 @@ class AttachmentError extends StatelessWidget {
|
||||
return Center(
|
||||
child: Container(
|
||||
width: size?.width,
|
||||
height: size?.height,
|
||||
color: Color(0xffd0021B).withOpacity(.1),
|
||||
height: size?.height ?? 200,
|
||||
color: StreamChatTheme.of(context).colorTheme.accentRed.withOpacity(.1),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.error_outline,
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -32,7 +32,7 @@ class AttachmentTitle extends StatelessWidget {
|
||||
attachment.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: messageTheme.messageText.copyWith(
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter/src/unread_indicator.dart';
|
||||
|
||||
import '../stream_chat_flutter.dart';
|
||||
|
||||
class StreamBackButton extends StatelessWidget {
|
||||
const StreamBackButton({
|
||||
Key key,
|
||||
@@ -35,9 +37,7 @@ class StreamBackButton extends StatelessWidget {
|
||||
},
|
||||
child: StreamSvgIcon.left(
|
||||
size: 24,
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -73,7 +73,7 @@ class ChannelBottomSheet extends StatelessWidget {
|
||||
ListTile(
|
||||
leading: StreamSvgIcon.userRemove(
|
||||
size: 24,
|
||||
color: Color(0xff7A7A7A),
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
title: Text(
|
||||
'Leave Group',
|
||||
@@ -106,13 +106,13 @@ class ChannelBottomSheet extends StatelessWidget {
|
||||
?.role))
|
||||
ListTile(
|
||||
leading: StreamSvgIcon.delete(
|
||||
color: Color(0xFFFF3742),
|
||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||
size: 24,
|
||||
),
|
||||
title: Text(
|
||||
'Delete chat',
|
||||
style: TextStyle(
|
||||
color: Color(0xFFFF3742),
|
||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||
),
|
||||
),
|
||||
onTap: () async {
|
||||
@@ -124,7 +124,7 @@ class ChannelBottomSheet extends StatelessWidget {
|
||||
'Are you sure you want to delete this conversation?',
|
||||
cancelText: 'CANCEL',
|
||||
icon: StreamSvgIcon.delete(
|
||||
color: Colors.red,
|
||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||
),
|
||||
);
|
||||
var channel = StreamChannel.of(context).channel;
|
||||
|
||||
@@ -52,6 +52,9 @@ class ChannelImage extends StatelessWidget {
|
||||
this.onTap,
|
||||
this.showOnlineStatus = true,
|
||||
this.borderRadius,
|
||||
this.selected = false,
|
||||
this.selectionColor,
|
||||
this.selectionThickness = 4,
|
||||
}) : super(key: key);
|
||||
|
||||
final BorderRadius borderRadius;
|
||||
@@ -67,6 +70,12 @@ class ChannelImage extends StatelessWidget {
|
||||
|
||||
final bool showOnlineStatus;
|
||||
|
||||
final bool selected;
|
||||
|
||||
final Color selectionColor;
|
||||
|
||||
final double selectionThickness;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final streamChat = StreamChat.of(context);
|
||||
@@ -94,11 +103,11 @@ class ChannelImage extends StatelessWidget {
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
onTap: onTap != null
|
||||
? (_) {
|
||||
onTap();
|
||||
}
|
||||
: null,
|
||||
onTap: onTap != null ? (_) => onTap() : null,
|
||||
selected: selected,
|
||||
selectionColor: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
selectionThickness: selectionThickness,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
@@ -111,16 +120,21 @@ class ChannelImage extends StatelessWidget {
|
||||
.toList();
|
||||
return GroupImage(
|
||||
images: images,
|
||||
borderRadius: borderRadius,
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
onTap: onTap,
|
||||
selected: selected,
|
||||
selectionColor: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
selectionThickness: selectionThickness,
|
||||
);
|
||||
}
|
||||
|
||||
return ClipRRect(
|
||||
Widget child = ClipRRect(
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
@@ -133,7 +147,7 @@ class ChannelImage extends StatelessWidget {
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
@@ -149,7 +163,9 @@ class ChannelImage extends StatelessWidget {
|
||||
? snapshot.data['name'][0]
|
||||
: '',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
@@ -169,6 +185,30 @@ class ChannelImage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
if (selected) {
|
||||
child = ClipRRect(
|
||||
borderRadius: (borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.borderRadius) +
|
||||
BorderRadius.circular(selectionThickness),
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
color: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(selectionThickness),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return child;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ class ChannelInfo extends StatelessWidget {
|
||||
child: Text(
|
||||
'Try Again',
|
||||
style: textStyle.copyWith(
|
||||
color: Color(0xFF006CFF),
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -99,7 +99,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
var color;
|
||||
switch (status) {
|
||||
case ConnectionStatus.connected:
|
||||
color = Color(0xFF006CFF);
|
||||
color = StreamChatTheme.of(context).colorTheme.accentBlue;
|
||||
break;
|
||||
case ConnectionStatus.connecting:
|
||||
color = Colors.grey;
|
||||
@@ -144,13 +144,8 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
|
||||
Widget _buildConnectedTitleState(BuildContext context) => Text(
|
||||
'Stream Chat',
|
||||
style: StreamChatTheme.of(context)
|
||||
.channelTheme
|
||||
.channelHeaderTheme
|
||||
.title
|
||||
.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
style: StreamChatTheme.of(context).textTheme.headlineBold.copyWith(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -210,7 +205,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF006CFF),
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
+189
-95
@@ -74,6 +74,8 @@ class ChannelListView extends StatefulWidget {
|
||||
this.onStartChatPressed,
|
||||
this.swipeToAction = false,
|
||||
this.pullToRefresh = true,
|
||||
this.crossAxisCount = 1,
|
||||
this.selectedChannels = const [],
|
||||
}) : super(key: key);
|
||||
|
||||
/// The builder that will be used in case of error
|
||||
@@ -134,6 +136,11 @@ class ChannelListView extends StatefulWidget {
|
||||
/// Callback used in the default empty list widget
|
||||
final VoidCallback onStartChatPressed;
|
||||
|
||||
/// The number of children in the cross axis.
|
||||
final int crossAxisCount;
|
||||
|
||||
final List<Channel> selectedChannels;
|
||||
|
||||
@override
|
||||
_ChannelListViewState createState() => _ChannelListViewState();
|
||||
}
|
||||
@@ -203,16 +210,18 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: StreamSvgIcon.message(
|
||||
size: 136,
|
||||
color: Color(0xffDBDBDB),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.greyGainsboro,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'Let’s start chatting!',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.headline,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
@@ -223,10 +232,14 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
child: Text(
|
||||
'How about sending your first message to a friend?',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xff7A7A7A),
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.body
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -242,11 +255,14 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
onPressed: widget.onStartChatPressed,
|
||||
child: Text(
|
||||
'Start a chat',
|
||||
style: TextStyle(
|
||||
color:
|
||||
StreamChatTheme.of(context).accentColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -259,22 +275,33 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
}
|
||||
|
||||
if (channels.isNotEmpty) {
|
||||
child = ListView.custom(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
childrenDelegate: SliverChildBuilderDelegate(
|
||||
(context, i) {
|
||||
return _itemBuilder(context, i, channels);
|
||||
if (widget.crossAxisCount > 1) {
|
||||
child = GridView.builder(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: widget.crossAxisCount),
|
||||
itemCount: channels.length,
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return _gridItemBuilder(context, index, 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;
|
||||
);
|
||||
} else {
|
||||
child = ListView.separated(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
itemCount:
|
||||
channels.isNotEmpty ? channels.length + 1 : channels.length,
|
||||
separatorBuilder: (_, index) {
|
||||
if (widget.separatorBuilder != null) {
|
||||
return widget.separatorBuilder(context, index);
|
||||
}
|
||||
return _separatorBuilder(context, index);
|
||||
},
|
||||
),
|
||||
);
|
||||
itemBuilder: (context, index) {
|
||||
return _listItemBuilder(context, index, channels);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,11 +319,13 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
children: List.generate(
|
||||
25,
|
||||
(i) {
|
||||
if (i % 2 != 0) {
|
||||
if (widget.separatorBuilder != null) {
|
||||
return widget.separatorBuilder(context, i);
|
||||
if (widget.crossAxisCount == 1) {
|
||||
if (i % 2 != 0) {
|
||||
if (widget.separatorBuilder != null) {
|
||||
return widget.separatorBuilder(context, i);
|
||||
}
|
||||
return _separatorBuilder(context, i);
|
||||
}
|
||||
return _separatorBuilder(context, i);
|
||||
}
|
||||
return _buildLoadingItem();
|
||||
},
|
||||
@@ -305,63 +334,96 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
}
|
||||
|
||||
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,
|
||||
),
|
||||
if (widget.crossAxisCount > 1) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
highlightColor: StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 4.0,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
for (int i = 0; i < widget.crossAxisCount; i++)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 70,
|
||||
width: 70,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
title: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
);
|
||||
} else {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
highlightColor: StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
child: ListTile(
|
||||
leading: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 16,
|
||||
width: 82,
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
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),
|
||||
title: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 16,
|
||||
width: 42,
|
||||
width: 82,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 16,
|
||||
width: 238,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 16,
|
||||
width: 42,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildErrorWidget(
|
||||
@@ -428,20 +490,10 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _itemBuilder(context, int i, List<Channel> channels) {
|
||||
if (i % 2 != 0) {
|
||||
if (widget.separatorBuilder != null) {
|
||||
return widget.separatorBuilder(context, i);
|
||||
}
|
||||
return _separatorBuilder(context, i);
|
||||
}
|
||||
|
||||
i = i ~/ 2;
|
||||
|
||||
Widget _listItemBuilder(BuildContext context, int i, List<Channel> channels) {
|
||||
final channelsProvider = ChannelsBloc.of(context);
|
||||
if (i < channels.length) {
|
||||
final channel = channels[i];
|
||||
|
||||
ChannelTapCallback onTap;
|
||||
if (widget.onChannelTap != null) {
|
||||
onTap = widget.onChannelTap;
|
||||
@@ -493,9 +545,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
);
|
||||
} else {
|
||||
final backgroundColor =
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? Color(0xffEBEBEB)
|
||||
: Color(0xff141414);
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke;
|
||||
child = Slidable(
|
||||
enabled: widget.swipeToAction,
|
||||
actionPane: SlidableBehindActionPane(),
|
||||
@@ -535,7 +585,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
IconSlideAction(
|
||||
color: backgroundColor,
|
||||
iconWidget: StreamSvgIcon.delete(
|
||||
color: Color(0xFFFF3742),
|
||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||
),
|
||||
onTap: () async {
|
||||
final res = await showConfirmationDialog(
|
||||
@@ -546,7 +596,9 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
'Are you sure you want to delete this conversation?',
|
||||
cancelText: 'CANCEL',
|
||||
icon: StreamSvgIcon.delete(
|
||||
color: Color(0xFFFF3742),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentRed,
|
||||
),
|
||||
);
|
||||
if (res == true) {
|
||||
@@ -556,7 +608,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
),
|
||||
],
|
||||
child: Container(
|
||||
color: StreamChatTheme.of(context).backgroundColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
child: ChannelPreview(
|
||||
onLongPress: widget.onChannelLongPress,
|
||||
channel: channel,
|
||||
@@ -581,6 +633,47 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
}
|
||||
}
|
||||
|
||||
Widget _gridItemBuilder(BuildContext context, int i, List<Channel> channels) {
|
||||
var channel = channels[i];
|
||||
|
||||
var selected = widget.selectedChannels.contains(channel);
|
||||
|
||||
return Container(
|
||||
key: ValueKey<String>('CHANNEL-${channel.id}'),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
ChannelImage(
|
||||
channel: channel,
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
selected: selected,
|
||||
constraints: BoxConstraints.tightFor(
|
||||
width: 64,
|
||||
height: 64,
|
||||
),
|
||||
onTap: () {
|
||||
widget.onChannelTap(channel, null);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 7),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: StreamChannel(
|
||||
child: ChannelName(
|
||||
textStyle: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
channel: channel,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQueryProgressIndicator(
|
||||
context,
|
||||
ChannelsBlocState channelsProvider,
|
||||
@@ -591,7 +684,10 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return Container(
|
||||
color: Color(0xffd0021B).withAlpha(26),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentRed
|
||||
.withOpacity(.2),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Center(
|
||||
@@ -613,9 +709,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
Widget _separatorBuilder(context, i) {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withOpacity(0.1)
|
||||
: Colors.black.withOpacity(0.1),
|
||||
color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.08),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ class ChannelPreview extends StatelessWidget {
|
||||
return Opacity(
|
||||
opacity: snapshot.data ? 0.5 : 1,
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
),
|
||||
onTap: () {
|
||||
if (onTap != null) {
|
||||
onTap(channel);
|
||||
@@ -122,12 +125,16 @@ class ChannelPreview extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(right: 4.0),
|
||||
child: SendingIndicator(
|
||||
message: channel.state.lastMessage,
|
||||
allRead: channel.state.read
|
||||
size: StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.lastMessageAt
|
||||
.fontSize,
|
||||
isMessageRead: channel.state.read
|
||||
.where((element) => element.lastRead
|
||||
.isAfter(channel
|
||||
.state.lastMessage.createdAt))
|
||||
.length ==
|
||||
(channel.memberCount ?? 0),
|
||||
((channel.memberCount ?? 0) - 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:emojis/emojis.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
@@ -21,7 +20,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
return Scaffold(
|
||||
backgroundColor: Color(0xFFe6e6e6),
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
body: ListView(
|
||||
children: [
|
||||
_buildUserHeader(),
|
||||
@@ -47,7 +46,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
|
||||
Widget _buildUserHeader() {
|
||||
return Material(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
child: SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
@@ -81,7 +80,11 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
child: Text(
|
||||
widget.user.name,
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5), fontSize: 16.0),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
fontSize: 16.0),
|
||||
),
|
||||
),
|
||||
onTap: () {},
|
||||
@@ -92,7 +95,9 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
top: 21,
|
||||
left: 16,
|
||||
child: InkWell(
|
||||
child: StreamSvgIcon.left(),
|
||||
child: StreamSvgIcon.left(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
@@ -113,7 +118,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
// title: 'Notifications',
|
||||
// leading: StreamSvgIcon.Icon_notification(
|
||||
// size: 24.0,
|
||||
// color: Colors.black.withOpacity(0.5),
|
||||
// color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
// ),
|
||||
// trailing: CupertinoSwitch(
|
||||
// value: true,
|
||||
@@ -128,7 +133,10 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
title: 'Mute user',
|
||||
leading: StreamSvgIcon.mute(
|
||||
size: 23.0,
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
trailing: snapshot.data == null
|
||||
? CircularProgressIndicator()
|
||||
@@ -149,7 +157,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
// title: 'Block User',
|
||||
// leading: StreamSvgIcon.Icon_user_delete(
|
||||
// size: 24.0,
|
||||
// color: Colors.black.withOpacity(0.5),
|
||||
// color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
// ),
|
||||
// trailing: CupertinoSwitch(
|
||||
// value: widget.user.banned,
|
||||
@@ -167,7 +175,8 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
title: 'Photos & Videos',
|
||||
leading: StreamSvgIcon.pictures(
|
||||
size: 32.0,
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
),
|
||||
trailing: StreamSvgIcon.right(),
|
||||
onTap: () {
|
||||
@@ -179,7 +188,8 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
title: 'Files',
|
||||
leading: StreamSvgIcon.files(
|
||||
size: 32.0,
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
),
|
||||
trailing: StreamSvgIcon.right(),
|
||||
onTap: () {
|
||||
@@ -191,7 +201,8 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
title: 'Shared groups',
|
||||
leading: StreamSvgIcon.Icon_group(
|
||||
size: 24.0,
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
),
|
||||
trailing: StreamSvgIcon.right(),
|
||||
onTap: () {
|
||||
@@ -208,7 +219,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
|
||||
Widget _buildDeleteListTile() {
|
||||
return _OptionListTile(
|
||||
title: 'Delete',
|
||||
title: 'Delete conversation',
|
||||
leading: StreamSvgIcon.delete(
|
||||
color: Colors.red,
|
||||
size: 24.0,
|
||||
@@ -248,12 +259,20 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
if (otherMember.online) {
|
||||
alternativeWidget = Text(
|
||||
'Online',
|
||||
style: TextStyle(color: Colors.black.withOpacity(0.5)),
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
);
|
||||
} else {
|
||||
alternativeWidget = Text(
|
||||
'Last seen ${Jiffy(otherMember.lastActive).fromNow()}',
|
||||
style: TextStyle(color: Colors.black.withOpacity(0.5)),
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -272,10 +291,10 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
),
|
||||
child: Material(
|
||||
shape: CircleBorder(),
|
||||
color: Color(0xff20E070),
|
||||
color: StreamChatTheme.of(context).colorTheme.accentGreen,
|
||||
),
|
||||
),
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
alternativeWidget,
|
||||
],
|
||||
@@ -303,11 +322,11 @@ class _OptionListTile extends StatelessWidget {
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
color: Color(0xffe6e6e6),
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
height: 2.0,
|
||||
),
|
||||
Material(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
child: Container(
|
||||
height: 56.0,
|
||||
child: InkWell(
|
||||
@@ -365,14 +384,16 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
|
||||
var chat = StreamChat.of(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
'Shared Groups',
|
||||
style: TextStyle(color: Colors.black, fontSize: 16.0),
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16.0),
|
||||
),
|
||||
leading: Center(
|
||||
child: InkWell(
|
||||
@@ -381,7 +402,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
|
||||
},
|
||||
child: Container(
|
||||
child: StreamSvgIcon.left(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24.0,
|
||||
),
|
||||
width: 24.0,
|
||||
@@ -389,7 +410,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: StreamChatTheme.of(context).primaryColor,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
body: FutureBuilder<List<Channel>>(
|
||||
future: chat.client.queryChannels(
|
||||
@@ -488,7 +509,11 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'${channel.memberCount} members',
|
||||
style: TextStyle(color: Colors.black.withOpacity(0.5)),
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
),
|
||||
)
|
||||
],
|
||||
@@ -496,7 +521,8 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
|
||||
),
|
||||
Container(
|
||||
height: 1.0,
|
||||
color: Color(0xffe6e6e6),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -509,14 +535,16 @@ class _MediaDisplayScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
'Photos & Videos',
|
||||
style: TextStyle(color: Colors.black, fontSize: 16.0),
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16.0),
|
||||
),
|
||||
leading: Center(
|
||||
child: InkWell(
|
||||
@@ -525,7 +553,7 @@ class _MediaDisplayScreen extends StatelessWidget {
|
||||
},
|
||||
child: Container(
|
||||
child: StreamSvgIcon.left(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24.0,
|
||||
),
|
||||
width: 24.0,
|
||||
@@ -533,7 +561,7 @@ class _MediaDisplayScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: StreamChatTheme.of(context).primaryColor,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -543,14 +571,16 @@ class _FileDisplayScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
appBar: AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
'Files',
|
||||
style: TextStyle(color: Colors.black, fontSize: 16.0),
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
fontSize: 16.0),
|
||||
),
|
||||
leading: Center(
|
||||
child: InkWell(
|
||||
@@ -559,7 +589,7 @@ class _FileDisplayScreen extends StatelessWidget {
|
||||
},
|
||||
child: Container(
|
||||
child: StreamSvgIcon.left(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24.0,
|
||||
),
|
||||
width: 24.0,
|
||||
@@ -567,7 +597,7 @@ class _FileDisplayScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: StreamChatTheme.of(context).primaryColor,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||
|
||||
/// It shows a date divider depending on the date difference
|
||||
class DateDivider extends StatelessWidget {
|
||||
@@ -39,21 +40,17 @@ class DateDivider extends StatelessWidget {
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: (Theme.of(context).brightness == Brightness.light
|
||||
? Colors.black
|
||||
: Colors.white)
|
||||
.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(
|
||||
8,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
dayInfo,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.footnoteBold.copyWith(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -41,8 +41,14 @@ class DeletedMessage extends StatelessWidget {
|
||||
side: borderSide ??
|
||||
BorderSide(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withAlpha(24)
|
||||
: Colors.black.withAlpha(24),
|
||||
? StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white
|
||||
.withAlpha(24)
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withAlpha(24),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
@@ -58,8 +64,8 @@ class DeletedMessage extends StatelessWidget {
|
||||
style: messageTheme.messageText.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
color: (Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black)
|
||||
? StreamChatTheme.of(context).colorTheme.white
|
||||
: StreamChatTheme.of(context).colorTheme.black)
|
||||
.withOpacity(.5),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter/src/utils.dart';
|
||||
import 'package:video_compress/video_compress.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import 'media_utils.dart';
|
||||
|
||||
enum FileAttachmentType { local, online }
|
||||
@@ -61,13 +62,13 @@ class _FileAttachmentState extends State<FileAttachment> {
|
||||
child: Container(
|
||||
width: widget.size?.width ?? 100,
|
||||
height: 56.0,
|
||||
margin: widget.trailing != null ? EdgeInsets.only(top: 4.0) : null,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius:
|
||||
widget.trailing != null ? BorderRadius.circular(16.0) : null,
|
||||
border: widget.trailing != null
|
||||
? Border.fromBorderSide(BorderSide(color: Color(0xFFE6E6E6)))
|
||||
? Border.fromBorderSide(BorderSide(
|
||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper))
|
||||
: null,
|
||||
),
|
||||
child: Row(
|
||||
@@ -88,10 +89,7 @@ class _FileAttachmentState extends State<FileAttachment> {
|
||||
children: [
|
||||
Text(
|
||||
widget.attachment?.title ?? 'File',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -100,10 +98,11 @@ class _FileAttachmentState extends State<FileAttachment> {
|
||||
),
|
||||
Text(
|
||||
'${getSizeText(widget.attachment.extraData['file_size'])}',
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
fontSize: 14.0,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.body.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -113,7 +112,7 @@ class _FileAttachmentState extends State<FileAttachment> {
|
||||
widget.trailing ??
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.cloud_download(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () {
|
||||
launchURL(context, widget.attachment.assetUrl);
|
||||
@@ -148,7 +147,8 @@ class _FileAttachmentState extends State<FileAttachment> {
|
||||
width: 20.0,
|
||||
height: 20.0,
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor: StreamChatTheme.of(context).accentColor,
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -112,7 +112,10 @@ class GiphyAttachment extends StatelessWidget {
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
child: Material(
|
||||
color: Colors.black.withOpacity(.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
@@ -124,13 +127,16 @@ class GiphyAttachment extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.lightning(
|
||||
color: Colors.white,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.white,
|
||||
size: 16,
|
||||
),
|
||||
Text(
|
||||
'GIPHY',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 11,
|
||||
),
|
||||
@@ -202,7 +208,10 @@ class GiphyAttachment extends StatelessWidget {
|
||||
height: 4.0,
|
||||
),
|
||||
Container(
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.2),
|
||||
width: double.infinity,
|
||||
height: 0.5,
|
||||
),
|
||||
@@ -220,15 +229,24 @@ class GiphyAttachment extends StatelessWidget {
|
||||
},
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black.withOpacity(0.5)),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 0.5,
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.2),
|
||||
height: 50.0,
|
||||
),
|
||||
Expanded(
|
||||
@@ -242,7 +260,9 @@ class GiphyAttachment extends StatelessWidget {
|
||||
child: Text(
|
||||
'Send',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
@@ -263,7 +283,10 @@ class GiphyAttachment extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
StreamSvgIcon.eye(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
size: 16.0,
|
||||
),
|
||||
SizedBox(
|
||||
@@ -271,10 +294,14 @@ class GiphyAttachment extends StatelessWidget {
|
||||
),
|
||||
Text(
|
||||
'Only visible to you',
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
fontSize: 12.0,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -331,7 +358,10 @@ class GiphyAttachment extends StatelessWidget {
|
||||
bottom: 8,
|
||||
left: 8,
|
||||
child: Material(
|
||||
color: Colors.black.withOpacity(.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
@@ -343,13 +373,13 @@ class GiphyAttachment extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.lightning(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
size: 16,
|
||||
),
|
||||
Text(
|
||||
'GIPHY',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 11,
|
||||
),
|
||||
|
||||
@@ -9,21 +9,33 @@ class GroupImage extends StatelessWidget {
|
||||
@required this.images,
|
||||
this.constraints,
|
||||
this.onTap,
|
||||
this.borderRadius,
|
||||
this.selected = false,
|
||||
this.selectionColor,
|
||||
this.selectionThickness = 4,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<String> images;
|
||||
final BoxConstraints constraints;
|
||||
final VoidCallback onTap;
|
||||
final bool selected;
|
||||
final BorderRadius borderRadius;
|
||||
final Color selectionColor;
|
||||
final double selectionThickness;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
var avatar;
|
||||
final streamChatTheme = StreamChatTheme.of(context);
|
||||
|
||||
avatar = GestureDetector(
|
||||
onTap: onTap,
|
||||
child: ClipRRect(
|
||||
borderRadius: StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.borderRadius,
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.borderRadius,
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
@@ -31,7 +43,7 @@ class GroupImage extends StatelessWidget {
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
child: Flex(
|
||||
direction: Axis.vertical,
|
||||
@@ -91,5 +103,25 @@ class GroupImage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (selected) {
|
||||
avatar = ClipRRect(
|
||||
borderRadius: (borderRadius ??
|
||||
streamChatTheme.ownMessageTheme.avatarTheme.borderRadius) +
|
||||
BorderRadius.circular(selectionThickness),
|
||||
child: Container(
|
||||
color: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
height: 64.0,
|
||||
width: 64.0,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(selectionThickness),
|
||||
child: avatar,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return avatar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
import '../stream_chat_flutter.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../stream_chat_flutter.dart';
|
||||
|
||||
class ImageActionsModal extends StatelessWidget {
|
||||
final Message message;
|
||||
final String userName;
|
||||
@@ -66,11 +67,12 @@ class ImageActionsModal extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
userName,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.headlineBold
|
||||
.copyWith(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.white),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
@@ -78,14 +80,18 @@ class ImageActionsModal extends StatelessWidget {
|
||||
style: StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.subtitle
|
||||
.copyWith(color: Colors.white.withOpacity(0.5)),
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white
|
||||
.withOpacity(0.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
size: 24.0,
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
@@ -104,10 +110,10 @@ class ImageActionsModal extends StatelessWidget {
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
color: Color(0xffe5e5e5),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: ListTile.divideTiles(
|
||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||
context: context,
|
||||
tiles: [
|
||||
_buildButton(
|
||||
@@ -115,7 +121,10 @@ class ImageActionsModal extends StatelessWidget {
|
||||
'Reply',
|
||||
StreamSvgIcon.Icon_curve_line_left_up(
|
||||
size: 24.0,
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
() {}),
|
||||
_buildButton(
|
||||
@@ -123,7 +132,8 @@ class ImageActionsModal extends StatelessWidget {
|
||||
'Show in Chat',
|
||||
StreamSvgIcon.eye(
|
||||
size: 24.0,
|
||||
color: Colors.black,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black,
|
||||
), () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
@@ -133,7 +143,10 @@ class ImageActionsModal extends StatelessWidget {
|
||||
'Save ${urls[currentIndex].type == 'video' ? 'Video' : 'Image'}',
|
||||
StreamSvgIcon.Icon_save(
|
||||
size: 24.0,
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
), () async {
|
||||
var url = urls[currentIndex].imageUrl ??
|
||||
urls[currentIndex].assetUrl ??
|
||||
@@ -153,7 +166,9 @@ class ImageActionsModal extends StatelessWidget {
|
||||
'Delete',
|
||||
StreamSvgIcon.delete(
|
||||
size: 24.0,
|
||||
color: Color(0xffFF3742),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentRed,
|
||||
),
|
||||
() {
|
||||
Navigator.pop(context);
|
||||
@@ -163,7 +178,9 @@ class ImageActionsModal extends StatelessWidget {
|
||||
StreamChannel.of(context).channel.cid,
|
||||
);
|
||||
},
|
||||
color: Color(0xffFF3742),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentRed,
|
||||
),
|
||||
],
|
||||
).toList(),
|
||||
@@ -182,10 +199,11 @@ class ImageActionsModal extends StatelessWidget {
|
||||
{Color color}) {
|
||||
var titleStyle = TextStyle(
|
||||
fontSize: 14.5,
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
);
|
||||
|
||||
return Material(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: ListTile(
|
||||
|
||||
+124
-118
@@ -5,10 +5,10 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:chewie/chewie.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
@@ -16,8 +16,6 @@ import 'package:stream_chat_flutter/src/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'stream_channel.dart';
|
||||
|
||||
class ImageFooter extends StatefulWidget {
|
||||
/// Callback to call when pressing the back button.
|
||||
/// By default it calls [Navigator.pop]
|
||||
@@ -64,11 +62,11 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
bool _userSearchMode = false;
|
||||
TextEditingController _searchController;
|
||||
TextEditingController _messageController = TextEditingController();
|
||||
FocusNode _messageFocusNode = FocusNode();
|
||||
|
||||
String _userNameQuery;
|
||||
bool _isSearchActive = false;
|
||||
String _channelNameQuery;
|
||||
|
||||
Set<User> _selectedUsers = {};
|
||||
List<Channel> _selectedChannels = [];
|
||||
bool _loading = false;
|
||||
|
||||
Timer _debounce;
|
||||
@@ -80,8 +78,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
_debounce = Timer(const Duration(milliseconds: 350), () {
|
||||
if (mounted && modalSetStateCallback != null) {
|
||||
modalSetStateCallback(() {
|
||||
_userNameQuery = _searchController.text;
|
||||
_isSearchActive = _userNameQuery.isNotEmpty;
|
||||
_channelNameQuery = _searchController.text;
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -91,6 +88,9 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_searchController = TextEditingController()..addListener(_userNameListener);
|
||||
_messageFocusNode.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -114,7 +114,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.icon_SHARE(
|
||||
size: 24.0,
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () {
|
||||
_buildShareModal(context);
|
||||
@@ -130,11 +130,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'${widget.currentPage + 1} of ${widget.totalPages}',
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.black,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -142,7 +138,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
),
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.Icon_grid(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () {
|
||||
_buildPhotosModal(context);
|
||||
@@ -170,7 +166,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(16.0),
|
||||
topLeft: Radius.circular(16.0),
|
||||
@@ -182,11 +178,8 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Photos',
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.black,
|
||||
),
|
||||
style:
|
||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -194,7 +187,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
@@ -205,7 +198,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
),
|
||||
),
|
||||
Container(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
@@ -258,7 +251,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildShareModal(context) {
|
||||
void _buildShareModal(context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
@@ -266,7 +259,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
modalSetStateCallback = modalSetState;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: _userSearchMode
|
||||
top: _userSearchMode || _messageFocusNode.hasFocus
|
||||
? 16.0
|
||||
: MediaQuery.of(context).size.height / 2,
|
||||
left: 8.0,
|
||||
@@ -282,80 +275,89 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
child: Column(
|
||||
children: [
|
||||
_buildTextInputSection(modalSetState),
|
||||
if (_userSearchMode)
|
||||
SizedBox(
|
||||
height: 22.0,
|
||||
),
|
||||
Expanded(
|
||||
child: UserListView(
|
||||
selectedUsers: _selectedUsers,
|
||||
onUserTap: (user, _) {
|
||||
_searchController.clear();
|
||||
if (!_selectedUsers.contains(user)) {
|
||||
modalSetState(() {
|
||||
_selectedUsers.add(user);
|
||||
});
|
||||
} else {
|
||||
modalSetState(() {
|
||||
_selectedUsers.remove(user);
|
||||
});
|
||||
}
|
||||
},
|
||||
crossAxisCount: 4,
|
||||
pagination: PaginationParams(
|
||||
limit: 25,
|
||||
),
|
||||
filter: {
|
||||
if (_searchController.text.isNotEmpty)
|
||||
'name': {
|
||||
r'$autocomplete': _userNameQuery,
|
||||
},
|
||||
'id': {
|
||||
r'$ne': StreamChat.of(context).user.id,
|
||||
child: ChannelsBloc(
|
||||
child: ChannelListView(
|
||||
selectedChannels: _selectedChannels,
|
||||
onChannelTap: (channel, _) {
|
||||
_searchController.clear();
|
||||
if (!_selectedChannels.contains(channel)) {
|
||||
modalSetState(() {
|
||||
_selectedChannels.add(channel);
|
||||
});
|
||||
} else {
|
||||
modalSetState(() {
|
||||
_selectedChannels.remove(channel);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
sort: [
|
||||
SortOption(
|
||||
'name',
|
||||
direction: 1,
|
||||
crossAxisCount: 4,
|
||||
pagination: PaginationParams(
|
||||
limit: 25,
|
||||
),
|
||||
],
|
||||
emptyBuilder: (_) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: Colors.grey,
|
||||
filter: {
|
||||
if (_channelNameQuery?.trim()?.isNotEmpty == true)
|
||||
'name': {
|
||||
r'$autocomplete': _channelNameQuery,
|
||||
},
|
||||
'id': {
|
||||
r'$ne': StreamChat.of(context).user.id,
|
||||
},
|
||||
},
|
||||
sort: [
|
||||
SortOption(
|
||||
'name',
|
||||
direction: SortOption.ASC,
|
||||
),
|
||||
],
|
||||
emptyBuilder: (_) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight:
|
||||
viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'No user matches these keywords...'),
|
||||
],
|
||||
Text(
|
||||
'No chat matches these keywords...'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_selectedUsers.isNotEmpty)
|
||||
if (_selectedChannels.isNotEmpty)
|
||||
_buildShareTextInputSection(modalSetState),
|
||||
if (!_userSearchMode && _selectedUsers.isEmpty)
|
||||
if (!_userSearchMode && _selectedChannels.isEmpty)
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
height: 48.0,
|
||||
child: Material(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.white,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
var url = widget
|
||||
@@ -385,7 +387,8 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
'Save to Photos',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.accentColor,
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
@@ -422,7 +425,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _searchController,
|
||||
cursorColor: Colors.black,
|
||||
cursorColor: StreamChatTheme.of(context).colorTheme.black,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
@@ -432,19 +435,25 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2.0, horizontal: 6.0),
|
||||
child: StreamSvgIcon.search(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
),
|
||||
hintText: 'Search',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
borderSide:
|
||||
BorderSide(color: Colors.black.withOpacity(0.08)),
|
||||
borderSide: BorderSide(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.08)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
borderSide:
|
||||
BorderSide(color: Colors.black.withOpacity(0.08)),
|
||||
borderSide: BorderSide(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.08)),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
@@ -455,7 +464,10 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
),
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.close_small(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
onPressed: () {
|
||||
modalSetState(() {
|
||||
@@ -476,7 +488,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.search(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
iconSize: 24.0,
|
||||
onPressed: () {
|
||||
@@ -490,18 +502,14 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Select a Chat to Share',
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.black,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.share_arrow(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () async {
|
||||
var url =
|
||||
@@ -528,7 +536,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
return Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
height: 56.0,
|
||||
child: _loading
|
||||
? Center(
|
||||
@@ -544,6 +552,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: TextField(
|
||||
controller: _messageController,
|
||||
focusNode: _messageFocusNode,
|
||||
onChanged: (val) {
|
||||
modalSetState(() {});
|
||||
},
|
||||
@@ -558,13 +567,19 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.black.withOpacity(0.16),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.16),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.black.withOpacity(0.16),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.16),
|
||||
)),
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 12.0),
|
||||
),
|
||||
@@ -591,7 +606,9 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
child: Transform.rotate(
|
||||
angle: -pi / 2,
|
||||
child: StreamSvgIcon.Icon_send_message(
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -612,27 +629,16 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
|
||||
_messageController.clear();
|
||||
|
||||
final client = StreamChat.of(context).client;
|
||||
|
||||
for (var user in _selectedUsers) {
|
||||
var c = client.channel('messaging', extraData: {
|
||||
'members': [
|
||||
user.id,
|
||||
StreamChat.of(context).user.id,
|
||||
],
|
||||
});
|
||||
|
||||
await c.watch();
|
||||
|
||||
for (var channel in _selectedChannels) {
|
||||
final message = Message(
|
||||
text: text,
|
||||
attachments: [attachments[widget.currentPage]],
|
||||
);
|
||||
|
||||
await c.sendMessage(message);
|
||||
await channel.sendMessage(message);
|
||||
}
|
||||
|
||||
_selectedUsers.clear();
|
||||
_selectedChannels.clear();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,9 @@ class ImageGroup extends StatelessWidget {
|
||||
child: Text(
|
||||
'${images.length - 4} +',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
fontSize: 28,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -50,7 +50,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
leading: showBackButton
|
||||
? IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24.0,
|
||||
),
|
||||
onPressed: onBackPressed,
|
||||
@@ -61,7 +61,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.Icon_menu_point_v(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () {
|
||||
_showMessageActionModalBottomSheet(context);
|
||||
@@ -81,11 +81,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
children: <Widget>[
|
||||
Text(
|
||||
userName,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.black,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
),
|
||||
Text(
|
||||
sentAt,
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import '../stream_chat_flutter.dart';
|
||||
|
||||
extension on Duration {
|
||||
String format() {
|
||||
@@ -80,7 +83,10 @@ class _MediaListViewState extends State<MediaListView> {
|
||||
? 1.0
|
||||
: 0.0,
|
||||
child: Container(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
alignment: Alignment.topRight,
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
@@ -88,10 +94,12 @@ class _MediaListViewState extends State<MediaListView> {
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 12,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).colorTheme.white,
|
||||
child: StreamSvgIcon.check(
|
||||
size: 24,
|
||||
color: Colors.black,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -113,7 +121,7 @@ class _MediaListViewState extends State<MediaListView> {
|
||||
child: Text(
|
||||
media.videoDuration.format(),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -141,7 +149,15 @@ class _MediaListViewState extends State<MediaListView> {
|
||||
void _getMedia() async {
|
||||
final assetList = await PhotoManager.getAssetPathList(
|
||||
hasAll: true,
|
||||
).then((value) => value.singleWhere((element) => element.isAll));
|
||||
).then((value) {
|
||||
if (value?.isNotEmpty == true) {
|
||||
return value.singleWhere((element) => element.isAll);
|
||||
}
|
||||
});
|
||||
|
||||
if (assetList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final media = await assetList.getAssetListPaged(_currentPage, 50);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ class MessageActionsModal extends StatelessWidget {
|
||||
final bool showDeleteMessage;
|
||||
final bool showCopyMessage;
|
||||
final bool showEditMessage;
|
||||
final bool showResendMessage;
|
||||
final bool showReply;
|
||||
final bool showThreadReply;
|
||||
final bool reverse;
|
||||
@@ -40,6 +41,7 @@ class MessageActionsModal extends StatelessWidget {
|
||||
this.onThreadReplyTap,
|
||||
this.showCopyMessage = true,
|
||||
this.showReply = true,
|
||||
this.showResendMessage = true,
|
||||
this.showThreadReply = true,
|
||||
this.showUserAvatar = DisplayWidget.show,
|
||||
this.editMessageInputBuilder,
|
||||
@@ -73,7 +75,7 @@ class MessageActionsModal extends StatelessWidget {
|
||||
sigmaY: 10.8731,
|
||||
),
|
||||
child: Container(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
color: Colors.black.withOpacity(.2),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -122,7 +124,10 @@ class MessageActionsModal extends StatelessWidget {
|
||||
showUserAvatar: showUserAvatar,
|
||||
showTimestamp: false,
|
||||
translateUserAvatar: false,
|
||||
showReactionPickerIndicator: true,
|
||||
showReactionPickerIndicator: showReactions &&
|
||||
(message.status ==
|
||||
MessageSendingStatus.SENT ||
|
||||
message.status == null),
|
||||
showInChannelIndicator: false,
|
||||
showSendingIndicator: DisplayWidget.gone,
|
||||
shape: messageShape,
|
||||
@@ -130,9 +135,7 @@ class MessageActionsModal extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}),
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
TweenAnimationBuilder<double>(
|
||||
tween: Tween(begin: 0.0, end: 1.0),
|
||||
duration: Duration(milliseconds: 300),
|
||||
@@ -148,6 +151,9 @@ class MessageActionsModal extends StatelessWidget {
|
||||
horizontal: 48.0,
|
||||
),
|
||||
child: Material(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.whiteSnow,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
@@ -166,10 +172,12 @@ class MessageActionsModal extends StatelessWidget {
|
||||
_buildReplyButton(context),
|
||||
if (showThreadReply &&
|
||||
(message.status ==
|
||||
MessageSendingStatus.SENT ||
|
||||
MessageSendingStatus.SENT ||
|
||||
message.status == null) &&
|
||||
message.parentId == null)
|
||||
_buildThreadReplyButton(context),
|
||||
if (showResendMessage)
|
||||
_buildResendMessage(context),
|
||||
if (showEditMessage)
|
||||
_buildEditMessage(context),
|
||||
if (showDeleteMessage)
|
||||
@@ -212,9 +220,10 @@ class MessageActionsModal extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _buildDeleteButton(BuildContext context) {
|
||||
final isDeleteFailed = message.status == MessageSendingStatus.FAILED_DELETE;
|
||||
return ListTile(
|
||||
title: Text(
|
||||
'Delete message',
|
||||
isDeleteFailed ? 'Retry deleting message' : 'Delete message',
|
||||
style:
|
||||
Theme.of(context).textTheme.headline6.copyWith(color: Colors.red),
|
||||
),
|
||||
@@ -263,6 +272,29 @@ class MessageActionsModal extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildResendMessage(BuildContext context) {
|
||||
final isUpdateFailed = message.status == MessageSendingStatus.FAILED_UPDATE;
|
||||
return ListTile(
|
||||
title: Text(
|
||||
isUpdateFailed ? 'Resend edited message' : 'Resend',
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
leading: StreamSvgIcon.circle_up(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
final client = StreamChat.of(context).client;
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
if (isUpdateFailed) {
|
||||
client.updateMessage(message, channel.cid);
|
||||
} else {
|
||||
channel.sendMessage(message);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showEditBottomSheet(BuildContext context) {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
showModalBottomSheet(
|
||||
@@ -270,6 +302,7 @@ class MessageActionsModal extends StatelessWidget {
|
||||
elevation: 2,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(32),
|
||||
@@ -330,9 +363,10 @@ class MessageActionsModal extends StatelessWidget {
|
||||
? editMessageInputBuilder(context, message)
|
||||
: MessageInput(
|
||||
editMessage: message,
|
||||
onMessageSent: (_) {
|
||||
preMessageSending: (m) {
|
||||
FocusScope.of(context).unfocus();
|
||||
Navigator.pop(context);
|
||||
return m;
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
+97
-69
@@ -305,7 +305,10 @@ class MessageInputState extends State<MessageInput> {
|
||||
border: _sendAsDm
|
||||
? null
|
||||
: Border.all(
|
||||
color: Colors.black.withOpacity(.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
@@ -314,8 +317,8 @@ class MessageInputState extends State<MessageInput> {
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
color: _sendAsDm
|
||||
? StreamChatTheme.of(context).accentColor
|
||||
: Colors.white,
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
@@ -330,7 +333,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
: CrossFadeState.showSecond,
|
||||
firstChild: StreamSvgIcon.check(
|
||||
size: 16.0,
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
secondChild: SizedBox(
|
||||
height: 16,
|
||||
@@ -376,7 +379,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
});
|
||||
},
|
||||
icon: StreamSvgIcon.emptyCircleLeft(
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
@@ -409,7 +412,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
border: Border.all(
|
||||
color: Colors.black.withOpacity(0.16),
|
||||
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
),
|
||||
),
|
||||
padding: _attachments.isEmpty ? null : EdgeInsets.all(6.0),
|
||||
@@ -458,8 +461,9 @@ class MessageInputState extends State<MessageInput> {
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Chip(
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).accentColor,
|
||||
backgroundColor: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
padding: EdgeInsets.zero,
|
||||
labelPadding:
|
||||
EdgeInsets.symmetric(horizontal: 8.0),
|
||||
@@ -468,13 +472,20 @@ class MessageInputState extends State<MessageInput> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
StreamSvgIcon.lightning(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
size: 16.0,
|
||||
),
|
||||
Text(
|
||||
_chosenCommand?.name?.toUpperCase() ?? '',
|
||||
style: TextStyle(
|
||||
color: Colors.white, fontSize: 12.0),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -643,12 +654,12 @@ class MessageInputState extends State<MessageInput> {
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
color: StreamChatTheme.of(context).primaryColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Container(
|
||||
constraints: BoxConstraints.loose(Size.fromHeight(400)),
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).primaryColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(8.0)),
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(0),
|
||||
@@ -666,13 +677,17 @@ class MessageInputState extends State<MessageInput> {
|
||||
),
|
||||
child: StreamSvgIcon.lightning(
|
||||
color: StreamChatTheme.of(context)
|
||||
.accentColor,
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Instant Commands',
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
)
|
||||
],
|
||||
@@ -699,15 +714,6 @@ class MessageInputState extends State<MessageInput> {
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: CircleAvatar(
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).accentColor,
|
||||
child: StreamSvgIcon.lightning(
|
||||
color: Colors.white,
|
||||
size: 12.5,
|
||||
),
|
||||
maxRadius: 12,
|
||||
),
|
||||
//subtitle: Text(c.description),
|
||||
onTap: () {
|
||||
_setCommand(c);
|
||||
@@ -734,27 +740,36 @@ class MessageInputState extends State<MessageInput> {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return _attachments.isEmpty
|
||||
? StreamChatTheme.of(context).accentColor
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: (!_attachmentContainsFile
|
||||
? StreamChatTheme.of(context).accentColor
|
||||
: Colors.black.withOpacity(0.2));
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.2));
|
||||
break;
|
||||
case 1:
|
||||
return _attachmentContainsFile
|
||||
? StreamChatTheme.of(context).accentColor
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: (_attachments.isEmpty
|
||||
? Colors.black.withOpacity(0.5)
|
||||
: Colors.black.withOpacity(0.2));
|
||||
? StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.2));
|
||||
break;
|
||||
case 2:
|
||||
return _attachmentContainsFile && _attachments.isNotEmpty
|
||||
? Colors.black.withOpacity(0.2)
|
||||
: Colors.black.withOpacity(0.5);
|
||||
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
|
||||
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
|
||||
break;
|
||||
case 3:
|
||||
return _attachmentContainsFile && _attachments.isNotEmpty
|
||||
? Colors.black.withOpacity(0.2)
|
||||
: Colors.black.withOpacity(0.5);
|
||||
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
|
||||
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -763,7 +778,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
duration: _animateContainer ? Duration(milliseconds: 300) : Duration.zero,
|
||||
height: _openFilePickerSection ? _filePickerSize : 0,
|
||||
child: Material(
|
||||
color: Color(0xFFF2F2F2),
|
||||
color: StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
@@ -830,7 +845,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16.0),
|
||||
topRight: Radius.circular(16.0),
|
||||
@@ -845,7 +860,8 @@ class MessageInputState extends State<MessageInput> {
|
||||
width: 40.0,
|
||||
height: 4.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF2F2F2),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
),
|
||||
@@ -858,7 +874,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
child: _buildPickerSection(),
|
||||
@@ -893,11 +909,12 @@ class MessageInputState extends State<MessageInput> {
|
||||
},
|
||||
child: Container(
|
||||
constraints: BoxConstraints.expand(),
|
||||
color: Color(0xfff2f2f2),
|
||||
color: StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
child: Text(
|
||||
'Add more files',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
@@ -926,7 +943,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
PhotoManager.openSetting();
|
||||
},
|
||||
child: Container(
|
||||
color: Color(0xFFF2F2F2),
|
||||
color: StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -935,16 +952,20 @@ class MessageInputState extends State<MessageInput> {
|
||||
'svgs/icon_picture_empty_state.svg',
|
||||
package: 'stream_chat_flutter',
|
||||
height: 140,
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
Center(
|
||||
child: Text(
|
||||
'Allow access to your gallery',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1060,7 +1081,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
CircleAvatar _buildGiphyIcon() {
|
||||
if (kIsWeb) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: Colors.black,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.black,
|
||||
child: Image.asset(
|
||||
'images/giphy_icon.png',
|
||||
package: 'stream_chat_flutter',
|
||||
@@ -1121,7 +1142,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
child: Card(
|
||||
margin: EdgeInsets.all(8.0),
|
||||
elevation: 2.0,
|
||||
color: StreamChatTheme.of(context).primaryColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
@@ -1129,7 +1150,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
child: Container(
|
||||
constraints: BoxConstraints.loose(Size.fromHeight(400)),
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).primaryColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
child: FutureBuilder<List<Member>>(
|
||||
future: queryMembers ?? Future.value(members),
|
||||
@@ -1157,7 +1178,8 @@ class MessageInputState extends State<MessageInput> {
|
||||
subtitle: Text('@${m.userId}'),
|
||||
trailing: StreamSvgIcon.mentions(
|
||||
color: StreamChatTheme.of(context)
|
||||
.accentColor,
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
),
|
||||
onTap: () {
|
||||
_mentionedUsers.add(m.user);
|
||||
@@ -1222,7 +1244,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
child: Card(
|
||||
margin: EdgeInsets.all(8.0),
|
||||
elevation: 2.0,
|
||||
color: StreamChatTheme.of(context).primaryColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
@@ -1237,7 +1259,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
offset: Offset(0, -4),
|
||||
),
|
||||
],
|
||||
color: StreamChatTheme.of(context).primaryColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(0),
|
||||
@@ -1253,14 +1275,19 @@ class MessageInputState extends State<MessageInput> {
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: StreamSvgIcon.smile(
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Emoji matching "$query"',
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -1373,10 +1400,15 @@ class MessageInputState extends State<MessageInput> {
|
||||
child: InkWell(
|
||||
child: CircleAvatar(
|
||||
backgroundColor:
|
||||
Colors.black.withOpacity(0.2),
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.6),
|
||||
maxRadius: 12.0,
|
||||
child: StreamSvgIcon.close(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
@@ -1461,11 +1493,11 @@ class MessageInputState extends State<MessageInput> {
|
||||
_attachments.remove(attachment);
|
||||
});
|
||||
},
|
||||
fillColor: Colors.black.withOpacity(.5),
|
||||
fillColor: StreamChatTheme.of(context).colorTheme.black.withOpacity(.5),
|
||||
child: Center(
|
||||
child: StreamSvgIcon.close(
|
||||
size: 24,
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1553,8 +1585,8 @@ class MessageInputState extends State<MessageInput> {
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.lightning(
|
||||
color: _commandsOverlay != null
|
||||
? StreamChatTheme.of(context).accentColor
|
||||
: Color(0xFF000000).withAlpha(128),
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
@@ -1594,8 +1626,8 @@ class MessageInputState extends State<MessageInput> {
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.attach(
|
||||
color: _openFilePickerSection
|
||||
? StreamChatTheme.of(context).accentColor
|
||||
: Color(0xFF000000).withAlpha(128),
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
@@ -1924,7 +1956,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
|
||||
child: StreamSvgIcon(
|
||||
assetName: _getIdleSendIcon(),
|
||||
color: Colors.grey,
|
||||
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1943,7 +1975,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
),
|
||||
icon: StreamSvgIcon(
|
||||
assetName: _getSendIcon(),
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -2043,10 +2075,6 @@ class MessageInputState extends State<MessageInput> {
|
||||
return sendingFuture.then((resp) {
|
||||
if (widget.onMessageSent != null) {
|
||||
widget.onMessageSent(resp.message);
|
||||
} else {
|
||||
if (widget.editMessage != null) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -259,16 +259,19 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
}
|
||||
|
||||
final messageList = snapshot.data?.reversed?.toList() ?? [];
|
||||
|
||||
if (messageList.isEmpty) {
|
||||
if (_upToDate) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'No chats here yet...',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black.withOpacity(.5),
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -355,8 +358,12 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color(0XFFF7F7F7),
|
||||
Color(0XFFFCFCFC),
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.whiteSmoke,
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.whiteSnow,
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -536,9 +543,9 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
FloatingActionButton(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: StreamSvgIcon.down(
|
||||
color: Colors.black,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () {
|
||||
if (unreadCount > 0) {
|
||||
@@ -598,7 +605,10 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return Container(
|
||||
color: Color(0xffd0021B).withAlpha(26),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentRed
|
||||
.withOpacity(.2),
|
||||
child: Center(
|
||||
child: Text('Error loading messages'),
|
||||
),
|
||||
@@ -867,7 +877,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
}
|
||||
if (event.message.user.id == streamChannel.channel.client.state.user.id) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollController.jumpTo(
|
||||
_scrollController?.jumpTo(
|
||||
index: 0,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -60,7 +60,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
sigmaY: 10.8731,
|
||||
),
|
||||
child: Container(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -110,14 +110,16 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
translateUserAvatar: false,
|
||||
showSendingIndicator: DisplayWidget.gone,
|
||||
shape: messageShape,
|
||||
showReactionPickerIndicator: true,
|
||||
showInChannelIndicator: false,
|
||||
showReactionPickerIndicator: showReactions &&
|
||||
(message.status ==
|
||||
MessageSendingStatus.SENT ||
|
||||
message.status == null),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
if (message.latestReactions?.isNotEmpty == true)
|
||||
_buildReactionCard(context),
|
||||
],
|
||||
@@ -137,6 +139,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
horizontal: 8.0,
|
||||
),
|
||||
child: Card(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
|
||||
@@ -126,8 +126,6 @@ class MessageSearchItem extends StatelessWidget {
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
|
||||
color:
|
||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle.color,
|
||||
fontStyle: (message.isSystem || message.isDeleted)
|
||||
? FontStyle.italic
|
||||
: FontStyle.normal,
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter/src/message_search_item.dart';
|
||||
|
||||
import '../stream_chat_flutter.dart';
|
||||
import 'lazy_load_scroll_view.dart';
|
||||
import 'message_search_bloc.dart';
|
||||
|
||||
@@ -127,8 +128,8 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withOpacity(0.1)
|
||||
: Colors.black.withOpacity(0.1),
|
||||
? StreamChatTheme.of(context).colorTheme.white.withOpacity(0.1)
|
||||
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -151,7 +152,10 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return Container(
|
||||
color: Color(0xffd0021B).withAlpha(26),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentRed
|
||||
.withOpacity(.2),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Center(
|
||||
@@ -313,8 +317,14 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
Colors.black.withOpacity(0.02),
|
||||
Colors.white.withOpacity(0.05),
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.02),
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white
|
||||
.withOpacity(0.05),
|
||||
],
|
||||
stops: [0, 1],
|
||||
),
|
||||
@@ -327,7 +337,10 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
||||
child: Text(
|
||||
'${items.length} results',
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
+193
-223
@@ -120,6 +120,9 @@ class MessageWidget extends StatefulWidget {
|
||||
/// Used in [MessageReactionsModal] and [MessageActionsModal]
|
||||
final bool showReactionPickerIndicator;
|
||||
|
||||
/// If true the widget will show the resendMessage indicator
|
||||
final bool showResendMessage;
|
||||
|
||||
final List<Read> readList;
|
||||
|
||||
/// If true show the users username next to the timestamp of the message
|
||||
@@ -165,6 +168,7 @@ class MessageWidget extends StatefulWidget {
|
||||
this.editMessageInputBuilder,
|
||||
this.textBuilder,
|
||||
Map<String, AttachmentBuilder> customAttachmentBuilders,
|
||||
this.showResendMessage = true,
|
||||
this.readList,
|
||||
this.padding,
|
||||
this.textPadding = const EdgeInsets.symmetric(
|
||||
@@ -232,13 +236,29 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
bool get showTimeStamp =>
|
||||
widget.message.createdAt != null && widget.showTimestamp;
|
||||
|
||||
bool get showReadList => widget.readList?.isNotEmpty == true;
|
||||
bool get isMessageRead => widget.readList?.isNotEmpty == true;
|
||||
|
||||
bool get showInChannel =>
|
||||
widget.showInChannelIndicator && widget.message?.showInChannel == true;
|
||||
|
||||
bool get _hasReplyToMessage => widget.message?.replyToMessage != null;
|
||||
|
||||
bool get isSendFailed => widget.message.status == MessageSendingStatus.FAILED;
|
||||
|
||||
bool get isUpdateFailed =>
|
||||
widget.message.status == MessageSendingStatus.FAILED_UPDATE;
|
||||
|
||||
bool get isDeleteFailed =>
|
||||
widget.message.status == MessageSendingStatus.FAILED_DELETE;
|
||||
|
||||
bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed;
|
||||
|
||||
bool get showBottomRow =>
|
||||
showThreadReplyIndicator ||
|
||||
showUsername ||
|
||||
showTimeStamp ||
|
||||
showInChannel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var leftPadding = widget.showUserAvatar != DisplayWidget.gone
|
||||
@@ -273,6 +293,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: AlignmentDirectional.bottomStart,
|
||||
children: [
|
||||
Column(
|
||||
@@ -317,97 +338,90 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
: 0,
|
||||
)
|
||||
: EdgeInsets.zero,
|
||||
child:
|
||||
(widget.message.isDeleted &&
|
||||
widget.message.status !=
|
||||
MessageSendingStatus
|
||||
.FAILED_DELETE)
|
||||
? Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.rotationY(
|
||||
widget.reverse ? pi : 0),
|
||||
child: DeletedMessage(
|
||||
reverse: widget.reverse,
|
||||
borderRadiusGeometry: widget
|
||||
.borderRadiusGeometry,
|
||||
borderSide:
|
||||
widget.borderSide,
|
||||
shape: widget.shape,
|
||||
messageTheme:
|
||||
widget.messageTheme,
|
||||
),
|
||||
)
|
||||
: GestureDetector(
|
||||
onTap: () =>
|
||||
retryMessage(context),
|
||||
onLongPress: () =>
|
||||
onLongPress(context),
|
||||
child: Material(
|
||||
clipBehavior:
|
||||
Clip.antiAlias,
|
||||
shape: widget.shape ??
|
||||
RoundedRectangleBorder(
|
||||
side: isOnlyEmoji &&
|
||||
!_hasReplyToMessage
|
||||
? BorderSide.none
|
||||
: widget.borderSide ??
|
||||
BorderSide(
|
||||
color: Theme.of(context).brightness ==
|
||||
Brightness
|
||||
.dark
|
||||
? Colors
|
||||
.white
|
||||
.withAlpha(
|
||||
24)
|
||||
: Colors
|
||||
.black
|
||||
.withAlpha(24),
|
||||
),
|
||||
borderRadius: widget
|
||||
.borderRadiusGeometry ??
|
||||
BorderRadius.zero,
|
||||
),
|
||||
color:
|
||||
_getBackgroundColor(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(
|
||||
hasFiles ? 2.0 : 0.0),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment
|
||||
.start,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (_hasReplyToMessage)
|
||||
ReplyToMessageWidget(
|
||||
message: widget
|
||||
.message
|
||||
.replyToMessage,
|
||||
messageTheme: isMyMessage
|
||||
child: (widget.message.isDeleted &&
|
||||
!isFailedState)
|
||||
? Transform(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.rotationY(
|
||||
widget.reverse ? pi : 0),
|
||||
child: DeletedMessage(
|
||||
reverse: widget.reverse,
|
||||
borderRadiusGeometry:
|
||||
widget.borderRadiusGeometry,
|
||||
borderSide: widget.borderSide,
|
||||
shape: widget.shape,
|
||||
messageTheme:
|
||||
widget.messageTheme,
|
||||
),
|
||||
)
|
||||
: GestureDetector(
|
||||
onLongPress: () =>
|
||||
onLongPress(context),
|
||||
child: Material(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: widget.shape ??
|
||||
RoundedRectangleBorder(
|
||||
side: isOnlyEmoji
|
||||
? BorderSide.none
|
||||
: widget.borderSide ??
|
||||
BorderSide(
|
||||
color: Theme.of(context)
|
||||
.brightness ==
|
||||
Brightness
|
||||
.dark
|
||||
? StreamChatTheme.of(
|
||||
context)
|
||||
.otherMessageTheme
|
||||
.colorTheme
|
||||
.white
|
||||
.withAlpha(
|
||||
24)
|
||||
: StreamChatTheme.of(
|
||||
context)
|
||||
.ownMessageTheme,
|
||||
reverse: widget
|
||||
.reverse,
|
||||
.colorTheme
|
||||
.black
|
||||
.withAlpha(
|
||||
24),
|
||||
),
|
||||
..._parseAttachments(
|
||||
context),
|
||||
if (widget
|
||||
.message.text
|
||||
.trim()
|
||||
.isNotEmpty &&
|
||||
!isGiphy)
|
||||
_buildTextBubble(
|
||||
context),
|
||||
],
|
||||
),
|
||||
),
|
||||
borderRadius: widget
|
||||
.borderRadiusGeometry ??
|
||||
BorderRadius.zero,
|
||||
),
|
||||
color: _getBackgroundColor(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(
|
||||
hasFiles ? 2.0 : 0.0),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (_hasReplyToMessage)
|
||||
ReplyToMessageWidget(
|
||||
message: widget
|
||||
.message
|
||||
.replyToMessage,
|
||||
messageTheme: isMyMessage
|
||||
? StreamChatTheme.of(
|
||||
context)
|
||||
.otherMessageTheme
|
||||
: StreamChatTheme.of(
|
||||
context)
|
||||
.ownMessageTheme,
|
||||
reverse: widget
|
||||
.reverse,
|
||||
),
|
||||
..._parseAttachments(
|
||||
context),
|
||||
if (widget.message.text
|
||||
.trim()
|
||||
.isNotEmpty &&
|
||||
!isGiphy)
|
||||
_buildTextBubble(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),),
|
||||
),
|
||||
if (widget.showReactionPickerIndicator)
|
||||
Positioned(
|
||||
@@ -432,18 +446,23 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (showThreadReplyIndicator ||
|
||||
showUsername ||
|
||||
showTimeStamp ||
|
||||
showInChannel)
|
||||
SizedBox(height: 20.0),
|
||||
if (showBottomRow) SizedBox(height: 20.0),
|
||||
],
|
||||
),
|
||||
if (showThreadReplyIndicator ||
|
||||
showUsername ||
|
||||
showTimeStamp ||
|
||||
showInChannel)
|
||||
_buildBottomRow(leftPadding)
|
||||
if (showBottomRow) _buildBottomRow(leftPadding),
|
||||
if (isFailedState)
|
||||
Positioned(
|
||||
left: widget.reverse ? -3 : null,
|
||||
right: widget.reverse ? null : -9,
|
||||
bottom: showBottomRow ? 20 : 0,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: StreamSvgIcon.error(size: 20),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -463,16 +482,18 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
StreamSvgIcon.eye(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
|
||||
size: 16.0,
|
||||
),
|
||||
SizedBox(width: 8.0),
|
||||
Text(
|
||||
'Only visible to you',
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
fontSize: 12.0,
|
||||
),
|
||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -510,14 +531,14 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
|
||||
children.addAll([
|
||||
if (showSendingIndicator) _buildSendingIndicator(),
|
||||
if (showReadList)
|
||||
SizedBox.fromSize(
|
||||
size: Size((widget.readList.length * 10.0) + 10, 17),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
child: _buildReadIndicator(),
|
||||
),
|
||||
),
|
||||
// if (showReadList)
|
||||
// SizedBox.fromSize(
|
||||
// size: Size((widget.readList.length * 10.0) + 10, 17),
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.only(left: 4.0),
|
||||
// child: _buildReadIndicator(),
|
||||
// ),
|
||||
// ),
|
||||
if (showThreadReplyIndicator)
|
||||
InkWell(
|
||||
onTap: widget.onThreadTap != null ? onThreadTap : null,
|
||||
@@ -551,6 +572,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
child: CustomPaint(
|
||||
size: const Size(16, 32),
|
||||
painter: _ThreadReplyPainter(
|
||||
context: context,
|
||||
color: widget.messageTheme.replyThreadColor,
|
||||
),
|
||||
),
|
||||
@@ -599,7 +621,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
child: Material(
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: CircleBorder(),
|
||||
child: Padding(
|
||||
@@ -669,15 +691,20 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
editMessageInputBuilder: widget.editMessageInputBuilder,
|
||||
onReplyTap: widget.onReplyTap,
|
||||
onThreadReplyTap: widget.onThreadTap,
|
||||
showCopyMessage: widget.message.text?.trim()?.isNotEmpty == true,
|
||||
showEditMessage: widget.showEditMessage &&
|
||||
showResendMessage:
|
||||
widget.showResendMessage && (isSendFailed || isUpdateFailed),
|
||||
showCopyMessage: !isFailedState &&
|
||||
widget.message.text?.trim()?.isNotEmpty == true, showEditMessage: widget.showEditMessage &&
|
||||
!isDeleteFailed &&
|
||||
widget.message.attachments
|
||||
?.any((element) => element.type == 'giphy') !=
|
||||
true,
|
||||
showReactions: widget.showReactions,
|
||||
showReply: widget.showReplyIndicator,
|
||||
showReply: widget.showReplyIndicator &&
|
||||
!isFailedState &&
|
||||
widget.onReplyTap != null,
|
||||
showThreadReply:
|
||||
widget.showThreadReplyIndicator && widget.onThreadTap != null,
|
||||
widget.showThreadReplyIndicator && widget.onThreadTap != null,
|
||||
),
|
||||
);
|
||||
});
|
||||
@@ -714,8 +741,8 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
widget.borderSide ??
|
||||
BorderSide(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withAlpha(24)
|
||||
: Colors.black.withAlpha(24),
|
||||
? StreamChatTheme.of(context).colorTheme.white.withAlpha(24)
|
||||
: StreamChatTheme.of(context).colorTheme.black.withAlpha(24),
|
||||
),
|
||||
borderRadius: widget.attachmentBorderRadiusGeometry ??
|
||||
widget.borderRadiusGeometry ??
|
||||
@@ -780,32 +807,22 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
final attachmentShape =
|
||||
widget.attachmentShape ?? widget.shape ?? _getDefaultShape(context);
|
||||
return Material(
|
||||
color: _getBackgroundColor(),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: attachmentShape,
|
||||
child: Padding(
|
||||
padding: widget.attachmentPadding,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: attachmentShape,
|
||||
type: MaterialType.transparency,
|
||||
child: Transform(
|
||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
getFailedMessageWidget(
|
||||
context,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
),
|
||||
attachmentWidget,
|
||||
],
|
||||
color: _getBackgroundColor(),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: attachmentShape,
|
||||
child: Padding(
|
||||
padding: widget.attachmentPadding,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: attachmentShape,
|
||||
type: MaterialType.transparency,
|
||||
child: Transform(
|
||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
||||
alignment: Alignment.center,
|
||||
child: attachmentWidget,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -824,14 +841,27 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
}
|
||||
|
||||
Widget _buildSendingIndicator() {
|
||||
return Container(
|
||||
height: widget.messageTheme.createdAt.fontSize + 2,
|
||||
width: widget.messageTheme.createdAt.fontSize + 2,
|
||||
child: SendingIndicator(
|
||||
message: widget.message,
|
||||
allRead: widget.allRead,
|
||||
),
|
||||
final style = widget.messageTheme.createdAt;
|
||||
Widget child = SendingIndicator(
|
||||
message: widget.message,
|
||||
isMessageRead: isMessageRead,
|
||||
size: style.fontSize,
|
||||
);
|
||||
if (isMessageRead) {
|
||||
child = Row(
|
||||
children: [
|
||||
Text(
|
||||
widget.readList.length.toString(),
|
||||
style: style.copyWith(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 2),
|
||||
child,
|
||||
],
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
Widget _buildUserAvatar() => Transform(
|
||||
@@ -856,55 +886,6 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
),
|
||||
);
|
||||
|
||||
Widget getFailedMessageWidget(
|
||||
BuildContext context, {
|
||||
EdgeInsetsGeometry padding,
|
||||
}) {
|
||||
Widget failedWidget;
|
||||
if (widget.message.status == MessageSendingStatus.FAILED) {
|
||||
failedWidget = Text(
|
||||
'MESSAGE FAILED · CLICK TO TRY AGAIN',
|
||||
style: widget.messageTheme.messageText.copyWith(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withOpacity(.5)
|
||||
: Colors.black.withOpacity(.5),
|
||||
fontSize: 11,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.message.status == MessageSendingStatus.FAILED_UPDATE) {
|
||||
failedWidget = Text(
|
||||
'MESSAGE UPDATE FAILED · CLICK TO TRY AGAIN',
|
||||
style: widget.messageTheme.messageText.copyWith(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withOpacity(.5)
|
||||
: Colors.black.withOpacity(.5),
|
||||
fontSize: 11,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.message.status == MessageSendingStatus.FAILED_DELETE) {
|
||||
failedWidget = Text(
|
||||
'MESSAGE DELETE FAILED · CLICK TO TRY AGAIN',
|
||||
style: widget.messageTheme.messageText.copyWith(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withOpacity(.5)
|
||||
: Colors.black.withOpacity(.5),
|
||||
fontSize: 11,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (failedWidget != null) {
|
||||
return Padding(
|
||||
padding: padding ?? EdgeInsets.zero,
|
||||
child: failedWidget,
|
||||
);
|
||||
}
|
||||
|
||||
return SizedBox();
|
||||
}
|
||||
|
||||
Widget _buildTextBubble(BuildContext context) {
|
||||
return Transform(
|
||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
||||
@@ -914,26 +895,20 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
children: [
|
||||
Padding(
|
||||
padding: widget.textPadding,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
getFailedMessageWidget(context),
|
||||
widget.textBuilder != null
|
||||
? widget.textBuilder(context, widget.message)
|
||||
: MessageText(
|
||||
onLinkTap: widget.onLinkTap,
|
||||
message: widget.message,
|
||||
onMentionTap: widget.onMentionTap,
|
||||
messageTheme: isOnlyEmoji
|
||||
? widget.messageTheme.copyWith(
|
||||
messageText:
|
||||
widget.messageTheme.messageText.copyWith(
|
||||
fontSize: 40,
|
||||
))
|
||||
: widget.messageTheme,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: widget.textBuilder != null
|
||||
? widget.textBuilder(context, widget.message)
|
||||
: MessageText(
|
||||
onLinkTap: widget.onLinkTap,
|
||||
message: widget.message,
|
||||
onMentionTap: widget.onMentionTap,
|
||||
messageTheme: isOnlyEmoji
|
||||
? widget.messageTheme.copyWith(
|
||||
messageText:
|
||||
widget.messageTheme.messageText.copyWith(
|
||||
fontSize: 40,
|
||||
))
|
||||
: widget.messageTheme,
|
||||
),
|
||||
),
|
||||
if (widget.message.attachments
|
||||
?.any((element) => element.ogScrapeUrl != null) ==
|
||||
@@ -954,16 +929,10 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
return widget.messageTheme.messageBackgroundColor;
|
||||
}
|
||||
|
||||
if ((widget.message.status == MessageSendingStatus.FAILED ||
|
||||
widget.message.status == MessageSendingStatus.FAILED_UPDATE ||
|
||||
widget.message.status == MessageSendingStatus.FAILED_DELETE)) {
|
||||
return Color(0xffd0021B).withOpacity(.1);
|
||||
}
|
||||
|
||||
if (widget.message.attachments
|
||||
?.any((element) => element.ogScrapeUrl != null) ==
|
||||
true) {
|
||||
return Color(0xFFE9F2FF);
|
||||
return StreamChatTheme.of(context).colorTheme.blueAlice;
|
||||
}
|
||||
|
||||
if (isOnlyEmoji) {
|
||||
@@ -1003,13 +972,14 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
|
||||
class _ThreadReplyPainter extends CustomPainter {
|
||||
final Color color;
|
||||
final BuildContext context;
|
||||
|
||||
const _ThreadReplyPainter({@required this.color});
|
||||
const _ThreadReplyPainter({this.context, @required this.color});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = color ?? Color(0XFFDBDBDB)
|
||||
..color = color ?? StreamChatTheme.of(context).colorTheme.greyGainsboro
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 1
|
||||
..strokeCap = StrokeCap.round;
|
||||
|
||||
@@ -107,16 +107,22 @@ class ReactionBubble extends StatelessWidget {
|
||||
height: 16,
|
||||
color: (!highlightOwnReactions ||
|
||||
reaction.user.id == StreamChat.of(context).user.id)
|
||||
? StreamChatTheme.of(context).accentColor
|
||||
: Colors.black.withOpacity(.5),
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
)
|
||||
: Icon(
|
||||
Icons.help_outline_rounded,
|
||||
size: 16,
|
||||
color: (!highlightOwnReactions ||
|
||||
reaction.user.id == StreamChat.of(context).user.id)
|
||||
? StreamChatTheme.of(context).accentColor
|
||||
: Colors.black.withOpacity(.5),
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -89,7 +89,9 @@ class _ReactionPickerState extends State<ReactionPicker>
|
||||
height: animations[index].value * 24.0,
|
||||
width: animations[index].value * 24.0,
|
||||
color: ownReactionIndex != -1
|
||||
? StreamChatTheme.of(context).accentColor
|
||||
? StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue
|
||||
: Theme.of(context)
|
||||
.iconTheme
|
||||
.color
|
||||
|
||||
@@ -4,27 +4,29 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
/// Used to show the sending status of the message
|
||||
class SendingIndicator extends StatelessWidget {
|
||||
final Message message;
|
||||
final bool allRead;
|
||||
final bool isMessageRead;
|
||||
final double size;
|
||||
|
||||
const SendingIndicator({
|
||||
Key key,
|
||||
this.message,
|
||||
this.allRead = false,
|
||||
this.isMessageRead = false,
|
||||
this.size = 12,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (allRead) {
|
||||
if (isMessageRead) {
|
||||
return Icon(
|
||||
Icons.done_all,
|
||||
size: 8,
|
||||
color: StreamChatTheme.of(context).accentColor,
|
||||
Icons.done_all_rounded,
|
||||
size: size,
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
);
|
||||
}
|
||||
if (message.status == MessageSendingStatus.SENT || message.status == null) {
|
||||
return Icon(
|
||||
Icons.done,
|
||||
size: 8,
|
||||
size: size,
|
||||
color: IconTheme.of(context).color.withOpacity(0.5),
|
||||
);
|
||||
}
|
||||
@@ -32,18 +34,9 @@ class SendingIndicator extends StatelessWidget {
|
||||
message.status == MessageSendingStatus.UPDATING) {
|
||||
return Icon(
|
||||
Icons.access_time,
|
||||
size: 8,
|
||||
size: size,
|
||||
);
|
||||
}
|
||||
if (message.status == MessageSendingStatus.FAILED ||
|
||||
message.status == MessageSendingStatus.FAILED_UPDATE ||
|
||||
message.status == MessageSendingStatus.FAILED_DELETE) {
|
||||
return Icon(
|
||||
Icons.error_outline,
|
||||
size: 8,
|
||||
);
|
||||
}
|
||||
|
||||
return SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
||||
return Theme(
|
||||
data: materialTheme.copyWith(
|
||||
primaryIconTheme: streamTheme.primaryIconTheme,
|
||||
accentColor: streamTheme.accentColor,
|
||||
scaffoldBackgroundColor: streamTheme.backgroundColor,
|
||||
accentColor: streamTheme.colorTheme.accentBlue,
|
||||
scaffoldBackgroundColor: streamTheme.colorTheme.white,
|
||||
),
|
||||
child: widget.child,
|
||||
);
|
||||
@@ -92,11 +92,9 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
||||
) {
|
||||
final defaultTheme = StreamChatThemeData.getDefaultTheme(Theme.of(context));
|
||||
final theme = defaultTheme.copyWith(
|
||||
primaryColor: themeData?.primaryColor,
|
||||
defaultChannelImage: themeData?.defaultChannelImage,
|
||||
primaryIconTheme: themeData?.primaryIconTheme,
|
||||
defaultUserImage: themeData?.defaultUserImage,
|
||||
backgroundColor: themeData?.backgroundColor,
|
||||
channelTheme: defaultTheme.channelTheme.copyWith(
|
||||
channelHeaderTheme:
|
||||
defaultTheme.channelTheme.channelHeaderTheme.copyWith(
|
||||
@@ -151,8 +149,6 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
||||
reactionsBackgroundColor:
|
||||
themeData?.otherMessageTheme?.reactionsBackgroundColor,
|
||||
),
|
||||
accentColor: themeData?.accentColor,
|
||||
secondaryColor: themeData?.secondaryColor,
|
||||
channelPreviewTheme: defaultTheme.channelPreviewTheme.copyWith(
|
||||
avatarTheme: defaultTheme.channelPreviewTheme.avatarTheme.copyWith(
|
||||
constraints: themeData?.channelPreviewTheme?.avatarTheme?.constraints,
|
||||
|
||||
+285
-81
@@ -42,17 +42,11 @@ class StreamChatTheme extends InheritedWidget {
|
||||
|
||||
/// Theme data
|
||||
class StreamChatThemeData {
|
||||
/// Primary color of the chat widgets
|
||||
final Color primaryColor;
|
||||
/// The text themes used in the widgets
|
||||
final TextTheme textTheme;
|
||||
|
||||
/// Secondary color of the chat widgets
|
||||
final Color secondaryColor;
|
||||
|
||||
/// Accent color of the chat widgets
|
||||
final Color accentColor;
|
||||
|
||||
/// Background color of the chat widgets
|
||||
final Color backgroundColor;
|
||||
/// The text themes used in the widgets
|
||||
final ColorTheme colorTheme;
|
||||
|
||||
/// Theme of the [ChannelPreview]
|
||||
final ChannelPreviewTheme channelPreviewTheme;
|
||||
@@ -80,10 +74,8 @@ class StreamChatThemeData {
|
||||
|
||||
/// Create a theme from scratch
|
||||
StreamChatThemeData({
|
||||
this.primaryColor,
|
||||
this.secondaryColor,
|
||||
this.accentColor,
|
||||
this.backgroundColor,
|
||||
this.textTheme,
|
||||
this.colorTheme,
|
||||
this.channelPreviewTheme,
|
||||
this.channelTheme,
|
||||
this.otherMessageTheme,
|
||||
@@ -99,11 +91,7 @@ class StreamChatThemeData {
|
||||
final defaultTheme = getDefaultTheme(theme);
|
||||
|
||||
return defaultTheme.copyWith(
|
||||
accentColor: theme.accentColor,
|
||||
primaryIconTheme: theme.primaryIconTheme,
|
||||
primaryColor: theme.colorScheme.primary,
|
||||
secondaryColor: theme.colorScheme.secondary,
|
||||
backgroundColor: theme.scaffoldBackgroundColor,
|
||||
channelTheme: defaultTheme.channelTheme.copyWith(
|
||||
inputGradient: LinearGradient(colors: [
|
||||
theme.accentColor.withOpacity(.5),
|
||||
@@ -131,10 +119,8 @@ class StreamChatThemeData {
|
||||
|
||||
/// Creates a copy of [StreamChatThemeData] with specified attributes overridden.
|
||||
StreamChatThemeData copyWith({
|
||||
Color primaryColor,
|
||||
Color secondaryColor,
|
||||
Color accentColor,
|
||||
Color backgroundColor,
|
||||
TextTheme textTheme,
|
||||
ColorTheme colorTheme,
|
||||
ChannelPreviewTheme channelPreviewTheme,
|
||||
ChannelTheme channelTheme,
|
||||
MessageTheme ownMessageTheme,
|
||||
@@ -145,24 +131,35 @@ class StreamChatThemeData {
|
||||
List<ReactionIcon> reactionIcons,
|
||||
}) =>
|
||||
StreamChatThemeData(
|
||||
primaryColor: primaryColor ?? this.primaryColor,
|
||||
secondaryColor: secondaryColor ?? this.secondaryColor,
|
||||
textTheme: this.textTheme?.copyWith(
|
||||
title: textTheme?.title,
|
||||
body: textTheme?.body,
|
||||
bodyBold: textTheme?.bodyBold,
|
||||
captionBold: textTheme?.captionBold,
|
||||
footnote: textTheme?.footnote,
|
||||
footnoteBold: textTheme?.footnoteBold,
|
||||
headline: textTheme?.headline,
|
||||
headlineBold: textTheme?.headlineBold,
|
||||
),
|
||||
colorTheme: this.colorTheme?.copyWith(
|
||||
black: colorTheme?.black,
|
||||
grey: colorTheme?.grey,
|
||||
greyGainsboro: colorTheme?.greyGainsboro,
|
||||
greyWhisper: colorTheme?.greyWhisper,
|
||||
whiteSmoke: colorTheme?.whiteSmoke,
|
||||
whiteSnow: colorTheme?.whiteSnow,
|
||||
white: colorTheme?.white,
|
||||
blueAlice: colorTheme?.blueAlice,
|
||||
),
|
||||
primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme,
|
||||
accentColor: accentColor ?? this.accentColor,
|
||||
defaultChannelImage: defaultChannelImage ?? this.defaultChannelImage,
|
||||
defaultUserImage: defaultUserImage ?? this.defaultUserImage,
|
||||
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||
channelPreviewTheme: channelPreviewTheme?.copyWith(
|
||||
title:
|
||||
channelPreviewTheme.title ?? this.channelPreviewTheme.title,
|
||||
subtitle: channelPreviewTheme.subtitle ??
|
||||
this.channelPreviewTheme.subtitle,
|
||||
lastMessageAt: channelPreviewTheme.lastMessageAt ??
|
||||
this.channelPreviewTheme.lastMessageAt,
|
||||
avatarTheme: channelPreviewTheme.avatarTheme ??
|
||||
this.channelPreviewTheme.avatarTheme,
|
||||
) ??
|
||||
this.channelPreviewTheme,
|
||||
channelPreviewTheme: this.channelPreviewTheme?.copyWith(
|
||||
title: channelPreviewTheme.title,
|
||||
subtitle: channelPreviewTheme.subtitle,
|
||||
lastMessageAt: channelPreviewTheme.lastMessageAt,
|
||||
avatarTheme: channelPreviewTheme.avatarTheme,
|
||||
),
|
||||
channelTheme: channelTheme?.copyWith(
|
||||
channelHeaderTheme: channelTheme.channelHeaderTheme ??
|
||||
this.channelTheme.channelHeaderTheme,
|
||||
@@ -222,14 +219,13 @@ class StreamChatThemeData {
|
||||
static StreamChatThemeData getDefaultTheme(ThemeData theme) {
|
||||
final accentColor = Color(0xff006cff);
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
final textTheme = isDark ? TextTheme.dark() : TextTheme.light();
|
||||
final colorTheme = isDark ? ColorTheme.dark() : ColorTheme.light();
|
||||
return StreamChatThemeData(
|
||||
secondaryColor: Color(0xffEAEAEA),
|
||||
accentColor: accentColor,
|
||||
primaryColor: isDark ? Colors.black : Colors.white,
|
||||
primaryIconTheme: IconThemeData(
|
||||
color: isDark ? Colors.white : Colors.black.withOpacity(.5)),
|
||||
textTheme: textTheme,
|
||||
colorTheme: colorTheme,
|
||||
primaryIconTheme: IconThemeData(color: colorTheme.black.withOpacity(.5)),
|
||||
defaultChannelImage: (context, channel) => SizedBox(),
|
||||
backgroundColor: isDark ? Colors.black : Colors.white,
|
||||
defaultUserImage: (context, user) => Center(
|
||||
child: CachedNetworkImage(
|
||||
filterQuality: FilterQuality.high,
|
||||
@@ -238,7 +234,7 @@ class StreamChatThemeData {
|
||||
),
|
||||
),
|
||||
channelPreviewTheme: ChannelPreviewTheme(
|
||||
unreadCounterColor: Color(0xffff3742),
|
||||
unreadCounterColor: colorTheme.accentRed,
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
@@ -246,19 +242,12 @@ class StreamChatThemeData {
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
title: TextStyle(
|
||||
fontSize: 14,
|
||||
color: isDark ? Colors.white : Colors.black,
|
||||
fontWeight: FontWeight.bold),
|
||||
subtitle: TextStyle(
|
||||
fontSize: 12.5,
|
||||
color: (isDark ? Colors.white : Colors.black).withOpacity(0.5),
|
||||
title: textTheme.bodyBold,
|
||||
subtitle: textTheme.footnote.copyWith(
|
||||
color: Color(0xff7A7A7A),
|
||||
),
|
||||
lastMessageAt: TextStyle(
|
||||
fontSize: 11,
|
||||
color: isDark
|
||||
? Colors.white.withOpacity(.5)
|
||||
: Colors.black.withOpacity(.5),
|
||||
lastMessageAt: textTheme.footnote.copyWith(
|
||||
color: colorTheme.black.withOpacity(.5),
|
||||
),
|
||||
),
|
||||
channelTheme: ChannelTheme(
|
||||
@@ -273,20 +262,17 @@ class StreamChatThemeData {
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
color: isDark ? Colors.black : Colors.white,
|
||||
color: colorTheme.white,
|
||||
title: TextStyle(
|
||||
fontSize: 14,
|
||||
color: isDark ? Colors.white : Colors.black,
|
||||
color: colorTheme.black,
|
||||
),
|
||||
lastMessageAt: TextStyle(
|
||||
fontSize: 11,
|
||||
color: isDark
|
||||
? Colors.white.withOpacity(.5)
|
||||
: Colors.black.withOpacity(.5),
|
||||
color: colorTheme.black.withOpacity(.5),
|
||||
),
|
||||
),
|
||||
inputBackground:
|
||||
isDark ? Colors.black.withAlpha(12) : Colors.white.withAlpha(12),
|
||||
inputBackground: colorTheme.white.withAlpha(12),
|
||||
inputGradient: LinearGradient(colors: [
|
||||
Color(0xFF00AEFF),
|
||||
Color(0xFF0076FF),
|
||||
@@ -295,12 +281,10 @@ class StreamChatThemeData {
|
||||
ownMessageTheme: MessageTheme(
|
||||
messageText: TextStyle(
|
||||
fontSize: 14.5,
|
||||
color: isDark ? Colors.white : Colors.black,
|
||||
color: colorTheme.black,
|
||||
),
|
||||
createdAt: TextStyle(
|
||||
color: isDark
|
||||
? Colors.white.withOpacity(.5)
|
||||
: Colors.black.withOpacity(.5),
|
||||
color: colorTheme.black.withOpacity(.5),
|
||||
fontSize: 12,
|
||||
),
|
||||
replies: TextStyle(
|
||||
@@ -308,10 +292,10 @@ class StreamChatThemeData {
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 12,
|
||||
),
|
||||
messageBackgroundColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
|
||||
reactionsBackgroundColor: isDark ? Colors.black : Colors.white,
|
||||
reactionsBorderColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
|
||||
replyThreadColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
|
||||
messageBackgroundColor: colorTheme.greyGainsboro,
|
||||
reactionsBackgroundColor: colorTheme.white,
|
||||
reactionsBorderColor: colorTheme.greyWhisper,
|
||||
replyThreadColor: colorTheme.greyWhisper,
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
@@ -324,17 +308,14 @@ class StreamChatThemeData {
|
||||
),
|
||||
),
|
||||
otherMessageTheme: MessageTheme(
|
||||
reactionsBackgroundColor:
|
||||
isDark ? Color(0xff191919) : Color(0xffEAEAEA),
|
||||
reactionsBorderColor: isDark ? Colors.black : Colors.white,
|
||||
reactionsBackgroundColor: colorTheme.greyGainsboro,
|
||||
reactionsBorderColor: colorTheme.white,
|
||||
messageText: TextStyle(
|
||||
fontSize: 14.5,
|
||||
color: isDark ? Colors.white : Colors.black,
|
||||
color: colorTheme.black,
|
||||
),
|
||||
createdAt: TextStyle(
|
||||
color: isDark
|
||||
? Colors.white.withOpacity(.5)
|
||||
: Colors.black.withOpacity(.5),
|
||||
color: colorTheme.black.withOpacity(.5),
|
||||
fontSize: 12,
|
||||
),
|
||||
replies: TextStyle(
|
||||
@@ -345,9 +326,8 @@ class StreamChatThemeData {
|
||||
messageLinks: TextStyle(
|
||||
color: accentColor,
|
||||
),
|
||||
messageBackgroundColor: isDark ? Colors.black : Colors.white,
|
||||
replyThreadColor:
|
||||
isDark ? Colors.white.withAlpha(24) : Colors.black.withAlpha(24),
|
||||
messageBackgroundColor: colorTheme.white,
|
||||
replyThreadColor: colorTheme.black.withAlpha(24),
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
@@ -382,6 +362,230 @@ class StreamChatThemeData {
|
||||
}
|
||||
}
|
||||
|
||||
enum TextThemeType {
|
||||
light,
|
||||
dark,
|
||||
}
|
||||
|
||||
class TextTheme {
|
||||
final TextStyle title;
|
||||
final TextStyle headlineBold;
|
||||
final TextStyle headline;
|
||||
final TextStyle bodyBold;
|
||||
final TextStyle body;
|
||||
final TextStyle footnoteBold;
|
||||
final TextStyle footnote;
|
||||
final TextStyle captionBold;
|
||||
|
||||
TextTheme.light({
|
||||
this.title = const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
this.headlineBold = const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
this.headline = const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
this.bodyBold = const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
this.body = const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
this.footnoteBold = const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
this.footnote = const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
),
|
||||
this.captionBold = const TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
});
|
||||
|
||||
TextTheme.dark({
|
||||
this.title = const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
this.headlineBold = const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
this.headline = const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
this.bodyBold = const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
this.body = const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
this.footnoteBold = const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
this.footnote = const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white,
|
||||
),
|
||||
this.captionBold = const TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
});
|
||||
|
||||
TextTheme copyWith({
|
||||
TextThemeType type = TextThemeType.light,
|
||||
TextStyle body,
|
||||
TextStyle title,
|
||||
TextStyle headlineBold,
|
||||
TextStyle headline,
|
||||
TextStyle bodyBold,
|
||||
TextStyle footnoteBold,
|
||||
TextStyle footnote,
|
||||
TextStyle captionBold,
|
||||
}) {
|
||||
return type == TextThemeType.light
|
||||
? TextTheme.light(
|
||||
body: body ?? this.body,
|
||||
title: title ?? this.title,
|
||||
headlineBold: headlineBold ?? this.headlineBold,
|
||||
headline: headline ?? this.headline,
|
||||
bodyBold: bodyBold ?? this.bodyBold,
|
||||
footnoteBold: footnoteBold ?? this.footnoteBold,
|
||||
footnote: footnote ?? this.footnote,
|
||||
captionBold: captionBold ?? this.captionBold,
|
||||
)
|
||||
: TextTheme.dark(
|
||||
body: body ?? this.body,
|
||||
title: title ?? this.title,
|
||||
headlineBold: headlineBold ?? this.headlineBold,
|
||||
headline: headline ?? this.headline,
|
||||
bodyBold: bodyBold ?? this.bodyBold,
|
||||
footnoteBold: footnoteBold ?? this.footnoteBold,
|
||||
footnote: footnote ?? this.footnote,
|
||||
captionBold: captionBold ?? this.captionBold,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum ColorThemeType {
|
||||
light,
|
||||
dark,
|
||||
}
|
||||
|
||||
class ColorTheme {
|
||||
final Color black;
|
||||
final Color grey;
|
||||
final Color greyGainsboro;
|
||||
final Color greyWhisper;
|
||||
final Color whiteSmoke;
|
||||
final Color whiteSnow;
|
||||
final Color white;
|
||||
final Color blueAlice;
|
||||
final Color accentBlue;
|
||||
final Color accentRed;
|
||||
final Color accentGreen;
|
||||
|
||||
ColorTheme.light({
|
||||
this.black = const Color(0xff000000),
|
||||
this.grey = const Color(0xff7a7a7a),
|
||||
this.greyGainsboro = const Color(0xffdbdbdb),
|
||||
this.greyWhisper = const Color(0xffecebeb),
|
||||
this.whiteSmoke = const Color(0xfff2f2f2),
|
||||
this.whiteSnow = const Color(0xfffcfcfc),
|
||||
this.white = const Color(0xffffffff),
|
||||
this.blueAlice = const Color(0xffe9f2ff),
|
||||
this.accentBlue = const Color(0xff005FFF),
|
||||
this.accentRed = const Color(0xffFF3742),
|
||||
this.accentGreen = const Color(0xff20E070),
|
||||
});
|
||||
|
||||
ColorTheme.dark({
|
||||
this.black = const Color(0xffffffff),
|
||||
this.grey = const Color(0xff7a7a7a),
|
||||
this.greyGainsboro = const Color(0xff2d2f2f),
|
||||
this.greyWhisper = const Color(0xff1c1e22),
|
||||
this.whiteSmoke = const Color(0xff13151b),
|
||||
this.whiteSnow = const Color(0xff070A0D),
|
||||
this.white = const Color(0xff101418),
|
||||
this.blueAlice = const Color(0xff00193D),
|
||||
this.accentBlue = const Color(0xff005FFF),
|
||||
this.accentRed = const Color(0xffFF3742),
|
||||
this.accentGreen = const Color(0xff20E070),
|
||||
});
|
||||
|
||||
ColorTheme copyWith({
|
||||
ColorThemeType type = ColorThemeType.light,
|
||||
Color black,
|
||||
Color grey,
|
||||
Color greyGainsboro,
|
||||
Color greyWhisper,
|
||||
Color whiteSmoke,
|
||||
Color whiteSnow,
|
||||
Color white,
|
||||
Color blueAlice,
|
||||
Color accentBlue,
|
||||
Color accentRed,
|
||||
Color accentGreen,
|
||||
}) {
|
||||
return type == ColorThemeType.light
|
||||
? ColorTheme.light(
|
||||
black: black ?? this.black,
|
||||
grey: grey ?? this.grey,
|
||||
greyGainsboro: greyGainsboro ?? this.greyGainsboro,
|
||||
greyWhisper: greyWhisper ?? this.greyWhisper,
|
||||
whiteSmoke: whiteSmoke ?? this.whiteSmoke,
|
||||
whiteSnow: whiteSnow ?? this.whiteSnow,
|
||||
white: white ?? this.white,
|
||||
blueAlice: blueAlice ?? this.blueAlice,
|
||||
accentBlue: accentBlue ?? this.accentBlue,
|
||||
accentRed: accentRed ?? this.accentRed,
|
||||
accentGreen: accentGreen ?? this.accentGreen,
|
||||
)
|
||||
: ColorTheme.dark(
|
||||
black: black ?? this.black,
|
||||
grey: grey ?? this.grey,
|
||||
greyGainsboro: greyGainsboro ?? this.greyGainsboro,
|
||||
greyWhisper: greyWhisper ?? this.greyWhisper,
|
||||
whiteSmoke: whiteSmoke ?? this.whiteSmoke,
|
||||
whiteSnow: whiteSnow ?? this.whiteSnow,
|
||||
white: white ?? this.white,
|
||||
blueAlice: blueAlice ?? this.blueAlice,
|
||||
accentBlue: accentBlue ?? this.accentBlue,
|
||||
accentRed: accentRed ?? this.accentRed,
|
||||
accentGreen: accentGreen ?? this.accentGreen,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Channel theme data
|
||||
class ChannelTheme {
|
||||
/// Theme of the [ChannelHeader] widget
|
||||
|
||||
@@ -769,4 +769,28 @@ class StreamSvgIcon extends StatelessWidget {
|
||||
height: size,
|
||||
);
|
||||
}
|
||||
|
||||
factory StreamSvgIcon.error({
|
||||
double size,
|
||||
Color color,
|
||||
}) {
|
||||
return StreamSvgIcon(
|
||||
assetName: 'Icon_error.svg',
|
||||
color: color,
|
||||
width: size,
|
||||
height: size,
|
||||
);
|
||||
}
|
||||
|
||||
factory StreamSvgIcon.circle_up({
|
||||
double size,
|
||||
Color color,
|
||||
}) {
|
||||
return StreamSvgIcon(
|
||||
assetName: 'Icon_circle_up.svg',
|
||||
color: color,
|
||||
width: size,
|
||||
height: size,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-13
@@ -51,17 +51,21 @@ class UrlAttachment extends StatelessWidget {
|
||||
),
|
||||
child: Text(
|
||||
hostDisplayName,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Color(0xFF006CFF),
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(16.0),
|
||||
),
|
||||
color: Color(0xFFE9F2FF),
|
||||
color: StreamChatTheme.of(context).colorTheme.blueAlice,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -81,18 +85,18 @@ class UrlAttachment extends StatelessWidget {
|
||||
urlAttachment.title.trim(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.body
|
||||
.copyWith(fontWeight: FontWeight.w700),
|
||||
),
|
||||
if (urlAttachment.text != null)
|
||||
Text(
|
||||
urlAttachment.text,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.body
|
||||
.copyWith(fontWeight: FontWeight.w400),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -16,7 +16,7 @@ class UserAvatar extends StatelessWidget {
|
||||
this.borderRadius,
|
||||
this.onlineIndicatorAlignment = Alignment.topRight,
|
||||
this.selected = false,
|
||||
this.selectionColor = const Color(0xFF006CFF),
|
||||
this.selectionColor,
|
||||
this.selectionThickness = 4,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -47,7 +47,7 @@ class UserAvatar extends StatelessWidget {
|
||||
constraints: constraints ??
|
||||
streamChatTheme.ownMessageTheme.avatarTheme.constraints,
|
||||
decoration: BoxDecoration(
|
||||
color: streamChatTheme.accentColor,
|
||||
color: streamChatTheme.colorTheme.accentBlue,
|
||||
),
|
||||
child: hasImage
|
||||
? CachedNetworkImage(
|
||||
@@ -61,13 +61,17 @@ class UserAvatar extends StatelessWidget {
|
||||
: streamChatTheme.defaultUserImage(context, user),
|
||||
),
|
||||
);
|
||||
|
||||
if (selected) {
|
||||
avatar = ClipRRect(
|
||||
borderRadius: (borderRadius ??
|
||||
streamChatTheme.ownMessageTheme.avatarTheme.borderRadius) +
|
||||
BorderRadius.circular(selectionThickness),
|
||||
child: Container(
|
||||
color: selectionColor,
|
||||
constraints: constraints ??
|
||||
streamChatTheme.ownMessageTheme.avatarTheme.constraints,
|
||||
color: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(selectionThickness),
|
||||
child: avatar,
|
||||
@@ -96,10 +100,10 @@ class UserAvatar extends StatelessWidget {
|
||||
),
|
||||
child: Material(
|
||||
shape: CircleBorder(),
|
||||
color: Color(0xff20E070),
|
||||
color: streamChatTheme.colorTheme.accentGreen,
|
||||
),
|
||||
),
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
+11
-5
@@ -74,24 +74,30 @@ class UserItem extends StatelessWidget {
|
||||
),
|
||||
trailing: selected
|
||||
? CircleAvatar(
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
child: StreamSvgIcon.check(
|
||||
size: 20,
|
||||
color: Colors.white,
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
radius: 10,
|
||||
)
|
||||
: null,
|
||||
title: Text(
|
||||
user.name,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||
),
|
||||
subtitle: showLastOnline ? _buildLastActive(context) : null,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLastActive(context) {
|
||||
return user.online == true
|
||||
? Text('Online')
|
||||
: Text('Last online ${Jiffy(user.lastActive).fromNow()}');
|
||||
return Text(
|
||||
user.online == true
|
||||
? 'Online'
|
||||
: 'Last online ${Jiffy(user.lastActive).fromNow()}',
|
||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||
color: StreamChatTheme.of(context).colorTheme.black.withOpacity(.5)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,15 +356,19 @@ class _UserListViewState extends State<UserListView>
|
||||
headerItem: (header) {
|
||||
return Container(
|
||||
key: ValueKey<String>('HEADER-$header'),
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.05),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6),
|
||||
child: Text(
|
||||
header,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
color: Colors.black.withOpacity(0.3)),
|
||||
fontSize: 14.5,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.3)),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -449,7 +453,10 @@ class _UserListViewState extends State<UserListView>
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return Container(
|
||||
color: Color(0xffd0021B).withAlpha(26),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentRed
|
||||
.withOpacity(.2),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Center(
|
||||
@@ -472,8 +479,8 @@ class _UserListViewState extends State<UserListView>
|
||||
return Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white.withOpacity(0.1)
|
||||
: Colors.black.withOpacity(0.1),
|
||||
? StreamChatTheme.of(context).colorTheme.white.withOpacity(0.1)
|
||||
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.1),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -27,7 +27,7 @@ Future<bool> showConfirmationDialog(
|
||||
String cancelText,
|
||||
}) {
|
||||
return showModalBottomSheet(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
context: context,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
@@ -47,7 +47,7 @@ Future<bool> showConfirmationDialog(
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0),
|
||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
),
|
||||
SizedBox(
|
||||
height: 7.0,
|
||||
@@ -57,7 +57,8 @@ Future<bool> showConfirmationDialog(
|
||||
height: 36.0,
|
||||
),
|
||||
Container(
|
||||
color: Color(0xffe6e6e6),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
|
||||
height: 1.0,
|
||||
),
|
||||
Row(
|
||||
@@ -67,7 +68,10 @@ Future<bool> showConfirmationDialog(
|
||||
child: Text(
|
||||
cancelText,
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
onPressed: () {
|
||||
|
||||
Reference in New Issue
Block a user