add stream chat theme draft
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,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(),
|
||||
),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user