import 'dart:async'; 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'; /// Widget used to provide information about the chat to the widget tree /// /// 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(), /// ), /// ), /// ); /// } /// } /// /// Use [StreamChat.of] to get the current [StreamChatState] instance. class StreamChat extends StatefulWidget { final Client client; final Widget child; final StreamChatThemeData streamChatThemeData; StreamChat({ Key key, @required this.client, @required this.child, this.streamChatThemeData, }) : super( key: key, ); @override StreamChatState createState() => StreamChatState(); /// Use this method to get the current [StreamChatState] instance static StreamChatState of(BuildContext context) { StreamChatState streamChatState; streamChatState = context.findAncestorStateOfType(); if (streamChatState == null) { throw Exception( 'You must have a StreamChat widget at the top of your widget tree'); } return streamChatState; } } class StreamChatState extends State with AutomaticKeepAliveClientMixin { final List _subscriptions = []; Client get client => widget.client; final GlobalKey _navigatorKey = GlobalKey(); @override Widget build(BuildContext context) { super.build(context); final theme = _getTheme(context, widget.streamChatThemeData); return StreamChatTheme( data: theme, child: Builder( builder: (context) { final materialTheme = Theme.of(context); final isDark = materialTheme.brightness == Brightness.dark; return Theme( data: materialTheme.copyWith( accentColor: StreamChatTheme.of(context).accentColor, scaffoldBackgroundColor: isDark ? Colors.black : Colors.white, backgroundColor: isDark ? Colors.black : Colors.white, ), 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 _getTheme( BuildContext context, StreamChatThemeData themeData, ) { final defaultTheme = StreamChatThemeData.getDefaultTheme(Theme.of(context)); final theme = defaultTheme.copyWith( primaryColor: themeData?.primaryColor, channelTheme: defaultTheme.channelTheme.copyWith( channelHeaderTheme: defaultTheme.channelTheme.channelHeaderTheme.copyWith( color: themeData?.channelTheme?.channelHeaderTheme?.color, lastMessageAt: themeData?.channelTheme?.channelHeaderTheme?.lastMessageAt, title: themeData?.channelTheme?.channelHeaderTheme?.title, avatarTheme: defaultTheme.channelPreviewTheme.avatarTheme.copyWith( constraints: themeData ?.channelTheme?.channelHeaderTheme?.avatarTheme?.constraints, borderRadius: themeData ?.channelTheme?.channelHeaderTheme?.avatarTheme?.borderRadius, ), ), inputBackground: themeData?.channelTheme?.inputBackground, messageInputButtonIconTheme: themeData?.channelTheme?.messageInputButtonIconTheme, inputGradient: themeData?.channelTheme?.inputGradient, messageInputButtonTheme: themeData?.channelTheme?.messageInputButtonTheme, ), ownMessageTheme: defaultTheme.ownMessageTheme.copyWith( replies: themeData?.ownMessageTheme?.replies, createdAt: themeData?.ownMessageTheme?.createdAt, messageText: themeData?.ownMessageTheme?.messageText, messageBackgroundColor: themeData?.ownMessageTheme?.messageBackgroundColor, messageAuthor: themeData?.ownMessageTheme?.messageAuthor, messageMention: themeData?.ownMessageTheme?.messageMention, avatarTheme: defaultTheme.ownMessageTheme.avatarTheme.copyWith( constraints: themeData?.ownMessageTheme?.avatarTheme?.constraints, borderRadius: themeData?.ownMessageTheme?.avatarTheme?.borderRadius, ), ), otherMessageTheme: defaultTheme.otherMessageTheme.copyWith( replies: themeData?.otherMessageTheme?.replies, createdAt: themeData?.otherMessageTheme?.createdAt, messageText: themeData?.otherMessageTheme?.messageText, messageBackgroundColor: themeData?.otherMessageTheme?.messageBackgroundColor, messageAuthor: themeData?.otherMessageTheme?.messageAuthor, messageMention: themeData?.otherMessageTheme?.messageMention, avatarTheme: defaultTheme.otherMessageTheme.avatarTheme.copyWith( constraints: themeData?.otherMessageTheme?.avatarTheme?.constraints, borderRadius: themeData?.otherMessageTheme?.avatarTheme?.borderRadius, ), ), accentColor: themeData?.accentColor, secondaryColor: themeData?.secondaryColor, channelPreviewTheme: defaultTheme.channelPreviewTheme.copyWith( avatarTheme: defaultTheme.channelPreviewTheme.avatarTheme.copyWith( constraints: themeData?.channelPreviewTheme?.avatarTheme?.constraints, borderRadius: themeData?.channelPreviewTheme?.avatarTheme?.borderRadius, ), title: themeData?.channelPreviewTheme?.title, lastMessageAt: themeData?.channelPreviewTheme?.lastMessageAt, subtitle: themeData?.channelPreviewTheme?.subtitle, ), ); return theme; } @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); channels.insert(0, channel); _channelsController.add(channels); } })); } /// The current user User get user => widget.client.state.user; /// The current user as a stream Stream get userStream => widget.client.state.userStream; /// The current channel list final List channels = []; /// The current channel list as a stream Stream> get channelsStream => _channelsController.stream; final BehaviorSubject> _channelsController = BehaviorSubject(); final BehaviorSubject _queryChannelsLoadingController = BehaviorSubject.seeded(false); /// The stream notifying the state of queryChannel call Stream get queryChannelsLoading => _queryChannelsLoadingController.stream; /// Calls [client.queryChannels] updating [queryChannelsLoading] stream Future queryChannels({ Map filter, List sortOptions, PaginationParams paginationParams, Map options, }) async { if (_queryChannelsLoadingController.value) { return; } _queryChannelsLoadingController.sink.add(true); try { final res = await widget.client.queryChannels( filter: filter, sort: sortOptions, options: options, paginationParams: paginationParams, ); channels.addAll(res); _channelsController.sink.add(channels); } catch (e) { _channelsController.sink.addError(e); } finally { _queryChannelsLoadingController.sink.add(false); } } /// Clear the current channel list void clearChannels() { channels.clear(); } @override void dispose() { _subscriptions.forEach((s) => s.cancel()); _queryChannelsLoadingController.close(); _channelsController.close(); super.dispose(); } @override bool get wantKeepAlive => true; }