add stream chat theme draft

This commit is contained in:
Salvatore Giordano
2020-02-27 17:09:20 +01:00
parent a1308fb380
commit 541cafb49f
17 changed files with 647 additions and 128 deletions
+18 -7
View File
@@ -9,14 +9,25 @@ void main() async {
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2lsZC1icmVlemUtNyJ9.VM2EX1EXOfgqa-bTH_3JzeY0T99ngWzWahSauP3dBMo',
);
runApp(
StreamChat(
client: client,
child: MaterialApp(
home: ChannelListPage(),
runApp(MyApp(client));
}
class MyApp extends StatelessWidget {
final Client client;
MyApp(this.client);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
child: StreamChat(
client: client,
child: ChannelListPage(),
),
),
),
);
);
}
}
class ChannelListPage extends StatelessWidget {
+18 -7
View File
@@ -9,14 +9,25 @@ void main() async {
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2lsZC1icmVlemUtNyJ9.VM2EX1EXOfgqa-bTH_3JzeY0T99ngWzWahSauP3dBMo',
);
runApp(
StreamChat(
client: client,
child: MaterialApp(
home: ChannelListPage(),
runApp(MyApp(client));
}
class MyApp extends StatelessWidget {
final Client client;
MyApp(this.client);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
child: StreamChat(
client: client,
child: ChannelListPage(),
),
),
),
);
);
}
}
class ChannelListPage extends StatelessWidget {
+18 -7
View File
@@ -9,14 +9,25 @@ void main() async {
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2lsZC1icmVlemUtNyJ9.VM2EX1EXOfgqa-bTH_3JzeY0T99ngWzWahSauP3dBMo',
);
runApp(
StreamChat(
client: client,
child: MaterialApp(
home: ChannelListPage(),
runApp(MyApp(client));
}
class MyApp extends StatelessWidget {
final Client client;
MyApp(this.client);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
child: StreamChat(
client: client,
child: ChannelListPage(),
),
),
),
);
);
}
}
class ChannelListPage extends StatelessWidget {
+4 -4
View File
@@ -18,10 +18,10 @@ void main() async {
channel.watch();
runApp(
StreamChat(
client: client,
child: MaterialApp(
home: StreamChannel(
MaterialApp(
home: StreamChat(
client: client,
child: StreamChannel(
channel: channel,
child: ChannelPage(),
),
+27 -7
View File
@@ -9,14 +9,34 @@ void main() async {
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2lsZC1icmVlemUtNyJ9.VM2EX1EXOfgqa-bTH_3JzeY0T99ngWzWahSauP3dBMo',
);
runApp(
StreamChat(
client: client,
child: MaterialApp(
home: ChannelListPage(),
runApp(MyApp(client));
}
class MyApp extends StatelessWidget {
final Client client;
MyApp(this.client);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
child: StreamChat(
streamChatThemeData: StreamChatThemeData(
accentColor: Colors.red,
channelTheme: ChannelTheme(
inputGradient: LinearGradient(colors: [
Colors.red,
Colors.yellow,
]),
),
),
client: client,
child: ChannelListPage(),
),
),
),
);
);
}
}
class ChannelListPage extends StatelessWidget {
+12 -1
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/channel_name.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:timeago/timeago.dart' as timeago;
import './channel_name.dart';
@@ -22,7 +23,10 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
Widget build(BuildContext context) {
final channel = StreamChannel.of(context).channel;
return AppBar(
elevation: 1,
leading: showBackButton ? _buildBackButton(context) : Container(),
backgroundColor:
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 10.0),
@@ -39,6 +43,10 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
children: <Widget>[
ChannelName(
channel: channel,
textStyle: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.title,
),
_buildLastActive(context, channel),
],
@@ -54,7 +62,10 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
return (snapshot.data != null)
? Text(
'Active ${timeago.format(snapshot.data)}',
style: Theme.of(context).textTheme.caption,
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.lastMessageAt,
)
: Container();
},
+28 -11
View File
@@ -1,16 +1,17 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class ChannelImage extends StatelessWidget {
const ChannelImage({
Key key,
@required this.channel,
this.radius = 20,
this.size = 40,
}) : super(key: key);
final Channel channel;
final double radius;
final double size;
@override
Widget build(BuildContext context) {
@@ -18,16 +19,32 @@ class ChannelImage extends StatelessWidget {
stream: channel.extraDataStream,
initialData: channel.extraData,
builder: (context, snapshot) {
return CircleAvatar(
radius: radius,
backgroundImage: snapshot.data?.containsKey('image') ?? false
? CachedNetworkImageProvider(snapshot.data['image'])
: null,
return Container(
padding: const EdgeInsets.all(4),
constraints: StreamChatTheme.of(context)
.channelPreviewTheme
.avatarTheme
.constraints,
decoration: BoxDecoration(
borderRadius: StreamChatTheme.of(context)
.channelPreviewTheme
.avatarTheme
.borderRadius,
color: StreamChatTheme.of(context).accentColor,
),
child: snapshot.data?.containsKey('image') ?? false
? null
: Text(snapshot.data?.containsKey('name') ?? false
? snapshot.data['name'][0]
: ''),
? CachedNetworkImage(
imageUrl: snapshot.data['image'],
errorWidget: (_, __, ___) {
return Center(
child: Text(snapshot.data?.containsKey('name') ?? false
? snapshot.data['name'][0]
: ''),
);
},
fit: BoxFit.cover,
)
: SizedBox(),
);
});
}
+2 -2
View File
@@ -161,7 +161,7 @@ class _ChannelListViewState extends State<ChannelListView> {
}
}
Widget _buildQueryProgressIndicator(context, StreamChat streamChat) {
Widget _buildQueryProgressIndicator(context, StreamChatState streamChat) {
return StreamBuilder<bool>(
stream: streamChat.queryChannelsLoading,
initialData: false,
@@ -184,7 +184,7 @@ class _ChannelListViewState extends State<ChannelListView> {
);
}
void _listenChannelPagination(StreamChat streamChat) {
void _listenChannelPagination(StreamChatState streamChat) {
if (widget._scrollController.position.maxScrollExtent ==
widget._scrollController.position.pixels) {
streamChat.queryChannels(
+13 -10
View File
@@ -5,22 +5,25 @@ class ChannelName extends StatelessWidget {
const ChannelName({
Key key,
@required this.channel,
this.textStyle,
}) : super(key: key);
final Channel channel;
final TextStyle textStyle;
@override
Widget build(BuildContext context) {
return StreamBuilder<Map<String, dynamic>>(
stream: channel.extraDataStream,
initialData: channel.extraData,
builder: (context, snapshot) {
return Text(
snapshot.data != null
? (snapshot.data['name'] ?? channel.cid)
: channel.cid,
style: Theme.of(context).textTheme.body2,
);
});
stream: channel.extraDataStream,
initialData: channel.extraData,
builder: (context, snapshot) {
return Text(
snapshot.data != null
? (snapshot.data['name'] ?? channel.cid)
: channel.cid,
style: textStyle,
);
},
);
}
}
+16 -6
View File
@@ -26,6 +26,7 @@ class ChannelPreview extends StatelessWidget {
channel: channel,
),
title: ChannelName(
textStyle: StreamChatTheme.of(context).channelPreviewTheme.title,
channel: channel,
),
subtitle: _buildSubtitle(),
@@ -62,7 +63,7 @@ class ChannelPreview extends StatelessWidget {
return Text(
stringDate,
style: Theme.of(context).textTheme.caption,
style: StreamChatTheme.of(context).channelPreviewTheme.lastMessageAt,
);
},
);
@@ -115,9 +116,14 @@ class ChannelPreview extends StatelessWidget {
text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.caption.copyWith(
color: Colors.black.withOpacity(opacity),
),
style:
StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
color: StreamChatTheme.of(context)
.channelPreviewTheme
.subtitle
.color
.withOpacity(opacity),
),
);
},
);
@@ -127,8 +133,12 @@ class ChannelPreview extends StatelessWidget {
return Text(
'${typings.map((u) => u.extraData.containsKey('name') ? u.extraData['name'] : u.id).join(',')} ${typings.length == 1 ? 'is' : 'are'} typing...',
maxLines: 1,
style: Theme.of(context).textTheme.caption.copyWith(
color: Colors.black.withOpacity(opacity),
style: StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
color: StreamChatTheme.of(context)
.channelPreviewTheme
.subtitle
.color
.withOpacity(opacity),
),
);
}
+16 -13
View File
@@ -1,7 +1,6 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'stream_channel.dart';
@@ -34,17 +33,17 @@ class _MessageInputState extends State<MessageInput> {
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
gradient: _typingStarted
? LinearGradient(colors: [Color(0xFF00AEFF), Color(0xFF0076FF)])
? StreamChatTheme.of(context).channelTheme.inputGradient
: null,
),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).canvasColor,
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Container(
decoration: BoxDecoration(
color: Colors.black.withAlpha(5),
color: StreamChatTheme.of(context).channelTheme.inputBackground,
borderRadius: BorderRadius.circular(10.0),
border: Border.all(color: Colors.black.withOpacity(.2)),
),
@@ -84,7 +83,7 @@ class _MessageInputState extends State<MessageInput> {
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild: _buildSendButton(context),
secondChild: Container(),
secondChild: SizedBox(),
duration: Duration(milliseconds: 300),
alignment: Alignment.center,
),
@@ -96,13 +95,17 @@ class _MessageInputState extends State<MessageInput> {
);
}
IconButton _buildSendButton(BuildContext context) {
return IconButton(
onPressed: () {
_sendMessage(context);
},
icon: Icon(
Icons.send,
Widget _buildSendButton(BuildContext context) {
return IconTheme(
data:
StreamChatTheme.of(context).channelTheme.messageInputButtonIconTheme,
child: IconButton(
onPressed: () {
_sendMessage(context);
},
icon: Icon(
Icons.send,
),
),
);
}
+2 -1
View File
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_widgets/flutter_widgets.dart';
import 'package:stream_chat/stream_chat.dart';
import '../stream_chat_flutter.dart';
import 'message_widget.dart';
import 'stream_channel.dart';
@@ -87,7 +88,7 @@ class _MessageListViewState extends State<MessageListView> {
child: Container(
padding: const EdgeInsets.all(8),
child: Text(
'Start of a new thread',
'Start of thread',
textAlign: TextAlign.center,
),
color: Theme.of(context).primaryColorLight,
+27 -10
View File
@@ -7,6 +7,7 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_channel.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:video_player/video_player.dart';
@@ -41,7 +42,7 @@ class _MessageWidgetState extends State<MessageWidget>
super.build(context);
final streamChat = StreamChat.of(context);
final currentUserId = streamChat.user.id;
final currentUserId = streamChat.client.state.user.id;
final messageUserId = widget.message.user.id;
final previousUserId = widget.previousMessage?.user?.id;
final nextUserId = widget.nextMessage?.user?.id;
@@ -116,7 +117,9 @@ class _MessageWidgetState extends State<MessageWidget>
),
child: Text(
'This message was deleted...',
style: TextStyle(fontStyle: FontStyle.italic),
style: StreamChatTheme.of(context).messageTheme.messageText.copyWith(
fontStyle: FontStyle.italic,
),
),
),
);
@@ -203,10 +206,13 @@ class _MessageWidgetState extends State<MessageWidget>
Text(
attachment.title,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.subtitle
.copyWith(color: Colors.blue),
style: StreamChatTheme.of(context)
.messageTheme
.messageText
.copyWith(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
Text(
Uri.parse(attachment.thumbUrl)
@@ -218,8 +224,9 @@ class _MessageWidgetState extends State<MessageWidget>
.reversed
.join('.'),
overflow: TextOverflow.ellipsis,
style:
Theme.of(context).textTheme.caption,
style: StreamChatTheme.of(context)
.messageTheme
.createdAt,
),
],
),
@@ -298,7 +305,12 @@ class _MessageWidgetState extends State<MessageWidget>
_launchURL(link);
},
styleSheet:
MarkdownStyleSheet.fromTheme(Theme.of(context)),
MarkdownStyleSheet.fromTheme(Theme.of(context))
.copyWith(
p: StreamChatTheme.of(context)
.messageTheme
.messageText,
),
),
),
),
@@ -547,6 +559,7 @@ class _MessageWidgetState extends State<MessageWidget>
padding: const EdgeInsets.only(top: 5.0),
child: Text(
formatDate(widget.message.createdAt.toLocal(), [HH, ':', nn]),
style: StreamChatTheme.of(context).messageTheme.createdAt,
),
);
}
@@ -560,7 +573,11 @@ class _MessageWidgetState extends State<MessageWidget>
topRight: Radius.circular((isMyMessage && isLastUser) ? 2 : 16),
bottomRight: Radius.circular(isMyMessage ? 2 : 16),
),
color: isMyMessage ? Color(0xffebebeb) : Colors.white,
color: isMyMessage
? StreamChatTheme.of(context).messageTheme.ownMessageBackgroundColor
: StreamChatTheme.of(context)
.messageTheme
.otherMessageBackgroundColor,
);
}
+204 -32
View File
@@ -4,20 +4,210 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
class StreamChat extends InheritedWidget {
class StreamChat extends StatefulWidget {
final Client client;
final List<StreamSubscription> _subscriptions = [];
final Widget child;
final StreamChatThemeData streamChatThemeData;
StreamChat({
Key key,
@required this.client,
@required Widget child,
@required this.child,
this.streamChatThemeData,
}) : super(
key: key,
child: child,
) {
_subscriptions.add(client.on('message.new').listen((Event e) {
);
@override
StreamChatState createState() => StreamChatState();
static StreamChatState of(BuildContext context) {
StreamChatState streamChatState;
streamChatState = context.findAncestorStateOfType<StreamChatState>();
if (streamChatState == null) {
throw Exception(
'You must have a StreamChat widget at the top of your widget tree');
}
return streamChatState;
}
}
class StreamChatState extends State<StreamChat> {
final List<StreamSubscription> _subscriptions = [];
Client get client => widget.client;
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
final theme = _computeTheme(context);
return StreamChatTheme(
data: theme,
child: Builder(
builder: (context) => Theme(
data: Theme.of(context).copyWith(
accentColor: StreamChatTheme.of(context).accentColor,
),
child: WillPopScope(
onWillPop: () async {
if (_navigatorKey.currentState.canPop()) {
_navigatorKey.currentState.pop();
return false;
} else {
return true;
}
},
child: Navigator(
initialRoute: '/',
key: _navigatorKey,
onGenerateRoute: (settings) {
return MaterialPageRoute(
settings: settings,
builder: (_) => widget.child,
);
},
),
),
),
),
);
}
StreamChatThemeData _computeTheme(BuildContext context) {
final defaultTheme = _getDefaultTheme(context);
final theme = defaultTheme.copyWith(
primaryColor: widget.streamChatThemeData?.primaryColor,
channelTheme: defaultTheme.channelTheme.copyWith(
channelHeaderTheme:
widget.streamChatThemeData?.channelTheme?.channelHeaderTheme,
inputBackground:
widget.streamChatThemeData?.channelTheme?.inputBackground,
messageInputButtonIconTheme: widget
.streamChatThemeData?.channelTheme?.messageInputButtonIconTheme,
inputGradient: widget.streamChatThemeData?.channelTheme?.inputGradient,
messageInputButtonTheme:
widget.streamChatThemeData?.channelTheme?.messageInputButtonTheme,
),
messageTheme: defaultTheme.messageTheme.copyWith(
createdAt: widget.streamChatThemeData?.messageTheme?.createdAt,
messageText: widget.streamChatThemeData?.messageTheme?.messageText,
otherMessageBackgroundColor: widget
.streamChatThemeData?.messageTheme?.otherMessageBackgroundColor,
ownMessageBackgroundColor:
widget.streamChatThemeData?.messageTheme?.ownMessageBackgroundColor,
fontFamily: widget.streamChatThemeData?.messageTheme?.fontFamily,
messageAuthor: widget.streamChatThemeData?.messageTheme?.messageAuthor,
messageMention:
widget.streamChatThemeData?.messageTheme?.messageMention,
avatarTheme: defaultTheme.messageTheme.avatarTheme.copyWith(
constraints: widget
.streamChatThemeData?.messageTheme?.avatarTheme?.constraints,
borderRadius: widget
.streamChatThemeData?.messageTheme?.avatarTheme?.borderRadius,
),
),
accentColor: widget.streamChatThemeData?.accentColor,
secondaryColor: widget.streamChatThemeData?.secondaryColor,
channelPreviewTheme: defaultTheme.channelPreviewTheme.copyWith(
avatarTheme: defaultTheme.channelPreviewTheme.avatarTheme.copyWith(
constraints: widget.streamChatThemeData?.channelPreviewTheme
?.avatarTheme?.constraints,
borderRadius: widget.streamChatThemeData?.channelPreviewTheme
?.avatarTheme?.borderRadius,
),
title: widget.streamChatThemeData?.channelPreviewTheme?.title,
lastMessageAt:
widget.streamChatThemeData?.channelPreviewTheme?.lastMessageAt,
subtitle: widget.streamChatThemeData?.channelPreviewTheme?.subtitle,
),
);
return theme;
}
StreamChatThemeData _getDefaultTheme(BuildContext context) {
final accentColor = Color(0xff006cff);
return StreamChatThemeData(
accentColor: accentColor,
primaryColor: Colors.white,
channelPreviewTheme: ChannelPreviewTheme(
avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
title: TextStyle(
fontSize: 14,
color: Colors.black,
),
subtitle: TextStyle(
fontSize: 13,
color: Colors.black,
),
lastMessageAt: TextStyle(
fontSize: 11,
color: Colors.black.withOpacity(.5),
),
),
channelTheme: ChannelTheme(
messageInputButtonIconTheme: Theme.of(context).iconTheme.copyWith(
color: accentColor,
),
channelHeaderTheme: ChannelHeaderTheme(
avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
color: Colors.white,
title: TextStyle(
fontSize: 14,
color: Colors.black,
),
lastMessageAt: TextStyle(
fontSize: 11,
color: Colors.black.withOpacity(.5),
),
),
inputBackground: Colors.black.withAlpha(12),
inputGradient: LinearGradient(colors: [
Color(0xFF00AEFF),
Color(0xFF0076FF),
]),
),
messageTheme: MessageTheme(
messageText: TextStyle(
fontSize: 15,
color: Colors.black,
),
createdAt: TextStyle(
color: Colors.black.withOpacity(.5),
fontSize: 11,
),
ownMessageBackgroundColor: Color(0xffebebeb),
otherMessageBackgroundColor: Colors.white,
avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor(
height: 32,
width: 32,
),
),
),
);
}
@override
void initState() {
super.initState();
_subscriptions.add(widget.client.on('message.new').listen((Event e) {
final index = channels.indexWhere((c) => c.cid == e.cid);
if (index > 0) {
final channel = channels.removeAt(index);
@@ -27,12 +217,14 @@ class StreamChat extends InheritedWidget {
}));
}
User get user => client.state.user;
User get user => widget.client.state.user;
Stream<User> get userStream => client.state.userStream;
Stream<User> get userStream => widget.client.state.userStream;
Stream<List<Channel>> get channelsStream => _channelsController.stream;
final BehaviorSubject<List<Channel>> _channelsController = BehaviorSubject();
final List<Channel> channels = [];
final BehaviorSubject<bool> _queryChannelsLoadingController =
@@ -53,7 +245,7 @@ class StreamChat extends InheritedWidget {
_queryChannelsLoadingController.sink.add(true);
try {
final res = await client.queryChannels(
final res = await widget.client.queryChannels(
filter: filter,
sort: sortOptions,
options: options,
@@ -71,32 +263,12 @@ class StreamChat extends InheritedWidget {
channels.clear();
}
@override
void dispose() {
client.dispose();
widget.client.dispose();
_subscriptions.forEach((s) => s.cancel());
_queryChannelsLoadingController.close();
_channelsController.close();
}
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
static StreamChat of(BuildContext context, [bool listen = false]) {
StreamChat streamChat;
if (listen) {
streamChat = context.dependOnInheritedWidgetOfExactType<StreamChat>();
} else {
streamChat = context.findAncestorWidgetOfExactType<StreamChat>();
}
if (streamChat == null) {
throw Exception(
'You must have a StreamChat widget at the top of your widget tree');
}
return streamChat;
super.dispose();
}
}
+217
View File
@@ -0,0 +1,217 @@
import 'package:flutter/material.dart';
class StreamChatTheme extends InheritedWidget {
final StreamChatThemeData data;
StreamChatTheme({
Key key,
@required this.data,
Widget child,
}) : super(
key: key,
child: child,
);
@override
bool updateShouldNotify(StreamChatTheme old) {
return data != old.data;
}
static StreamChatThemeData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<StreamChatTheme>().data;
}
}
class StreamChatThemeData {
final Color primaryColor;
final Color secondaryColor;
final Color accentColor;
final ChannelPreviewTheme channelPreviewTheme;
final ChannelTheme channelTheme;
final MessageTheme messageTheme;
StreamChatThemeData({
this.primaryColor,
this.secondaryColor,
this.accentColor,
this.channelPreviewTheme,
this.channelTheme,
this.messageTheme,
});
factory StreamChatThemeData.fromTheme(ThemeData theme) {
return StreamChatThemeData(
accentColor: theme.accentColor,
primaryColor: theme.colorScheme.primary,
secondaryColor: theme.colorScheme.secondary,
);
}
StreamChatThemeData copyWith({
Color primaryColor,
Color secondaryColor,
Color accentColor,
ChannelPreviewTheme channelPreviewTheme,
ChannelTheme channelTheme,
MessageTheme messageTheme,
}) =>
StreamChatThemeData(
primaryColor: primaryColor ?? this.primaryColor,
secondaryColor: secondaryColor ?? this.secondaryColor,
accentColor: accentColor ?? this.accentColor,
channelPreviewTheme: channelPreviewTheme ?? this.channelPreviewTheme,
channelTheme: channelTheme ?? this.channelTheme,
messageTheme: messageTheme ?? this.messageTheme,
);
}
class ChannelTheme {
final ChannelHeaderTheme channelHeaderTheme;
final IconThemeData messageInputButtonIconTheme;
final ButtonThemeData messageInputButtonTheme;
final Gradient inputGradient;
final Color inputBackground;
ChannelTheme({
this.channelHeaderTheme,
this.messageInputButtonIconTheme,
this.messageInputButtonTheme,
this.inputBackground,
this.inputGradient,
});
ChannelTheme copyWith({
ChannelHeaderTheme channelHeaderTheme,
IconThemeData messageInputButtonIconTheme,
ButtonThemeData messageInputButtonTheme,
Gradient inputGradient,
Color inputBackground,
}) =>
ChannelTheme(
channelHeaderTheme: channelHeaderTheme ?? this.channelHeaderTheme,
messageInputButtonIconTheme:
messageInputButtonIconTheme ?? this.messageInputButtonIconTheme,
messageInputButtonTheme:
messageInputButtonTheme ?? this.messageInputButtonTheme,
inputGradient: inputGradient ?? this.inputGradient,
inputBackground: inputBackground ?? this.inputBackground,
);
}
class AvatarTheme {
final BoxConstraints constraints;
final BorderRadius borderRadius;
AvatarTheme({
this.constraints,
this.borderRadius,
});
AvatarTheme copyWith({
BoxConstraints constraints,
BorderRadius borderRadius,
}) =>
AvatarTheme(
constraints: constraints ?? this.constraints,
borderRadius: borderRadius ?? this.borderRadius,
);
}
class MessageTheme {
final TextStyle messageText;
final TextStyle messageAuthor;
final TextStyle messageMention;
final TextStyle createdAt;
final String fontFamily;
final Color ownMessageBackgroundColor;
final Color otherMessageBackgroundColor;
final AvatarTheme avatarTheme;
const MessageTheme({
this.messageText,
this.messageAuthor,
this.messageMention,
this.fontFamily,
this.ownMessageBackgroundColor,
this.otherMessageBackgroundColor,
this.avatarTheme,
this.createdAt,
});
MessageTheme copyWith({
TextStyle messageText,
TextStyle messageAuthor,
TextStyle messageMention,
TextStyle createdAt,
String fontFamily,
Color ownMessageBackgroundColor,
Color otherMessageBackgroundColor,
AvatarTheme avatarTheme,
}) =>
MessageTheme(
messageText: messageText ?? this.messageText,
messageAuthor: messageAuthor ?? this.messageAuthor,
messageMention: messageMention ?? this.messageMention,
createdAt: createdAt ?? this.createdAt,
fontFamily: fontFamily ?? this.fontFamily,
ownMessageBackgroundColor:
ownMessageBackgroundColor ?? this.ownMessageBackgroundColor,
otherMessageBackgroundColor:
otherMessageBackgroundColor ?? this.otherMessageBackgroundColor,
avatarTheme: avatarTheme ?? this.avatarTheme,
);
}
class ChannelPreviewTheme {
final TextStyle title;
final TextStyle subtitle;
final TextStyle lastMessageAt;
final AvatarTheme avatarTheme;
const ChannelPreviewTheme({
this.title,
this.subtitle,
this.lastMessageAt,
this.avatarTheme,
});
ChannelPreviewTheme copyWith({
TextStyle title,
TextStyle subtitle,
TextStyle lastMessageAt,
AvatarTheme avatarTheme,
}) =>
ChannelPreviewTheme(
title: title ?? this.title,
subtitle: subtitle ?? this.subtitle,
lastMessageAt: lastMessageAt ?? this.lastMessageAt,
avatarTheme: avatarTheme ?? this.avatarTheme,
);
}
class ChannelHeaderTheme {
final TextStyle title;
final TextStyle lastMessageAt;
final AvatarTheme avatarTheme;
final Color color;
const ChannelHeaderTheme({
this.title,
this.lastMessageAt,
this.avatarTheme,
this.color,
});
ChannelHeaderTheme copyWith({
TextStyle title,
TextStyle lastMessageAt,
AvatarTheme avatarTheme,
Color color,
}) =>
ChannelHeaderTheme(
title: title ?? this.title,
lastMessageAt: lastMessageAt ?? this.lastMessageAt,
avatarTheme: avatarTheme ?? this.avatarTheme,
color: color ?? this.color,
);
}
+24 -10
View File
@@ -2,6 +2,8 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import '../stream_chat_flutter.dart';
class UserAvatar extends StatelessWidget {
const UserAvatar({
Key key,
@@ -14,16 +16,28 @@ class UserAvatar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CircleAvatar(
radius: radius,
backgroundImage: user.extraData.containsKey('image')
? CachedNetworkImageProvider(user.extraData['image'] as String)
: null,
child: user.extraData.containsKey('image')
? null
: Text(user?.extraData?.containsKey('name') ?? false
? user.extraData['name'][0]
: ''),
return Container(
padding: const EdgeInsets.all(4),
constraints:
StreamChatTheme.of(context).messageTheme.avatarTheme.constraints,
decoration: BoxDecoration(
borderRadius:
StreamChatTheme.of(context).messageTheme.avatarTheme.borderRadius,
color: StreamChatTheme.of(context).accentColor,
),
child: user.extraData?.containsKey('image') ?? false
? CachedNetworkImage(
imageUrl: user.extraData['image'],
errorWidget: (_, __, ___) {
return Center(
child: Text(user.extraData?.containsKey('name') ?? false
? user.extraData['name'][0]
: ''),
);
},
fit: BoxFit.cover,
)
: SizedBox(),
);
}
}
+1
View File
@@ -10,3 +10,4 @@ export 'src/message_list_view.dart';
export 'src/message_widget.dart';
export 'src/stream_channel.dart';
export 'src/stream_chat.dart';
export 'src/stream_chat_theme.dart';