stream_chat 10.0.0-beta.13 copy "stream_chat: ^10.0.0-beta.13" to clipboard
stream_chat: ^10.0.0-beta.13 copied to clipboard

The official Dart client for Stream Chat, a service for building chat applications.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';

Future<void> main() async {
  /// Create a new instance of [StreamChatClient]
  /// by passing the apikey obtained from your project dashboard.
  final client = StreamChatClient(
    'b67pax5b2wdq',
    logLevel: Level.INFO,
  );

  /// 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',
      name: 'Cool Shadow',
      image: 'https://getstream.io/random_png/?id=cool-shadow-7&amp;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({
    super.key,
    required this.client,
    required this.channel,
  });

  /// 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({
    super.key,
    required this.channel,
  });

  /// Channel object containing the [Channel.id] we'd like to observe.
  final Channel channel;

  @override
  Widget build(BuildContext context) {
    final messages = channel.state!.messagesStream;
    return Scaffold(
      appBar: AppBar(
        title: Text('Channel: ${channel.id}'),
      ),
      body: SafeArea(
        child: StreamBuilder<List<Message>?>(
          stream: messages,
          builder:
              (
                BuildContext context,
                AsyncSnapshot<List<Message>?> snapshot,
              ) {
                if (snapshot.hasData && snapshot.data != null) {
                  return MessageView(
                    messages: snapshot.data!.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({
    super.key,
    required this.messages,
    required this.channel,
  });

  /// List of messages sent in the given channel.
  final List<Message> messages;

  /// Current channel being observed.
  final Channel channel;

  @override
  _MessageViewState createState() => _MessageViewState();
}

class _MessageViewState extends State<MessageView> {
  late final TextEditingController _controller;
  late final ScrollController _scrollController;

  List<Message> 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;
}