From 2db6ff5406f4a3d6f030bfc31bf22f9cb1585fcb Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 2 Mar 2020 11:14:00 +0100 Subject: [PATCH] add tutorial docs in examples --- example/lib/custom_message.dart | 22 +++- example/lib/custom_theme.dart | 26 ++++- example/lib/customize_channel_preview.dart | 27 ++++- example/lib/main.dart | 111 +++++++++++++++++++++ example/lib/multiple_conversation.dart | 26 ++++- example/lib/single_conversation.dart | 29 +++++- example/lib/threads.dart | 17 +++- lib/src/channel_image.dart | 11 +- 8 files changed, 248 insertions(+), 21 deletions(-) create mode 100644 example/lib/main.dart diff --git a/example/lib/custom_message.dart b/example/lib/custom_message.dart index 9a86876e..8d9e726a 100644 --- a/example/lib/custom_message.dart +++ b/example/lib/custom_message.dart @@ -1,12 +1,28 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +/// Fifth step of the [tutorial](https://getstream.io/chat/flutter/tutorial/) +/// +/// Customizing how messages are rendered is another very common use-case that the SDK supports easily. +/// +/// Replace the built-in message component with your own is done by passing it as a builder function to the [MessageListView] widget. +/// +/// The message builder function will get the usual [BuildContext] argument as well as the [Message] object and its position inside the list. +/// +/// If you look at the code you can see that we use [StreamChat.of] to retrieve the current user so that we can style messages own messages in a different way. +/// +/// Since custom widgets and builders are always children of [StreamChat] or part of a [Channel], +/// you can use [StreamChat.of], [StreamChannel.of] and [StreamChatTheme.of] to use the API client directly +/// or to retrieve outer scope needed such as messages from the [Channel.state]. void main() async { - final client = Client('qk4nn7rpcn75'); + final client = Client( + 'b67pax5b2wdq', + logLevel: Level.INFO, + ); await client.setUser( - User(id: 'wild-breeze-7'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2lsZC1icmVlemUtNyJ9.VM2EX1EXOfgqa-bTH_3JzeY0T99ngWzWahSauP3dBMo', + User(id: 'falling-mountain-7'), + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E', ); runApp(MyApp(client)); diff --git a/example/lib/custom_theme.dart b/example/lib/custom_theme.dart index 0f2a57d1..078faeb8 100644 --- a/example/lib/custom_theme.dart +++ b/example/lib/custom_theme.dart @@ -1,12 +1,32 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +/// Sixth step of the [tutorial](https://getstream.io/chat/flutter/tutorial/) +/// +/// The Flutter SDK comes with a fully designed set of widgets which you can customize to fit with your application style and typography. +/// Changing the theme of Chat widgets works in a very similar way that [MaterialApp] and [Theme] do. +/// +/// Out of the box all chat widgets use their own default styling, there are two ways to change the styling: +/// +/// 1. Initialize the [StreamChatTheme] from your existing [MaterialApp] style +/// 2. Construct a custom theme and provide all the customizations needed +/// +/// First we create a new Material [Theme] and pick [Colors.green] as swatch color. The theme is then passed to [MaterialApp] as usual. +/// +/// Then we create a new [StreamChatTheme] from the green theme we just created. +/// After saving the app you will see the UI will update several widgets to match with the new color. +/// +/// We also change the message color posted by the current user. +/// You can perform these more granular style changes using [StreamChatTheme.copyWith]. void main() async { - final client = Client('qk4nn7rpcn75', logLevel: Level.INFO); + final client = Client( + 'b67pax5b2wdq', + logLevel: Level.INFO, + ); await client.setUser( - User(id: 'wild-breeze-7'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2lsZC1icmVlemUtNyJ9.VM2EX1EXOfgqa-bTH_3JzeY0T99ngWzWahSauP3dBMo', + User(id: 'falling-mountain-7'), + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E', ); runApp(MyApp(client)); diff --git a/example/lib/customize_channel_preview.dart b/example/lib/customize_channel_preview.dart index da196fd9..68022b85 100644 --- a/example/lib/customize_channel_preview.dart +++ b/example/lib/customize_channel_preview.dart @@ -1,12 +1,33 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +/// Third step of the [tutorial](https://getstream.io/chat/flutter/tutorial/) +/// +/// So far you’ve learned how to use the default widgets. +/// The library has been designed with composition in mind and to allow all common customizations to be very easy. +/// This means that you can change any component in your application by swapping the default widgets with the ones you build yourself. +/// +/// Let’s see how we can make some changes to the SDK’s UI components. +/// We start by changing how channel previews are shown in the channel list and include the number of unread messages for each. +/// +/// We're passing a custom widget to [ChannelListView.channelPreviewBuilder], this will override the default [ChannelPreview] and allows you to create one yourself. +/// +/// There are a couple interesting things we do in this widget: +/// +/// - Instead of creating a whole new style for the channel name, we inherit the text style from the parent theme ([StreamChatTheme.of]) and only change the color attribute +/// +/// - We loop over the list of channel messages to search for the first not deleted message ([Channel.state.messages]) +/// +/// - We retrieve the count of unread messages from [Channel.state] void main() async { - final client = Client('qk4nn7rpcn75'); + final client = Client( + 'b67pax5b2wdq', + logLevel: Level.INFO, + ); await client.setUser( - User(id: 'wild-breeze-7'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2lsZC1icmVlemUtNyJ9.VM2EX1EXOfgqa-bTH_3JzeY0T99ngWzWahSauP3dBMo', + User(id: 'falling-mountain-7'), + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E', ); runApp(MyApp(client)); diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 00000000..a28a4320 --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +void main() async { + final client = Client( + 'b67pax5b2wdq', + logLevel: Level.INFO, + ); + + await client.setUser( + User(id: 'falling-mountain-7'), + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E', + ); + + 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 { + @override + Widget build(BuildContext context) { + return Scaffold( + body: ChannelListView( + filter: { + 'members': { + '\$in': [StreamChat.of(context).user.id], + } + }, + sort: [SortOption('last_message_at')], + pagination: PaginationParams( + limit: 20, + ), + channelWidget: ChannelPage(), + ), + ); + } +} + +class ChannelPage extends StatelessWidget { + const ChannelPage({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: ChannelHeader(), + body: Column( + children: [ + Expanded( + child: MessageListView( + threadBuilder: (_, parentMessage) { + return ThreadPage( + parent: parentMessage, + ); + }, + ), + ), + MessageInput(), + ], + ), + ); + } +} + +class ThreadPage extends StatelessWidget { + final Message parent; + + ThreadPage({ + Key key, + this.parent, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: ThreadHeader( + parent: parent, + ), + body: Column( + children: [ + Expanded( + child: MessageListView( + parentMessage: parent, + ), + ), + MessageInput( + parentMessage: parent, + ), + ], + ), + ); + } +} diff --git a/example/lib/multiple_conversation.dart b/example/lib/multiple_conversation.dart index bbf20812..56e5ed1e 100644 --- a/example/lib/multiple_conversation.dart +++ b/example/lib/multiple_conversation.dart @@ -1,12 +1,32 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +/// Second step of the [tutorial](https://getstream.io/chat/flutter/tutorial/) +/// +/// Most chat applications handle more than just one single conversation. +/// Apps like Facebook Messenger, Whatsapp and Telegram allows you to have multiple one to one and group conversations. +/// +/// Let’s find out how we can change our application chat screen to display the list of conversations and navigate between them. +/// +/// > Note: the SDK uses Flutter’s [Navigator] to move from one route to another, this allows us to avoid any boiler-plate code. +/// > Of course you can take total control of how navigation works by customizing widgets like [Channel] and [ChannelList]. +/// +/// If you run the application, you will see that the first screen shows a list of conversations, you can open each by tapping and go back to the list. +/// +/// Every single widget involved in this UI can be customized or swapped with your own. +/// +/// The [ChannelListPage] widget retrieves the list of channels based on a custom query and ordering. +/// In this case we are showing the list of channels the current user is a member and we order them based on the time they had a new message. +/// [ChannelListView] handles pagination and updates automatically out of the box when new channels are created or when a new message is added to a channel. void main() async { - final client = Client('s2dxdhpxd94g', logLevel: Level.INFO); + final client = Client( + 'b67pax5b2wdq', + logLevel: Level.INFO, + ); await client.setUser( - User(id: 'still-resonance-3'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3RpbGwtcmVzb25hbmNlLTMifQ.e6BGSX21gLZYpls_rFX8PB2SfGlfdpvv00pQiVW70VQ', + User(id: 'falling-mountain-7'), + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E', ); runApp(MyApp(client)); diff --git a/example/lib/single_conversation.dart b/example/lib/single_conversation.dart index 8028a84a..a7863fc0 100644 --- a/example/lib/single_conversation.dart +++ b/example/lib/single_conversation.dart @@ -1,15 +1,38 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +/// First step of the [tutorial](https://getstream.io/chat/flutter/tutorial/) +/// +/// There are three important things to notice that are common to all Flutter application using StreamChat: +/// +/// 1. The Dart API [Client] is initialized with your API Key +/// 2. The current user is set by calling [Client.setUser] +/// 3. The client is then passed to the top-level [StreamChat] widget +/// [StreamChat] is an inherited widget and must be the parent of all Chat related widgets. +/// +/// Please note that while Flutter can be used to build both mobile and web applications; +/// in this tutorial we focus on mobile, make sure when running the app you use a mobile device. +/// +/// Let's have a look at what we've built: +/// +/// - We set up the Chat [Client] with the API key +/// +/// - We set the the current user for Chat with [Client.setUser] and a pre-generated user token +/// +/// - We make [StreamChat] the root Widget of our application +/// +/// - We create a single [ChannelPage] widget under [StreamChat] with three widgets: [ChannelHeader], [MessageListView] and [MessageInput] +/// +/// If you now run the simulator you will see a single channel UI. void main() async { final client = Client( - 'qk4nn7rpcn75', + 'b67pax5b2wdq', logLevel: Level.INFO, ); await client.setUser( - User(id: 'wild-breeze-7'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2lsZC1icmVlemUtNyJ9.VM2EX1EXOfgqa-bTH_3JzeY0T99ngWzWahSauP3dBMo', + User(id: 'falling-mountain-7'), + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E', ); final channel = client.channel('messaging', id: 'godevs'); diff --git a/example/lib/threads.dart b/example/lib/threads.dart index 20f67f63..092a5c7f 100644 --- a/example/lib/threads.dart +++ b/example/lib/threads.dart @@ -1,12 +1,23 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +/// Fourth step of the [tutorial](https://getstream.io/chat/flutter/tutorial/) +/// +/// Stream Chat supports message threads out of the box. Threads allows users to create sub-conversations inside the same channel. +/// +/// Using threaded conversations is very simple and mostly a matter of plugging the [MessageListView] to another widget that renders the widget. +/// To make this simple, such a widget only needs to build [MessageListView] with the parent attribute set to the thread’s root message. +/// +/// Now we can open threads and create new ones as well, if you long press a message you can tap on Reply and it will open the same [ThreadPage]. void main() async { - final client = Client('qk4nn7rpcn75', logLevel: Level.INFO); + final client = Client( + 'b67pax5b2wdq', + logLevel: Level.INFO, + ); await client.setUser( - User(id: 'wild-breeze-7'), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoid2lsZC1icmVlemUtNyJ9.VM2EX1EXOfgqa-bTH_3JzeY0T99ngWzWahSauP3dBMo', + User(id: 'falling-mountain-7'), + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmFsbGluZy1tb3VudGFpbi03In0.AKgRXHMQQMz6vJAKszXdY8zMFfsAgkoUeZHlI-Szz9E', ); runApp(MyApp(client)); diff --git a/lib/src/channel_image.dart b/lib/src/channel_image.dart index 03b18a92..5d0a6693 100644 --- a/lib/src/channel_image.dart +++ b/lib/src/channel_image.dart @@ -38,9 +38,14 @@ class ChannelImage extends StatelessWidget { errorWidget: (_, __, ___) { return Center( child: Text( - snapshot.data?.containsKey('name') ?? false - ? snapshot.data['name'][0] - : ''), + snapshot.data?.containsKey('name') ?? false + ? snapshot.data['name'][0] + : '', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), ); }, fit: BoxFit.cover,