import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.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; } } /// The current state of the StreamChat widget class StreamChatState extends State with WidgetsBindingObserver { Client get client => widget.client; final GlobalKey _navigatorKey = GlobalKey(); Timer _disconnectTimer; @override Widget build(BuildContext 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; } /// The current user User get user => widget.client.state.user; /// The current user as a stream Stream get userStream => widget.client.state.userStream; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } StreamSubscription _newMessageSubscription; @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.paused) { if (Platform.isAndroid) { _newMessageSubscription = client.on(EventType.messageNew).listen((event) { client.androidNotificationHandler({ 'data': { 'message_id': event.message.id, }, }); }); _disconnectTimer = Timer(Duration(minutes: 1), () { client.disconnect(); }); } else { client.disconnect(); } } else if (state == AppLifecycleState.resumed) { _newMessageSubscription?.cancel(); if (_disconnectTimer?.isActive == true) { _disconnectTimer.cancel(); } else { if (client.wsConnectionStatus.value == ConnectionStatus.disconnected) { NotificationService.handleIosMessageQueue(client); client.connect(); } } } } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } }