import 'package:flutter/material.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/stream_chat_persistence.dart'; Future main() async { /// Create a new instance of [StreamChatClient] passing the apikey obtained /// from your project dashboard. final client = StreamChatClient('b67pax5b2wdq'); WidgetsFlutterBinding.ensureInitialized(); /// Set the chatPersistenceClient for offline support client.chatPersistenceClient = StreamChatPersistenceClient( logLevel: Level.INFO, connectionMode: ConnectionMode.background, ); /// Set the current user. In a production scenario, this should be done using /// a backend to generate a user token using our server SDK. /// Please see the following for more information: /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ await client.connectUser( User( id: 'cool-shadow-7', image: 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', ), 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.' 'gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo', ); /// Creates a channel using the type `messaging` and `godevs`. /// Channels are containers for holding messages between different members. To /// learn more about channels and some of our predefined types, checkout our /// our channel docs: https://getstream.io/chat/docs/initialize_channel/?language=dart final channel = client.channel('messaging', id: 'godevs'); /// `.watch()` is used to create and listen to the channel for updates. If the /// channel already exists, it will simply listen for new events. await channel.watch(); runApp( StreamExample( client: client, channel: channel, ), ); } /// Example using Stream's Low Level Dart client. class StreamExample extends StatelessWidget { /// To initialize this example, an instance of /// [client] and [channel] is required. const StreamExample({ Key? key, required this.client, required this.channel, }) : super(key: key); /// Instance of [StreamChatClient] we created earlier. /// This contains information about our application and connection state. final StreamChatClient client; /// The channel we'd like to observe and participate. final Channel channel; @override Widget build(BuildContext context) => MaterialApp( title: 'Stream Chat Dart Example', home: HomeScreen(channel: channel), ); } /// Main screen of our application. The layout is comprised of an [AppBar] /// containing the channel name and a [MessageView] displaying recent messages. class HomeScreen extends StatelessWidget { /// [HomeScreen] is constructed using the [Channel] we defined earlier. const HomeScreen({ Key? key, required this.channel, }) : super(key: key); /// Channel object containing the [Channel.id] we'd like to observe. final Channel channel; @override Widget build(BuildContext context) { final messages = channel.state!.channelStateStream; return Scaffold( appBar: AppBar( title: Text('Channel: ${channel.id}'), ), body: SafeArea( child: StreamBuilder( stream: messages, builder: ( BuildContext context, AsyncSnapshot snapshot, ) { if (snapshot.hasData && snapshot.data != null) { return MessageView( messages: snapshot.data!.messages.reversed.toList(), channel: channel, ); } else if (snapshot.hasError) { return const Center( child: Text( 'There was an error loading messages. Please see logs.', ), ); } return const Center( child: SizedBox( width: 100, height: 100, child: CircularProgressIndicator(), ), ); }, ), ), ); } } /// UI used to display a list of recent messages and a [TextField] for sending /// new messages. class MessageView extends StatefulWidget { /// Message takes the latest list of messages and the current channel. const MessageView({ Key? key, required this.messages, required this.channel, }) : super(key: key); /// List of messages sent in the given channel. final List messages; /// Current channel being observed. final Channel channel; @override _MessageViewState createState() => _MessageViewState(); } class _MessageViewState extends State { late final TextEditingController _controller; late final ScrollController _scrollController; List get _messages => widget.messages; @override void initState() { super.initState(); _controller = TextEditingController(); _scrollController = ScrollController(); } @override void dispose() { _controller.dispose(); _scrollController.dispose(); super.dispose(); } /// Convenience method for scrolling the list view when a new message is sent. void _updateList() { _scrollController.animateTo( 0, duration: const Duration(milliseconds: 200), curve: Curves.easeOut, ); } @override Widget build(BuildContext context) => Column( children: [ Expanded( child: ListView.builder( controller: _scrollController, itemCount: _messages.length, reverse: true, itemBuilder: (BuildContext context, int index) { final item = _messages[index]; if (item.user?.id == widget.channel.client.uid) { return Align( alignment: Alignment.centerRight, child: Padding( padding: const EdgeInsets.all(8), child: Text(item.text ?? ''), ), ); } else { return Align( alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.all(8), child: Text(item.text ?? ''), ), ); } }, ), ), Padding( padding: const EdgeInsets.all(8), child: Row( children: [ Expanded( child: TextField( controller: _controller, decoration: const InputDecoration( hintText: 'Enter your message', ), ), ), Material( type: MaterialType.circle, color: Colors.blue, clipBehavior: Clip.hardEdge, child: InkWell( onTap: () async { // We can send a new message by calling `sendMessage` on // the current channel. After sending a message, the // TextField is cleared and the list view is scrolled // to show the new item. if (_controller.value.text.isNotEmpty) { await widget.channel.sendMessage( Message(text: _controller.value.text), ); _controller.clear(); _updateList(); } }, child: const Padding( padding: EdgeInsets.all(8), child: Center( child: Icon( Icons.send, color: Colors.white, ), ), ), ), ), ], ), ) ], ); } /// Helper extension for quickly retrieving /// the current user id from a [StreamChatClient]. extension on StreamChatClient { String get uid => state.currentUser!.id; }