From acdb7ed2ae1c003e0ae2cb6b7213e7476eafcf9f Mon Sep 17 00:00:00 2001 From: VelikovPetar Date: Mon, 24 Aug 2026 13:04:04 +0200 Subject: [PATCH 1/2] docs(repo): rebuild the tutorial example on `StreamChannelPage` Co-Authored-By: Claude Opus 5 --- .../lib/tutorial/channel_list_page.dart | 57 +++++ .../example/lib/tutorial/client.dart | 46 ++++ .../example/lib/tutorial/main_step4.dart | 40 ++++ .../example/lib/tutorial/main_step5.dart | 85 +++++++ .../example/lib/tutorial/main_step6.dart | 89 +++++++ .../example/lib/tutorial/rounded_avatar.dart | 41 ++++ .../example/lib/tutorial_part_1.dart | 117 --------- .../example/lib/tutorial_part_2.dart | 142 ----------- .../example/lib/tutorial_part_3.dart | 196 --------------- .../example/lib/tutorial_part_4.dart | 185 --------------- .../example/lib/tutorial_part_5.dart | 174 -------------- .../example/lib/tutorial_part_6.dart | 223 ------------------ 12 files changed, 358 insertions(+), 1037 deletions(-) create mode 100644 packages/stream_chat_flutter/example/lib/tutorial/channel_list_page.dart create mode 100644 packages/stream_chat_flutter/example/lib/tutorial/client.dart create mode 100644 packages/stream_chat_flutter/example/lib/tutorial/main_step4.dart create mode 100644 packages/stream_chat_flutter/example/lib/tutorial/main_step5.dart create mode 100644 packages/stream_chat_flutter/example/lib/tutorial/main_step6.dart create mode 100644 packages/stream_chat_flutter/example/lib/tutorial/rounded_avatar.dart delete mode 100644 packages/stream_chat_flutter/example/lib/tutorial_part_1.dart delete mode 100644 packages/stream_chat_flutter/example/lib/tutorial_part_2.dart delete mode 100644 packages/stream_chat_flutter/example/lib/tutorial_part_3.dart delete mode 100644 packages/stream_chat_flutter/example/lib/tutorial_part_4.dart delete mode 100644 packages/stream_chat_flutter/example/lib/tutorial_part_5.dart delete mode 100644 packages/stream_chat_flutter/example/lib/tutorial_part_6.dart diff --git a/packages/stream_chat_flutter/example/lib/tutorial/channel_list_page.dart b/packages/stream_chat_flutter/example/lib/tutorial/channel_list_page.dart new file mode 100644 index 0000000000..acd2a971dd --- /dev/null +++ b/packages/stream_chat_flutter/example/lib/tutorial/channel_list_page.dart @@ -0,0 +1,57 @@ +// ignore_for_file: public_member_api_docs +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +/// The entry screen from Step 4 of the +/// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/). +/// +/// [StreamChannelListController] owns the query, pagination, and live updates; +/// [StreamChannelListView] renders it. Tapping a channel pushes +/// [StreamChannelPage], the SDK's ready-made conversation screen - it wires up +/// the header, message list, composer, and threads for you. +/// +/// Shared by all three `main_step*.dart` entry points. +class ChannelListPage extends StatefulWidget { + const ChannelListPage({super.key}); + + @override + State createState() => _ChannelListPageState(); +} + +class _ChannelListPageState extends State { + /// Queries channels the current user belongs to, newest activity first. + /// The controller owns pagination and live updates. + late final _listController = StreamChannelListController( + client: StreamChat.of(context).client, + filter: Filter.in_('members', [StreamChat.of(context).currentUser!.id]), + channelStateSort: const [SortOption.desc('last_message_at')], + limit: 20, + ); + + @override + void dispose() { + _listController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: context.streamColorScheme.backgroundApp, + appBar: const StreamChannelListHeader(), + body: StreamChannelListView( + controller: _listController, + onChannelTap: (channel) => Navigator.of(context).push( + MaterialPageRoute( + /// `StreamChannel` scopes the tapped channel to the subtree and + /// calls `watch()` on it, so `StreamChannelPage` needs no arguments. + builder: (_) => StreamChannel( + channel: channel, + child: const StreamChannelPage(), + ), + ), + ), + ), + ); + } +} diff --git a/packages/stream_chat_flutter/example/lib/tutorial/client.dart b/packages/stream_chat_flutter/example/lib/tutorial/client.dart new file mode 100644 index 0000000000..e5933a1a49 --- /dev/null +++ b/packages/stream_chat_flutter/example/lib/tutorial/client.dart @@ -0,0 +1,46 @@ +// ignore_for_file: public_member_api_docs +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_persistence/stream_chat_persistence.dart'; + +/// Credentials and client setup shared by the tutorial entry points. +/// +/// This mirrors the top of `lib/main.dart` in Step 4 of the +/// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/). +/// The three `main_step*.dart` entry points differ only in `MyApp`, so the +/// setup lives here rather than being repeated in each of them. + +/// Credentials from Step 3 of the tutorial. +/// - API key: `getstream env --target flutter` writes it to `dart_defines.json`, +/// passed in with `--dart-define-from-file` and read here. Falls back to the demo key. +/// - User + token: set [userId] to the user you minted a token for and paste that +/// token below - both must match, or the connection is rejected. Or keep the +/// demo pair below as-is. +const _envApiKey = String.fromEnvironment('STREAM_API_KEY'); + +const apiKey = _envApiKey == '' ? 'b67pax5b2wdq' : _envApiKey; +const userId = 'tutorial-flutter'; +const userName = 'Tutorial Flutter'; +const userToken = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidHV0b3JpYWwtZmx1dHRlciJ9.S-MJpoSwDiqyXpUURgO5wVqJ4vKlIVFLSEyrFYCOE1c'; + +/// Builds the client, turns on offline storage, and connects the user. +Future connectTutorialUser() async { + /// Offline support: channels and messages are cached on device, so the app + /// opens with content even without a connection. Attach the persistence + /// client *before* `connectUser` - attaching it afterwards does nothing for + /// the current session. + final client = StreamChatClient(apiKey, logLevel: Level.INFO) + ..chatPersistenceClient = StreamChatPersistenceClient( + logLevel: Level.INFO, + connectionMode: ConnectionMode.regular, + ); + + /// Development token from `getstream token`. In production, fetch the + /// token from your backend after login - never hardcode secrets. + await client.connectUser( + User(id: userId, name: userName), + userToken, + ); + + return client; +} diff --git a/packages/stream_chat_flutter/example/lib/tutorial/main_step4.dart b/packages/stream_chat_flutter/example/lib/tutorial/main_step4.dart new file mode 100644 index 0000000000..586251dc1f --- /dev/null +++ b/packages/stream_chat_flutter/example/lib/tutorial/main_step4.dart @@ -0,0 +1,40 @@ +// ignore_for_file: public_member_api_docs +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_flutter_example/tutorial/channel_list_page.dart'; +import 'package:stream_chat_flutter_example/tutorial/client.dart'; + +/// Step 4 of the +/// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/) - +/// a working app on the default theme. +/// +/// Run with: `flutter run -t lib/tutorial/main_step4.dart` +/// +/// Two screens are all you write: the channel list, and the client setup that +/// puts [StreamChat] above the app. The conversation itself is +/// [StreamChannelPage], which the list navigates to. +/// +/// `main_step5.dart` and `main_step6.dart` differ from this file only in +/// `MyApp` - that is the whole surface theming and component overrides touch. +Future main() async { + final client = await connectTutorialUser(); + + runApp(MyApp(client: client)); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key, required this.client}); + + /// The client created in `main`. Holds the connection and the local cache. + final StreamChatClient client; + + @override + Widget build(BuildContext context) { + return MaterialApp( + /// `StreamChat` must be an ancestor of every Stream widget. Putting it in + /// `builder` keeps it above whatever `home` renders. + builder: (context, child) => StreamChat(client: client, child: child), + home: const ChannelListPage(), + ); + } +} diff --git a/packages/stream_chat_flutter/example/lib/tutorial/main_step5.dart b/packages/stream_chat_flutter/example/lib/tutorial/main_step5.dart new file mode 100644 index 0000000000..9b94051f21 --- /dev/null +++ b/packages/stream_chat_flutter/example/lib/tutorial/main_step5.dart @@ -0,0 +1,85 @@ +// ignore_for_file: public_member_api_docs +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_flutter_example/tutorial/channel_list_page.dart'; +import 'package:stream_chat_flutter_example/tutorial/client.dart'; + +/// Step 5 of the +/// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/) - +/// the same app, themed. +/// +/// Run with: `flutter run -t lib/tutorial/main_step5.dart` +/// +/// Theming works in two layers, and you rarely need more than the first: +/// +/// 1. Design tokens - a [StreamTheme] registered as a [ThemeData] extension. +/// Give it a brand color and Stream derives its whole semantic palette +/// from that swatch. +/// 2. Per-widget overrides - a [StreamChatThemeData] passed to +/// [StreamChat.themeData], merged on top. Reach for this only when one +/// component needs to differ. +/// +/// Both land inside [StreamChannelPage] as well, since it resolves the ambient +/// theme like any other Stream widget. Only `MyApp` changes from +/// `main_step4.dart`. +Future main() async { + final client = await connectTutorialUser(); + + runApp(MyApp(client: client)); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key, required this.client}); + + /// The client created in `main`. Holds the connection and the local cache. + final StreamChatClient client; + + @override + Widget build(BuildContext context) { + /// One brand color per brightness. Stream derives its whole semantic + /// palette from the swatch, so this single value restyles bubbles, + /// sending indicators, unread badges, and the composer cursor. + final brand = StreamColorSwatch.fromColor(Colors.green); + final brandDark = StreamColorSwatch.fromColor( + Colors.green, + brightness: Brightness.dark, + ); + + /// Per-widget override, merged on top of the derived palette. Reusing + /// `brand.shade100` is what keeps the tiles and the message bubbles in + /// the same green family. + final customTheme = StreamChatThemeData( + channelListItemTheme: StreamChannelListItemThemeData( + titleStyle: const TextStyle(fontWeight: FontWeight.bold), + backgroundColor: WidgetStateProperty.all(brand.shade100), + ), + ); + + return MaterialApp( + theme: ThemeData( + brightness: Brightness.light, + extensions: [ + StreamTheme( + brightness: Brightness.light, + colorScheme: StreamColorScheme.light(brand: brand), + ), + ], + ), + darkTheme: ThemeData( + brightness: Brightness.dark, + extensions: [ + StreamTheme( + brightness: Brightness.dark, + colorScheme: StreamColorScheme.dark(brand: brandDark), + ), + ], + ), + builder: (context, child) => StreamChat( + client: client, + themeData: customTheme, + child: child, + ), + home: const ChannelListPage(), + ); + } +} diff --git a/packages/stream_chat_flutter/example/lib/tutorial/main_step6.dart b/packages/stream_chat_flutter/example/lib/tutorial/main_step6.dart new file mode 100644 index 0000000000..6a571c79a8 --- /dev/null +++ b/packages/stream_chat_flutter/example/lib/tutorial/main_step6.dart @@ -0,0 +1,89 @@ +// ignore_for_file: public_member_api_docs +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_flutter_example/tutorial/channel_list_page.dart'; +import 'package:stream_chat_flutter_example/tutorial/client.dart'; +import 'package:stream_chat_flutter_example/tutorial/rounded_avatar.dart'; + +/// Step 6 of the +/// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/) - +/// the themed app, with one widget swapped out. +/// +/// Run with: `flutter run -t lib/tutorial/main_step6.dart` +/// +/// Theming changes tokens (colors, fonts, shapes). To change an actual +/// *widget*, register a component builder and override only the slot you want - +/// every other widget keeps its default. Here [RoundedAvatar] replaces the +/// circular avatar with a rounded square. +/// +/// `avatar` is one of the shared slots you pass directly. Chat-specific ones - +/// `messageItem`, `channelListItem`, `messageComposer` - go through +/// `extensions: streamChatComponentBuilders(...)` instead. +/// +/// Component builders resolve through the factory, so they reach inside +/// [StreamChannelPage] too even though it owns its own header, list, and +/// composer. Only `MyApp` changes from `main_step5.dart`. +Future main() async { + final client = await connectTutorialUser(); + + runApp(MyApp(client: client)); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key, required this.client}); + + /// The client created in `main`. Holds the connection and the local cache. + final StreamChatClient client; + + @override + Widget build(BuildContext context) { + /// One brand color per brightness. Stream derives its whole semantic + /// palette from the swatch, so this single value restyles bubbles, + /// sending indicators, unread badges, and the composer cursor. + final brand = StreamColorSwatch.fromColor(Colors.green); + final brandDark = StreamColorSwatch.fromColor( + Colors.green, + brightness: Brightness.dark, + ); + + /// Per-widget override, merged on top of the derived palette. Reusing + /// `brand.shade100` is what keeps the tiles and the message bubbles in + /// the same green family. + final customTheme = StreamChatThemeData( + channelListItemTheme: StreamChannelListItemThemeData( + titleStyle: const TextStyle(fontWeight: FontWeight.bold), + backgroundColor: WidgetStateProperty.all(brand.shade100), + ), + ); + + return MaterialApp( + theme: ThemeData( + brightness: Brightness.light, + extensions: [ + StreamTheme( + brightness: Brightness.light, + colorScheme: StreamColorScheme.light(brand: brand), + ), + ], + ), + darkTheme: ThemeData( + brightness: Brightness.dark, + extensions: [ + StreamTheme( + brightness: Brightness.dark, + colorScheme: StreamColorScheme.dark(brand: brandDark), + ), + ], + ), + builder: (context, child) => StreamChat( + client: client, + themeData: customTheme, + componentBuilders: StreamComponentBuilders( + avatar: (context, props) => RoundedAvatar(props: props), + ), + child: child, + ), + home: const ChannelListPage(), + ); + } +} diff --git a/packages/stream_chat_flutter/example/lib/tutorial/rounded_avatar.dart b/packages/stream_chat_flutter/example/lib/tutorial/rounded_avatar.dart new file mode 100644 index 0000000000..724db29a98 --- /dev/null +++ b/packages/stream_chat_flutter/example/lib/tutorial/rounded_avatar.dart @@ -0,0 +1,41 @@ +// ignore_for_file: public_member_api_docs +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +/// A rounded-square avatar to replace the SDK's circular one. +/// +/// From Step 6 of the +/// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/). +/// Registered on the `avatar` component-builder slot in `main_step6.dart`. +/// Because `avatar` is a single global slot, the change lands in the message +/// rows, the channel list, and the headers at once - including inside +/// [StreamChannelPage], which owns those widgets itself. +class RoundedAvatar extends StatelessWidget { + const RoundedAvatar({super.key, required this.props}); + + /// Everything the SDK would have used to draw the default avatar. + final StreamAvatarProps props; + + @override + Widget build(BuildContext context) { + final imageUrl = props.imageUrl; + final size = props.size?.value ?? StreamAvatarSize.lg.value; + + return ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(8)), + child: SizedBox.square( + dimension: size, + child: ColoredBox( + color: props.backgroundColor ?? context.streamColorScheme.backgroundApp, + child: imageUrl == null + ? Center(child: props.placeholder(context)) + : Image.network( + imageUrl, + fit: BoxFit.cover, + errorBuilder: (context, _, _) => Center(child: props.placeholder(context)), + ), + ), + ), + ); + } +} diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_1.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_1.dart deleted file mode 100644 index c3984888a9..0000000000 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_1.dart +++ /dev/null @@ -1,117 +0,0 @@ -// ignore_for_file: public_member_api_docs -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 [StreamChatClient] is initialized with your API Key -/// 2. The current user is set by calling [StreamChatClient.connectUser] -/// 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 that you use a mobile device. -/// -/// Let's have a look at what we've built: -/// -/// - We set up the Chat [StreamChatClient] with the API key -/// -/// - We set the current user for Chat with [StreamChatClient.connectUser] -/// 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: [StreamChannelHeader], [StreamMessageListView] -/// and [StreamMessageComposer] -/// -/// If you now run the simulator you will see a single channel UI. -Future main() async { - final client = StreamChatClient( - 'b67pax5b2wdq', - logLevel: Level.INFO, - ); - - await client.connectUser( - User(id: 'tutorial-flutter'), - '''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidHV0b3JpYWwtZmx1dHRlciJ9.S-MJpoSwDiqyXpUURgO5wVqJ4vKlIVFLSEyrFYCOE1c''', - ); - - final channel = client.channel( - 'messaging', - id: 'flutterdevs', - extraData: const { - 'members': ['tutorial-flutter'], - }, - ); - - await channel.watch(); - - runApp( - MyApp( - client: client, - channel: channel, - ), - ); -} - -class MyApp extends StatelessWidget { - const MyApp({ - 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 in. - final Channel channel; - - @override - Widget build(BuildContext context) { - return MaterialApp( - builder: (context, widget) { - return StreamChat( - client: client, - child: widget, - ); - }, - home: StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ); - } -} - -/// Displays the list of messages inside the channel. -class ChannelPage extends StatelessWidget { - const ChannelPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelHeader(), - body: Column( - children: [ - const Expanded( - child: StreamMessageListView(), - ), - StreamMessageComposer(), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_2.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_2.dart deleted file mode 100644 index 2d41e0938a..0000000000 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_2.dart +++ /dev/null @@ -1,142 +0,0 @@ -// ignore_for_file: public_member_api_docs -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 [StreamChannel] and [StreamChannelListView]. -/// -/// 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 in which the current user is a member and we order them based -/// on the time they had a new message. -/// [StreamChannelListView] handles pagination -/// and updates automatically when new channels are created or when a new -/// message is added to a channel. -Future main() async { - final client = StreamChatClient( - 'b67pax5b2wdq', - logLevel: Level.INFO, - ); - - await client.connectUser( - User(id: 'tutorial-flutter'), - '''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidHV0b3JpYWwtZmx1dHRlciJ9.S-MJpoSwDiqyXpUURgO5wVqJ4vKlIVFLSEyrFYCOE1c''', - ); - - runApp( - MyApp( - client: client, - ), - ); -} - -class MyApp extends StatelessWidget { - const MyApp({ - super.key, - required this.client, - }); - - /// Instance of [StreamChatClient] we created earlier. This contains - /// information about our application and connection state. - final StreamChatClient client; - - @override - Widget build(BuildContext context) { - return MaterialApp( - builder: (context, child) => StreamChat( - client: client, - child: child, - ), - home: const ChannelListPage(), - ); - } -} - -/// Displays the list of channels for the current user. -class ChannelListPage extends StatefulWidget { - const ChannelListPage({super.key}); - - @override - State createState() => _ChannelListPageState(); -} - -class _ChannelListPageState extends State { - late final _controller = StreamChannelListController( - client: StreamChat.of(context).client, - filter: Filter.in_( - 'members', - [StreamChat.of(context).currentUser!.id], - ), - channelStateSort: const [SortOption.desc('last_message_at')], - ); - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelListHeader(), - body: StreamChannelListView( - controller: _controller, - onChannelTap: (channel) => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ), - ), - ), - ); - } -} - -/// Displays the list of messages inside the channel. -class ChannelPage extends StatelessWidget { - const ChannelPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelHeader(), - body: Column( - children: [ - const Expanded( - child: StreamMessageListView(), - ), - StreamMessageComposer(), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_3.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_3.dart deleted file mode 100644 index 312ce5c72b..0000000000 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_3.dart +++ /dev/null @@ -1,196 +0,0 @@ -// ignore_for_file: public_member_api_docs -import 'package:collection/collection.dart' show IterableExtension; -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 [StreamChannelListView.itemBuilder]; -/// this will override the default [StreamChannelListItem] 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] -Future main() async { - final client = StreamChatClient( - 'b67pax5b2wdq', - logLevel: Level.INFO, - ); - - await client.connectUser( - User(id: 'tutorial-flutter'), - '''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidHV0b3JpYWwtZmx1dHRlciJ9.S-MJpoSwDiqyXpUURgO5wVqJ4vKlIVFLSEyrFYCOE1c''', - ); - - runApp( - MyApp( - client: client, - ), - ); -} - -class MyApp extends StatelessWidget { - const MyApp({ - super.key, - required this.client, - }); - - /// Instance of [StreamChatClient] we created earlier. This contains - /// information about our application and connection state. - final StreamChatClient client; - - @override - Widget build(BuildContext context) { - return MaterialApp( - builder: (context, child) => StreamChat( - client: client, - child: child, - ), - home: const ChannelListPage(), - ); - } -} - -/// Displays the list of channels for the current user. -class ChannelListPage extends StatefulWidget { - const ChannelListPage({ - super.key, - }); - - @override - State createState() => _ChannelListPageState(); -} - -class _ChannelListPageState extends State { - late final _listController = StreamChannelListController( - client: StreamChat.of(context).client, - filter: Filter.in_( - 'members', - [StreamChat.of(context).currentUser!.id], - ), - channelStateSort: const [SortOption.desc('last_message_at')], - limit: 20, - ); - - @override - void dispose() { - _listController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelListHeader(), - body: StreamChannelListView( - controller: _listController, - itemBuilder: _channelTileBuilder, - onChannelTap: (channel) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) { - return StreamChannel( - channel: channel, - child: const ChannelPage(), - ); - }, - ), - ); - }, - ), - ); - } - - Widget _channelTileBuilder( - BuildContext context, - List channels, - int index, - StreamChannelListItem defaultChannelListItem, - ) { - final channel = channels[index]; - final lastMessage = channel.state?.messages.reversed.firstWhereOrNull( - (message) => !message.isDeleted, - ); - - final subtitle = lastMessage == null ? 'nothing yet' : lastMessage.text!; - final unreadCount = channel.state?.unreadCount ?? 0; - final opacity = unreadCount > 0 ? 1.0 : 0.5; - - return ListTile( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ), - ); - }, - leading: StreamChannelAvatar( - channel: channel, - ), - title: StreamChannelName( - channel: channel, - textStyle: StreamChannelListItemTheme.of(context).titleStyle?.copyWith( - color: context.streamColorScheme.textPrimary.withValues(alpha: opacity), - ), - ), - subtitle: Text(subtitle), - trailing: unreadCount > 0 - ? CircleAvatar( - radius: 10, - child: Text(unreadCount.toString()), - ) - : const SizedBox(), - ); - } -} - -/// Displays the list of messages inside the channel. -class ChannelPage extends StatelessWidget { - const ChannelPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelHeader(), - body: Column( - children: [ - const Expanded( - child: StreamMessageListView(), - ), - StreamMessageComposer(), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_4.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_4.dart deleted file mode 100644 index 02855ef59e..0000000000 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_4.dart +++ /dev/null @@ -1,185 +0,0 @@ -// ignore_for_file: public_member_api_docs -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 [StreamMessageListView] -/// to another widget that renders the widget. -/// To make this simple, such a widget only needs -/// to build [StreamMessageListView] -/// 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]. -Future main() async { - final client = StreamChatClient( - 'b67pax5b2wdq', - logLevel: Level.INFO, - ); - - await client.connectUser( - User(id: 'tutorial-flutter'), - '''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidHV0b3JpYWwtZmx1dHRlciJ9.S-MJpoSwDiqyXpUURgO5wVqJ4vKlIVFLSEyrFYCOE1c''', - ); - - runApp( - MyApp( - client: client, - ), - ); -} - -class MyApp extends StatelessWidget { - const MyApp({ - super.key, - required this.client, - }); - - /// Instance of [StreamChatClient] we created earlier. This contains - /// information about our application and connection state. - final StreamChatClient client; - - @override - Widget build(BuildContext context) { - return MaterialApp( - builder: (context, child) => StreamChat( - client: client, - child: child, - ), - home: const ChannelListPage(), - ); - } -} - -/// Displays the list of channels for the current user. -class ChannelListPage extends StatefulWidget { - const ChannelListPage({ - super.key, - }); - - @override - State createState() => _ChannelListPageState(); -} - -class _ChannelListPageState extends State { - late final _listController = StreamChannelListController( - client: StreamChat.of(context).client, - filter: Filter.in_( - 'members', - [StreamChat.of(context).currentUser!.id], - ), - channelStateSort: const [SortOption.desc('last_message_at')], - limit: 20, - ); - - @override - void dispose() { - _listController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelListHeader(), - body: StreamChannelListView( - controller: _listController, - onChannelTap: (channel) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ), - ); - }, - ), - ); - } -} - -/// Displays the list of messages inside the channel. -class ChannelPage extends StatelessWidget { - const ChannelPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelHeader(), - body: Column( - children: [ - Expanded( - child: StreamMessageListView( - threadBuilder: (_, parentMessage) => ThreadPage( - parent: parentMessage!, - ), - ), - ), - StreamMessageComposer(), - ], - ), - ); - } -} - -/// Displays the thread replies for a parent message. -class ThreadPage extends StatefulWidget { - const ThreadPage({ - super.key, - required this.parent, - }); - - /// The root message this thread is replying to. - final Message parent; - - @override - State createState() => _ThreadPageState(); -} - -class _ThreadPageState extends State { - late final _controller = StreamMessageComposerController( - message: Message(parentId: widget.parent.id), - ); - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: StreamThreadHeader(parent: widget.parent), - body: Column( - children: [ - Expanded( - child: StreamMessageListView( - parentMessage: widget.parent, - ), - ), - StreamMessageComposer( - messageComposerController: _controller, - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_5.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_5.dart deleted file mode 100644 index d66d0c2f60..0000000000 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_5.dart +++ /dev/null @@ -1,174 +0,0 @@ -// ignore_for_file: public_member_api_docs -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. -/// -/// Replacing the built-in message component with your own is done by -/// passing a `messageBuilder` to [StreamMessageListView]. -/// -/// The builder receives the [BuildContext], the [Message], and the -/// pre-configured [StreamMessageItemProps] with all list-level callbacks -/// already wired in. -/// -/// 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 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]. -Future main() async { - final client = StreamChatClient( - 'b67pax5b2wdq', - logLevel: Level.INFO, - ); - - await client.connectUser( - User(id: 'tutorial-flutter'), - '''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidHV0b3JpYWwtZmx1dHRlciJ9.S-MJpoSwDiqyXpUURgO5wVqJ4vKlIVFLSEyrFYCOE1c''', - ); - - runApp( - MyApp( - client: client, - ), - ); -} - -class MyApp extends StatelessWidget { - const MyApp({ - super.key, - required this.client, - }); - - /// Instance of [StreamChatClient] we created earlier. This contains - /// information about our application and connection state. - final StreamChatClient client; - - @override - Widget build(BuildContext context) { - return MaterialApp( - builder: (context, child) => StreamChat( - client: client, - child: child, - ), - home: const ChannelListPage(), - ); - } -} - -/// Displays the list of channels for the current user. -class ChannelListPage extends StatefulWidget { - const ChannelListPage({ - super.key, - }); - - @override - State createState() => _ChannelListPageState(); -} - -class _ChannelListPageState extends State { - late final _listController = StreamChannelListController( - client: StreamChat.of(context).client, - filter: Filter.in_( - 'members', - [StreamChat.of(context).currentUser!.id], - ), - channelStateSort: const [SortOption.desc('last_message_at')], - limit: 20, - ); - - @override - void dispose() { - _listController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelListHeader(), - body: StreamChannelListView( - controller: _listController, - onChannelTap: (channel) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ), - ); - }, - ), - ); - } -} - -/// Displays the list of messages inside the channel with a custom message widget. -class ChannelPage extends StatelessWidget { - const ChannelPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelHeader(), - body: Column( - children: [ - Expanded( - child: StreamMessageListView( - messageBuilder: _messageItemBuilder, - ), - ), - StreamMessageComposer(), - ], - ), - ); - } - - Widget _messageItemBuilder( - BuildContext context, - Message message, - StreamMessageItemProps defaultProps, - ) { - final isCurrentUser = StreamChat.of(context).currentUser!.id == message.user!.id; - final textAlign = isCurrentUser ? TextAlign.right : TextAlign.left; - final color = isCurrentUser ? Colors.blueGrey : Colors.blue; - - return Padding( - padding: const EdgeInsets.all(5), - child: DecoratedBox( - decoration: BoxDecoration( - border: Border.all( - color: color, - ), - borderRadius: const BorderRadius.all( - Radius.circular(5), - ), - ), - child: ListTile( - title: Text( - message.text!, - textAlign: textAlign, - ), - subtitle: Text( - message.user!.name, - textAlign: textAlign, - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart b/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart deleted file mode 100644 index 7557edc252..0000000000 --- a/packages/stream_chat_flutter/example/lib/tutorial_part_6.dart +++ /dev/null @@ -1,223 +0,0 @@ -// ignore_for_file: public_member_api_docs -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 ships fully designed widgets that you can theme to match -/// your app. Theming works in two layers: -/// -/// 1. Design tokens — a [StreamTheme] registered as a [ThemeData] extension. -/// Set a brand color here and Stream derives the rest of its semantic -/// palette from the swatch automatically. -/// 2. Per-widget overrides — a [StreamChatThemeData] passed via -/// [StreamChat.themeData]. Tweak individual components without touching -/// the rest of the theme. -/// -/// First, we register a [StreamTheme] on both [MaterialApp.theme] and -/// [MaterialApp.darkTheme] with a custom green brand swatch. Message -/// bubbles, sending indicators, unread badges, the composer cursor, and -/// other accents pick up the new tone in one go. -/// -/// On top of that, we build a [StreamChatThemeData] override for -/// [StreamChatThemeData.channelListItemTheme]: bold titles and a -/// light-green tile background that reuses `greenBrand.shade100`, the -/// same shade Stream uses for outgoing message bubbles, so the channel -/// list and the message list share the same green tone. -Future main() async { - final client = StreamChatClient( - 'b67pax5b2wdq', - logLevel: Level.INFO, - ); - - await client.connectUser( - User(id: 'tutorial-flutter'), - '''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidHV0b3JpYWwtZmx1dHRlciJ9.S-MJpoSwDiqyXpUURgO5wVqJ4vKlIVFLSEyrFYCOE1c''', - ); - - runApp( - MyApp( - client: client, - ), - ); -} - -class MyApp extends StatelessWidget { - const MyApp({ - super.key, - required this.client, - }); - - /// Instance of [StreamChatClient] we created earlier. This contains - /// information about our application and connection state. - final StreamChatClient client; - - @override - Widget build(BuildContext context) { - final greenBrand = StreamColorSwatch.fromColor(Colors.green); - final greenBrandDark = StreamColorSwatch.fromColor(Colors.green, brightness: Brightness.dark); - final customTheme = StreamChatThemeData( - channelListItemTheme: StreamChannelListItemThemeData( - titleStyle: const TextStyle(fontWeight: FontWeight.bold), - backgroundColor: WidgetStateProperty.all(greenBrand.shade100), - ), - ); - - return MaterialApp( - theme: ThemeData( - brightness: Brightness.light, - extensions: [ - StreamTheme( - brightness: Brightness.light, - colorScheme: StreamColorScheme.light(brand: greenBrand), - ), - ], - ), - darkTheme: ThemeData( - brightness: Brightness.dark, - extensions: [ - StreamTheme( - brightness: Brightness.dark, - colorScheme: StreamColorScheme.dark(brand: greenBrandDark), - ), - ], - ), - builder: (context, child) => StreamChat( - client: client, - themeData: customTheme, - child: child, - ), - home: const ChannelListPage(), - ); - } -} - -/// Displays the list of channels for the current user. -class ChannelListPage extends StatefulWidget { - const ChannelListPage({ - super.key, - }); - - @override - State createState() => _ChannelListPageState(); -} - -class _ChannelListPageState extends State { - late final _listController = StreamChannelListController( - client: StreamChat.of(context).client, - filter: Filter.in_( - 'members', - [StreamChat.of(context).currentUser!.id], - ), - channelStateSort: const [SortOption.desc('last_message_at')], - limit: 20, - ); - - @override - void dispose() { - _listController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelListHeader(), - body: StreamChannelListView( - controller: _listController, - onChannelTap: (channel) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ), - ); - }, - ), - ); - } -} - -/// Displays the list of messages inside the channel. -class ChannelPage extends StatelessWidget { - const ChannelPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: const StreamChannelHeader(), - body: Column( - children: [ - Expanded( - child: StreamMessageListView( - threadBuilder: (_, parentMessage) => ThreadPage( - parent: parentMessage!, - ), - ), - ), - StreamMessageComposer(), - ], - ), - ); - } -} - -/// Displays the thread replies for a parent message. -class ThreadPage extends StatefulWidget { - const ThreadPage({ - super.key, - required this.parent, - }); - - /// The root message this thread is replying to. - final Message parent; - - @override - State createState() => _ThreadPageState(); -} - -class _ThreadPageState extends State { - late final _controller = StreamMessageComposerController( - message: Message(parentId: widget.parent.id), - ); - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final colorScheme = context.streamColorScheme; - - return Scaffold( - backgroundColor: colorScheme.backgroundApp, - appBar: StreamThreadHeader( - parent: widget.parent, - ), - body: Column( - children: [ - Expanded( - child: StreamMessageListView( - parentMessage: widget.parent, - ), - ), - StreamMessageComposer( - messageComposerController: _controller, - ), - ], - ), - ); - } -} From 4e085fc8440221422de38ea690fa282216eca850 Mon Sep 17 00:00:00 2001 From: VelikovPetar Date: Mon, 24 Aug 2026 14:18:40 +0200 Subject: [PATCH 2/2] docs(repo): flatten the tutorial example files with a `tutorial_` prefix Co-Authored-By: Claude Opus 5 --- ..._list_page.dart => tutorial_channel_list_page.dart} | 0 .../lib/{tutorial/client.dart => tutorial_client.dart} | 6 +++--- .../main_step4.dart => tutorial_main_step4.dart} | 8 ++++---- .../main_step5.dart => tutorial_main_step5.dart} | 8 ++++---- .../main_step6.dart => tutorial_main_step6.dart} | 10 +++++----- ...ounded_avatar.dart => tutorial_rounded_avatar.dart} | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) rename packages/stream_chat_flutter/example/lib/{tutorial/channel_list_page.dart => tutorial_channel_list_page.dart} (100%) rename packages/stream_chat_flutter/example/lib/{tutorial/client.dart => tutorial_client.dart} (89%) rename packages/stream_chat_flutter/example/lib/{tutorial/main_step4.dart => tutorial_main_step4.dart} (81%) rename packages/stream_chat_flutter/example/lib/{tutorial/main_step5.dart => tutorial_main_step5.dart} (92%) rename packages/stream_chat_flutter/example/lib/{tutorial/main_step6.dart => tutorial_main_step6.dart} (90%) rename packages/stream_chat_flutter/example/lib/{tutorial/rounded_avatar.dart => tutorial_rounded_avatar.dart} (94%) diff --git a/packages/stream_chat_flutter/example/lib/tutorial/channel_list_page.dart b/packages/stream_chat_flutter/example/lib/tutorial_channel_list_page.dart similarity index 100% rename from packages/stream_chat_flutter/example/lib/tutorial/channel_list_page.dart rename to packages/stream_chat_flutter/example/lib/tutorial_channel_list_page.dart diff --git a/packages/stream_chat_flutter/example/lib/tutorial/client.dart b/packages/stream_chat_flutter/example/lib/tutorial_client.dart similarity index 89% rename from packages/stream_chat_flutter/example/lib/tutorial/client.dart rename to packages/stream_chat_flutter/example/lib/tutorial_client.dart index e5933a1a49..681ba25fd1 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial/client.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_client.dart @@ -4,10 +4,10 @@ import 'package:stream_chat_persistence/stream_chat_persistence.dart'; /// Credentials and client setup shared by the tutorial entry points. /// -/// This mirrors the top of `lib/main.dart` in Step 4 of the +/// This mirrors the top of the tutorial's own `main.dart` in Step 4 of the /// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/). -/// The three `main_step*.dart` entry points differ only in `MyApp`, so the -/// setup lives here rather than being repeated in each of them. +/// The three `tutorial_main_step*.dart` entry points differ only in `MyApp`, +/// so the setup lives here rather than being repeated in each of them. /// Credentials from Step 3 of the tutorial. /// - API key: `getstream env --target flutter` writes it to `dart_defines.json`, diff --git a/packages/stream_chat_flutter/example/lib/tutorial/main_step4.dart b/packages/stream_chat_flutter/example/lib/tutorial_main_step4.dart similarity index 81% rename from packages/stream_chat_flutter/example/lib/tutorial/main_step4.dart rename to packages/stream_chat_flutter/example/lib/tutorial_main_step4.dart index 586251dc1f..2446401a3e 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial/main_step4.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_main_step4.dart @@ -1,20 +1,20 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_flutter_example/tutorial/channel_list_page.dart'; -import 'package:stream_chat_flutter_example/tutorial/client.dart'; +import 'package:stream_chat_flutter_example/tutorial_channel_list_page.dart'; +import 'package:stream_chat_flutter_example/tutorial_client.dart'; /// Step 4 of the /// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/) - /// a working app on the default theme. /// -/// Run with: `flutter run -t lib/tutorial/main_step4.dart` +/// Run with: `flutter run -t lib/tutorial_main_step4.dart` /// /// Two screens are all you write: the channel list, and the client setup that /// puts [StreamChat] above the app. The conversation itself is /// [StreamChannelPage], which the list navigates to. /// -/// `main_step5.dart` and `main_step6.dart` differ from this file only in +/// `tutorial_main_step5.dart` and `tutorial_main_step6.dart` differ from this file only in /// `MyApp` - that is the whole surface theming and component overrides touch. Future main() async { final client = await connectTutorialUser(); diff --git a/packages/stream_chat_flutter/example/lib/tutorial/main_step5.dart b/packages/stream_chat_flutter/example/lib/tutorial_main_step5.dart similarity index 92% rename from packages/stream_chat_flutter/example/lib/tutorial/main_step5.dart rename to packages/stream_chat_flutter/example/lib/tutorial_main_step5.dart index 9b94051f21..5175384854 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial/main_step5.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_main_step5.dart @@ -1,14 +1,14 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_flutter_example/tutorial/channel_list_page.dart'; -import 'package:stream_chat_flutter_example/tutorial/client.dart'; +import 'package:stream_chat_flutter_example/tutorial_channel_list_page.dart'; +import 'package:stream_chat_flutter_example/tutorial_client.dart'; /// Step 5 of the /// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/) - /// the same app, themed. /// -/// Run with: `flutter run -t lib/tutorial/main_step5.dart` +/// Run with: `flutter run -t lib/tutorial_main_step5.dart` /// /// Theming works in two layers, and you rarely need more than the first: /// @@ -21,7 +21,7 @@ import 'package:stream_chat_flutter_example/tutorial/client.dart'; /// /// Both land inside [StreamChannelPage] as well, since it resolves the ambient /// theme like any other Stream widget. Only `MyApp` changes from -/// `main_step4.dart`. +/// `tutorial_main_step4.dart`. Future main() async { final client = await connectTutorialUser(); diff --git a/packages/stream_chat_flutter/example/lib/tutorial/main_step6.dart b/packages/stream_chat_flutter/example/lib/tutorial_main_step6.dart similarity index 90% rename from packages/stream_chat_flutter/example/lib/tutorial/main_step6.dart rename to packages/stream_chat_flutter/example/lib/tutorial_main_step6.dart index 6a571c79a8..ef2a8f4576 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial/main_step6.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_main_step6.dart @@ -1,15 +1,15 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_flutter_example/tutorial/channel_list_page.dart'; -import 'package:stream_chat_flutter_example/tutorial/client.dart'; -import 'package:stream_chat_flutter_example/tutorial/rounded_avatar.dart'; +import 'package:stream_chat_flutter_example/tutorial_channel_list_page.dart'; +import 'package:stream_chat_flutter_example/tutorial_client.dart'; +import 'package:stream_chat_flutter_example/tutorial_rounded_avatar.dart'; /// Step 6 of the /// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/) - /// the themed app, with one widget swapped out. /// -/// Run with: `flutter run -t lib/tutorial/main_step6.dart` +/// Run with: `flutter run -t lib/tutorial_main_step6.dart` /// /// Theming changes tokens (colors, fonts, shapes). To change an actual /// *widget*, register a component builder and override only the slot you want - @@ -22,7 +22,7 @@ import 'package:stream_chat_flutter_example/tutorial/rounded_avatar.dart'; /// /// Component builders resolve through the factory, so they reach inside /// [StreamChannelPage] too even though it owns its own header, list, and -/// composer. Only `MyApp` changes from `main_step5.dart`. +/// composer. Only `MyApp` changes from `tutorial_main_step5.dart`. Future main() async { final client = await connectTutorialUser(); diff --git a/packages/stream_chat_flutter/example/lib/tutorial/rounded_avatar.dart b/packages/stream_chat_flutter/example/lib/tutorial_rounded_avatar.dart similarity index 94% rename from packages/stream_chat_flutter/example/lib/tutorial/rounded_avatar.dart rename to packages/stream_chat_flutter/example/lib/tutorial_rounded_avatar.dart index 724db29a98..88424344e0 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial/rounded_avatar.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial_rounded_avatar.dart @@ -6,7 +6,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// /// From Step 6 of the /// [Flutter Chat tutorial](https://getstream.io/chat/sdk/flutter/tutorial/). -/// Registered on the `avatar` component-builder slot in `main_step6.dart`. +/// Registered on the `avatar` component-builder slot in `tutorial_main_step6.dart`. /// Because `avatar` is a single global slot, the change lands in the message /// rows, the channel list, and the headers at once - including inside /// [StreamChannelPage], which owns those widgets itself.