Complete example

- Update code
- Add docs
This commit is contained in:
Neevash Ramdial
2021-01-27 22:14:14 -04:00
parent fa6e6aff17
commit 303abee427
@@ -28,19 +28,31 @@ Future<void> main() async {
); );
} }
/// Example application using Stream Chat core widgets.
/// Stream Chat Core is a set of Flutter wrappers which provide basic functionality
/// for building Flutter applications using Stream.
/// If you'd prefer using pre-made UI widgets for your app, please see our other
/// package, `stream_chat_flutter`.
class StreamExample extends StatelessWidget { class StreamExample extends StatelessWidget {
/// Minimal example using Stream's core Flutter package.
/// If you'd prefer using pre-made UI widgets for your app, please see our other
/// package, `stream_chat_flutter`.
const StreamExample({ const StreamExample({
Key key, Key key,
@required this.client, @required this.client,
}) : super(key: key); }) : super(key: key);
/// Instance of Stream Client.
/// Stream's [Client] can be used to connect to our servers and set the default
/// user for the application. Performing these actions trigger a websocket connection
/// allowing for real-time updates.
final Client client; final Client client;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'Stream Chat Dart Example', title: 'Stream Chat Core Example',
home: _HomeScreen(), home: HomeScreen(),
builder: (context, child) => StreamChatCore( builder: (context, child) => StreamChatCore(
client: client, client: client,
child: ChannelsBloc(child: child), child: ChannelsBloc(child: child),
@@ -49,7 +61,12 @@ class StreamExample extends StatelessWidget {
} }
} }
class _HomeScreen extends StatelessWidget { /// Basic layout displaying a list of [Channel]s the user is a part of.
/// This is implemented using [ChannelListCore].
///
/// [ChannelListCore] is a `builder` with callbacks for constructing UIs based
/// on different scenarios.
class HomeScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -81,15 +98,21 @@ class _HomeScreen extends StatelessWidget {
ListView.builder( ListView.builder(
itemCount: channels.length, itemCount: channels.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final item = channels[index]; final _item = channels[index];
return ListTile( return ListTile(
title: Text(item.name), title: Text(_item.name),
subtitle: Text(item.state.lastMessage.text), subtitle: Text(_item.state.lastMessage.text),
onTap: () { onTap: () {
/// Display a list of messages when the user taps on an item.
/// We can use [StreamChannel] to wrap our [MessageScreen] screen
/// with the selected channel.
///
/// This allows us to use a built-in inherited widget for accessing
/// our `channel` later on.
Navigator.of(context).push( Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => StreamChannel( builder: (context) => StreamChannel(
channel: item, channel: _item,
child: MessageScreen(), child: MessageScreen(),
), ),
), ),
@@ -103,6 +126,12 @@ class _HomeScreen extends StatelessWidget {
} }
} }
/// A list of messages sent in the current channel.
/// When a user taps on a channel in [HomeScreen], a navigator push [MessageScreen]
/// to display the list of messages in the selected channel.
///
/// This is implemented using [MessageListCore], a convenience builder with
/// callbacks for building UIs based on different api results.
class MessageScreen extends StatefulWidget { class MessageScreen extends StatefulWidget {
@override @override
_MessageScreenState createState() => _MessageScreenState(); _MessageScreenState createState() => _MessageScreenState();
@@ -136,108 +165,121 @@ class _MessageScreenState extends State<MessageScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
/// To access the current channel, we can use the `.of()` method on [StreamChannel]
/// to fetch the closest instance.
final channel = StreamChannel.of(context).channel;
return Scaffold( return Scaffold(
body: Column( appBar: AppBar(
children: [ title: Text(channel.name),
Expanded( ),
child: MessageListCore( body: SafeArea(
emptyBuilder: (BuildContext context) { child: Column(
return Center( children: [
child: Text('Looks like you are not in any channels'), Expanded(
); child: MessageListCore(
}, emptyBuilder: (BuildContext context) {
loadingBuilder: (BuildContext context) { return Center(
return Center( child: Text('Looks like you are not in any channels'),
child: SizedBox( );
height: 100.0, },
width: 100.0, loadingBuilder: (BuildContext context) {
child: CircularProgressIndicator(), return Center(
), child: SizedBox(
); height: 100.0,
}, width: 100.0,
messageListBuilder: child: CircularProgressIndicator(),
(BuildContext context, List<Message> messages) {
return ListView.builder(
controller: _scrollController,
itemCount: messages.length,
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = messages[index];
final client = StreamChatCore.of(context).client;
if (item.user.id == client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.text),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.text),
),
);
}
},
);
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Enter your message',
), ),
), );
), },
Material( messageListBuilder: (
type: MaterialType.circle, BuildContext context,
color: Colors.blue, List<Message> messages,
clipBehavior: Clip.hardEdge, ) {
child: InkWell( return ListView.builder(
onTap: () async { controller: _scrollController,
if (_controller.value.text.isNotEmpty) { itemCount: messages.length,
//TODO(Nash): Send Message reverse: true,
// await widget.channel.sendMessage( itemBuilder: (BuildContext context, int index) {
// Message(text: _controller.value.text), final item = messages[index];
// ); final client = StreamChatCore.of(context).client;
_controller.clear(); if (item.user.id == client.uid) {
_updateList(); return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.text),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.text),
),
);
} }
}, },
child: const Padding( );
padding: EdgeInsets.all(8.0), },
child: Center( ),
child: Icon( ),
Icons.send, Padding(
color: Colors.white, padding: const EdgeInsets.all(8.0),
), 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 {
if (_controller.value.text.isNotEmpty) {
await channel.sendMessage(
Message(text: _controller.value.text),
);
_controller.clear();
_updateList();
}
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: Icon(
Icons.send,
color: Colors.white,
),
),
),
),
)
],
),
)
],
),
), ),
); );
} }
} }
/// Extensions can be used to add functionality to the SDK. In the examples
/// below, we add two simple extensions to the [Client] and [Channel].
extension on Client { extension on Client {
/// Fetches the current user id.
String get uid => state.user.id; String get uid => state.user.id;
} }
extension on Channel { extension on Channel {
/// Fetches the name of the channel by accessing [extraData] or [cid].
String get name { String get name {
final _channelName = extraData['name']; final _channelName = extraData['name'];
if (_channelName != null) { if (_channelName != null) {