fix(doc): fix broken documentation snippets

This commit is contained in:
Efthymis Sarmpanis
2023-10-11 15:05:12 +03:00
parent 31950419ee
commit b4dc9defd1
33 changed files with 222 additions and 188 deletions
@@ -21,8 +21,9 @@ provided inside the `messageBuilder` parameter of the `StreamMessageListView` li
```dart
StreamMessageListView(
messageBuilder: (context, details, messageList, defaultImpl) {
// Your implementation of the message here
// E.g: return Text(details.message.text ?? '');
return defaultWidget.copyWith(
...
);
},
),
```
@@ -91,7 +91,7 @@ StreamMessageListView(
messageBuilder: (context, messageDetails, messageList, defaultWidget) {
return defaultWidget.copyWith(
textBuilder: (context, message) {
return Text(message.text);
return Text(message.text ?? '');
},
);
},
@@ -108,14 +108,16 @@ StreamMessageListView(
messageBuilder: (context, messageDetails, messageList, defaultWidget) {
return defaultWidget.copyWith(
textBuilder: (context, message) {
final text = _replaceHashtags(message.text).replaceAll('\n', '\\\n');
final text = _replaceHashtags(message.text)?.replaceAll('\n', '\\\n');
final messageTheme = StreamChatTheme.of(context).ownMessageTheme;
if (text == null) return const SizedBox();
return MarkdownBody(
data: text,
onTapLink: (
String link,
String href,
String? href,
String title,
) {
// Do something with tapped hashtag
@@ -123,16 +125,16 @@ StreamMessageListView(
styleSheet: MarkdownStyleSheet.fromTheme(
Theme.of(context).copyWith(
textTheme: Theme.of(context).textTheme.apply(
bodyColor: messageTheme.messageText.color,
decoration: messageTheme.messageText.decoration,
decorationColor: messageTheme.messageText.decorationColor,
decorationStyle: messageTheme.messageText.decorationStyle,
fontFamily: messageTheme.messageText.fontFamily,
bodyColor: messageTheme.messageTextStyle?.color,
decoration: messageTheme.messageTextStyle?.decoration,
decorationColor: messageTheme.messageTextStyle?.decorationColor,
decorationStyle: messageTheme.messageTextStyle?.decorationStyle,
fontFamily: messageTheme.messageTextStyle?.fontFamily,
),
),
).copyWith(
a: messageTheme.messageLinks,
p: messageTheme.messageText,
a: messageTheme.messageLinksStyle,
p: messageTheme.messageTextStyle,
),
);
},
@@ -140,13 +142,16 @@ StreamMessageListView(
},
)
String _replaceHashtags(String text) {
RegExp exp = new RegExp(r"\B#\w\w+");
String? _replaceHashtags(String? text) {
if (text == null) return null;
final exp = RegExp(r"\B#\w\w+");
String result = text;
exp.allMatches(text).forEach((match){
text = text.replaceAll(
'${match.group(0)}', '[${match.group(0)}](${match.group(0).replaceAll(' ', '')})');
text = text!.replaceAll(
'${match.group(0)}', '[${match.group(0)}](${match.group(0)?.replaceAll(' ', '')})');
});
return text;
return result;
}
```
@@ -68,8 +68,8 @@ StreamMessageListView(
return defaultMessage.copyWith(
customActions: [
StreamMessageAction(
leading: Icon(Icons.add),
title: Text('Demo Action'),
leading: const Icon(Icons.add),
title: const Text('Demo Action'),
onTap: (message) {
/// Complete action here
},
@@ -35,9 +35,9 @@ Message(
text: 'This is my location',
attachments: [
Attachment(
uploadState: UploadState.success(),
uploadState: const UploadState.success(),
type: 'location',
extraData: {
extraData: const {
'latitude': 'fetched_latitude',
'longitude': 'fetched_longitude',
},
@@ -62,14 +62,13 @@ First, we add a button which when clicked fetches and shares location into the `
StreamMessageInput(
actions: [
InkWell(
child: Icon(
child: const Icon(
Icons.location_on,
size: 20.0,
color: StreamChatTheme.of(context).colorTheme.grey,
size: 20,
color: Colors.grey,
),
onTap: () {
var channel = StreamChannel.of(context).channel;
var user = StreamChat.of(context).user;
final channel = StreamChannel.of(context).channel;
_determinePosition().then((value) {
channel.sendMessage(
@@ -77,7 +76,7 @@ StreamMessageInput(
text: 'This is my location',
attachments: [
Attachment(
uploadState: UploadState.success(),
uploadState: const UploadState.success(),
type: 'location',
extraData: {
'latitude': value.latitude.toString(),
@@ -126,8 +125,7 @@ Next, we build the Static Maps URL (Add your API key before using the code snipp
```dart
String _buildMapAttachment(String lat, String long) {
var baseURL = 'https://maps.googleapis.com/maps/api/staticmap?';
var url = Uri(
final url = Uri(
scheme: 'https',
host: 'maps.googleapis.com',
port: 443,
@@ -155,8 +153,8 @@ StreamMessageListView(
'location': (context, message, attachments) {
final attachmentWidget = Image.network(
_buildMapAttachment(
attachments[0].extraData['latitude'],
attachments[0].extraData['longitude'],
attachments[0].extraData['latitude'].toString(),
attachments[0].extraData['longitude'].toString(),
),
);
@@ -194,14 +192,14 @@ First, we add the attachment when the location button is clicked:
InkWell(
child: Icon(
Icons.location_on,
size: 20.0,
color: StreamChatTheme.of(context).colorTheme.grey,
size: 20,
color: Colors.grey,
),
onTap: () {
_determinePosition().then((value) {
_messageInputController.addAttachment(
Attachment(
uploadState: UploadState.success(),
uploadState: const UploadState.success(),
type: 'location',
extraData: {
'latitude': value.latitude.toString(),
@@ -227,14 +225,14 @@ StreamMessageInput(
InkWell(
child: Icon(
Icons.location_on,
size: 20.0,
color: StreamChatTheme.of(context).colorTheme.grey,
size: 20,
color: Colors.grey,
),
onTap: () {
_determinePosition().then((value) {
_messageInputController.addAttachment(
Attachment(
uploadState: UploadState.success(),
uploadState: const UploadState.success(),
type: 'location',
extraData: {
'latitude': value.latitude.toString(),
@@ -248,16 +246,21 @@ StreamMessageInput(
},
),
],
attachmentThumbnailBuilders: {
'location': (context, attachment) {
return Image.network(
_buildMapAttachment(
attachment.extraData['latitude'],
attachment.extraData['longitude'],
),
);
},
},
mediaAttachmentBuilder: (
BuildContext context,
Attachment attachment,
ValueSetter<Attachment>? onRemovePressed,
) {
if (attachment.type == 'location') {
return Image.network(
_buildMapAttachment(
attachment.extraData['latitude'].toString(),
attachment.extraData['longitude'].toString(),
),
);
}
return const SizedBox();
},
),
```
@@ -25,11 +25,14 @@ The initial attachments can be passed to the Attachment Picker Modal in two ways
* By passing the `initialAttachments` parameter.
```dart
StreamMessageInputController _messageInputController =
StreamMessageInputController();
...
showStreamAttachmentPickerModalBottomSheet(
context: context,
initialAttachments: [
// Pass the initial attachments to the modal here if any are available already (optional)
...messageInputController.attachments,
..._messageInputController.attachments,
],
);
```
@@ -65,7 +68,7 @@ However, you can also customize the options by passing the `customOptions` param
customOptions: [
// Pass the custom attachment picker options here
AttachmentPickerOption(
icon: Icon(Icons.audiotrack),
icon: const Icon(Icons.audiotrack),
supportedTypes: [AttachmentPickerType.audios],
optionViewBuilder: (context, attachmentPickerController) {
return AudioPicker(
@@ -87,7 +90,7 @@ The size of the attachment thumbnail item shown in the gallery picker can be def
```dart
showStreamAttachmentPickerModalBottomSheet(
context: context,
attachmentThumbnailSize: ThumbnailSize.square(600),
attachmentThumbnailSize: const ThumbnailSize.square(600),
);
```
@@ -121,13 +124,13 @@ Possible values are between 0 and 100.
The scale of the attachment thumbnail item shown in the gallery picker can be defined by passing the `attachmentThumbnailScale` parameter.
For example, if this is 2.0, it means that there are four image pixels for every one logical pixel, and the image's actual width and height are
For example, if this is 2, it means that there are four image pixels for every one logical pixel, and the image's actual width and height are
double the height and width that should be used when painting the image.
```dart
showStreamAttachmentPickerModalBottomSheet(
context: context,
attachmentThumbnailScale: 2.0,
attachmentThumbnailScale: 2,
);
```
@@ -145,13 +148,13 @@ The `showStreamAttachmentPickerModalBottomSheet` function also accepts the param
isDismissible: true,
clipBehavior: Clip.antiAlias,
barrierColor: Colors.black.withOpacity(0.5),
constraints: BoxConstraints(
constraints: const BoxConstraints(
maxHeight: 500,
maxWidth: 500,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(16.0),
top: Radius.circular(16),
),
),
);
@@ -63,19 +63,19 @@ class StreamEmojiAutocompleteOptions extends StatelessWidget {
horizontalTitleGap: 0,
leading: Text(
emoji.char,
style: themeData.textTheme.headline6!.copyWith(
style: themeData.textTheme.titleLarge!.copyWith(
fontSize: 24,
),
),
title: SubstringHighlight(
text: emoji.shortName,
term: query,
textStyleHighlight: themeData.textTheme.headline6!.copyWith(
textStyleHighlight: themeData.textTheme.titleLarge!.copyWith(
color: Colors.yellow,
fontSize: 14.5,
fontWeight: FontWeight.bold,
),
textStyle: themeData.textTheme.headline6!.copyWith(
textStyle: themeData.textTheme.titleLarge!.copyWith(
fontSize: 14.5,
),
),
@@ -25,8 +25,8 @@ If you're new to Stream Chat Flutter, we recommend looking at our [getting start
dependencies:
flutter:
sdk: flutter
stream_chat_flutter: ^4.0.0
flutter_slidable: ^1.2.0
stream_chat_flutter: ^6.0.0
flutter_slidable: ^3.0.0
```
⚠️ Note: The examples shown in this guide use the above packages and versions.
@@ -120,7 +120,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
onRefresh: _controller.refresh,
child: StreamChannelListView(
controller: _controller,
itemBuilder: (context, channel, tile) {
itemBuilder: (context, channels, index, tile) {
final channel = channels[index];
final chatTheme = StreamChatTheme.of(context);
final backgroundColor = chatTheme.colorTheme.inputBg;
final canDeleteChannel = channel.ownCapabilities
@@ -29,41 +29,44 @@ factory StreamChatThemeData({
Brightness? brightness,
TextTheme? textTheme,
ColorTheme? colorTheme,
ChannelListHeaderTheme? channelListHeaderTheme,
ChannelPreviewTheme? channelPreviewTheme,
ChannelTheme? channelTheme,
MessageTheme? otherMessageTheme,
MessageTheme? ownMessageTheme,
MessageInputTheme? messageInputTheme,
Widget Function(BuildContext, Channel)? defaultChannelImage,
StreamChannelListHeaderThemeData? channelListHeaderTheme,
StreamChannelPreviewThemeData? channelPreviewTheme,
StreamChannelHeaderThemeData? channelHeaderTheme,
StreamMessageThemeData? otherMessageTheme,
StreamMessageThemeData? ownMessageTheme,
StreamMessageInputThemeData? messageInputTheme,
Widget Function(BuildContext, User)? defaultUserImage,
PlaceholderUserImage? placeholderUserImage,
IconThemeData? primaryIconTheme,
List<ReactionIcon>? reactionIcons,
List<StreamReactionIcon>? reactionIcons,
StreamGalleryHeaderThemeData? imageHeaderTheme,
StreamGalleryFooterThemeData? imageFooterTheme,
StreamMessageListViewThemeData? messageListViewTheme,
});
```
### Stream Chat Theme in use
Let's take a look at customizing widgets using `StreamChatTheme`. In the example below, we can change the default color theme to yellow and override the channel header's typography and colors.
Let's take a look at customizing widgets using `StreamChatThemeData`. In the example below, we can change the default color theme to yellow and override the channel header's typography and colors.
```dart
builder: (context, child) => StreamChat(
client: client,
child: child,
streamChatThemeData: StreamChatThemeData(
colorTheme: ColorTheme.light(
primaryAccent: const Color(0xffffe072),
),
channelTheme: ChannelTheme(
channelHeaderTheme: ChannelHeaderTheme(
color: const Color(0xffd34646),
title: TextStyle(
color: Colors.white,
),
),
MaterialApp(
builder: (context, child) => StreamChat(
client: client,
streamChatThemeData: StreamChatThemeData(
colorTheme: StreamColorTheme.light(
accentPrimary: const Color(0xffffe072),
),
channelHeaderTheme: const ChannelHeaderThemeData(
color: const Color(0xffd34646),
titleStyle: TextStyle(
color: Colors.white,
),
),
),
),
child: child,
),
);
```
We are creating this class at the very top of our widget tree using the `streamChatThemeData` parameter found in the `StreamChat` widget.
@@ -28,13 +28,13 @@ and send messages.
```dart
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
}) : super(key: key);
super.key,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: StreaChannelHeader(),
appBar: const StreamChannelHeader(),
body: Column(
children: <Widget>[
Expanded(
@@ -46,7 +46,7 @@ class ChannelPage extends StatelessWidget {
},
),
),
StreamMessageInput(),
const StreamMessageInput(),
],
),
);
@@ -27,9 +27,9 @@ common Channels Page.
```dart
class ChannelListPage extends StatefulWidget {
const ChannelListPage({
Key? key,
super.key,
required this.client,
}) : super(key: key);
});
final StreamChatClient client;
@@ -55,7 +55,7 @@ class _ChannelListPageState extends State<ChannelListPage> {
@override
Widget build(BuildContext context) => Scaffold(
appBar: StreamChannelListHeader(),
appBar: const StreamChannelListHeader(),
body: RefreshIndicator(
onRefresh: _controller.refresh,
child: StreamChannelListView(
@@ -33,9 +33,9 @@ Here is a basic example of the `StreamChannelListView` widget. It consists of th
```dart
class ChannelListPage extends StatefulWidget {
const ChannelListPage({
Key? key,
super.key,
required this.client,
}) : super(key: key);
});
final StreamChatClient client;
@@ -21,11 +21,11 @@ See the [StreamMemberListView](./stream_member_list_view.mdx) documentation for
```dart
class MemberGridPage extends StatefulWidget {
const MemberGridPage({
Key? key,
super.key,
required this.client,
}) : super(key: key);
});
final StreamChatClient client;
final Channel channel;
@override
State<MemberGridPage> createState() => _MemberGridPageState();
@@ -33,13 +33,13 @@ class MemberGridPage extends StatefulWidget {
class _MemberGridPageState extends State<MemberGridPage> {
late final _controller = StreamMemberListController(
client: widget.client,
channel: widget.channel,
limit: 25,
filter: Filter.and([
Filter.notEqual('id', StreamChat.of(context).currentUser!.id),
]),
sort: [
SortOption(
const SortOption(
'name',
direction: 1,
),
@@ -58,13 +58,16 @@ class _MemberGridPageState extends State<MemberGridPage> {
onRefresh: _controller.refresh,
child: StreamMemberGridView(
controller: _controller,
onChannelTap: (channel) => Navigator.push(
onMemberTap: (member) => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => StreamChannel(
channel: channel,
child: const ChannelPage(),
),
builder: (_) => Scaffold(
body: Center(
child: StreamUserAvatar(
user: member.user!,
),
),
),
),
),
),
@@ -20,7 +20,7 @@ Make sure to check the [StreamMemberListController](../04-stream_chat_flutter_co
```dart
class MemberListPage extends StatefulWidget {
const MemberListPage({Key? key}) : super(key: key);
const MemberListPage({super.key});
@override
State<MemberListPage> createState() => _MemberListPageState();
@@ -29,7 +29,7 @@ class MemberListPage extends StatefulWidget {
class _MemberListPageState extends State<MemberListPage> {
late final StreamMemberListController _memberListController =
StreamMemberListController(
client: StreamChat.of(context).client,
channel: StreamChannel.of(context).channel,
limit: 25,
filter: Filter.and(
[Filter.notEqual('id', StreamChat.of(context).currentUser!.id)],
@@ -62,7 +62,7 @@ You can use your own widget for the member items using the `itemBuilder` paramet
StreamMemberListView(
// ...
itemBuilder: (context, members, index, defaultWidget) {
return Text(members[index].name);
return Text(members[index].user!.name);
},
),
```
@@ -71,8 +71,8 @@ StreamMessageInput(
InkWell(
child: Icon(
Icons.location_on,
size: 20.0,
color: StreamChatTheme.of(context).colorTheme.grey,
size: 20,
color: Colors.grey,
),
onTap: () {
// Do something here
@@ -29,19 +29,19 @@ An example of how you can use the `StreamMessageListView` is:
```dart
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
}) : super(key: key);
super.key,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: StreamChannelHeader(),
appBar: const StreamChannelHeader(),
body: Column(
children: <Widget>[
Expanded(
child: StreamMessageListView(),
),
StreamMessageInput(),
const StreamMessageInput(),
],
),
);
@@ -21,9 +21,9 @@ Make sure to check the [StreamMessageSearchListView](./stream_message_search_lis
```dart
class StreamMessageSearchPage extends StatefulWidget {
const StreamMessageSearchPage({
Key? key,
super.key,
required this.client,
}) : super(key: key);`
});
final StreamChatClient client;
@@ -35,7 +35,10 @@ class _StreamMessageSearchState extends State<StreamMessageSearchPage> {
late final _controller = StreamMessageSearchListController(
client: widget.client,
limit: 20,
filters: Filter.in_('members', [StreamChat.of(context).user!.id],),
filter: Filter.in_(
'members',
[StreamChat.of(context).user!.id],
),
searchQuery: 'your query here',
);
@@ -49,6 +52,9 @@ class _StreamMessageSearchState extends State<StreamMessageSearchPage> {
Widget build(BuildContext context) => Scaffold(
body: StreamMessageSearchGridView(
controller: _controller,
itemBuilder: (context, values, index) {
// return your custom widget here
},
),
);
}
@@ -27,9 +27,9 @@ While the `StreamMessageListView` is tied to a certain `StreamChannel`, a `Strea
```dart
class StreamMessageSearchPage extends StatefulWidget {
const StreamMessageSearchPage({
Key? key,
super.key,
required this.client,
}) : super(key: key);`
});
final StreamChatClient client;
@@ -41,7 +41,10 @@ class _StreamMessageSearchState extends State<StreamMessageSearchPage> {
late final _controller = StreamMessageSearchListController(
client: widget.client,
limit: 20,
filters: Filter.in_('members', [StreamChat.of(context).user!.id],),
filter: Filter.in_(
'members',
[StreamChat.of(context).user!.id],
),
searchQuery: 'your query here',
);
@@ -25,8 +25,8 @@ a default implementation of the widget for us to modify.
```dart
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
}) : super(key: key);
super.key,
});
@override
Widget build(BuildContext context) {
@@ -21,9 +21,9 @@ Make sure to check the [StreamUserListView](./stream_user_list_view.mdx) documen
```dart
class UserGridPage extends StatefulWidget {
const UserGridPage({
Key? key,
super.key,
required this.client,
}) : super(key: key);
});
final StreamChatClient client;
@@ -39,7 +39,7 @@ class _UserGridPageState extends State<UserGridPage> {
Filter.notEqual('id', StreamChat.of(context).currentUser!.id),
]),
sort: [
SortOption(
const SortOption(
'name',
direction: 1,
),
@@ -58,12 +58,15 @@ class _UserGridPageState extends State<UserGridPage> {
onRefresh: _controller.refresh,
child: StreamUserGridView(
controller: _controller,
onChannelTap: (channel) => Navigator.push(
onMemberTap: (member) => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => StreamChannel(
channel: channel,
child: const ChannelPage(),
builder: (_) => Scaffold(
body: Center(
child: StreamUserAvatar(
user: member.user!,
),
),
),
),
),
@@ -24,7 +24,7 @@ Make sure to check the [StreamUserListController](../04-stream_chat_flutter_core
```dart
class UserListPage extends StatefulWidget {
const UserListPage({Key? key}) : super(key: key);
const UserListPage({super.key});
@override
State<UserListPage> createState() => _UserListPageState();
@@ -66,7 +66,7 @@ You can use your own widget for the user items using the `itemBuilder` parameter
StreamUsersListView(
// ...
itemBuilder: (context, users, index, defaultWidget) {
return Text(user[index].name);
return Text(users[index].name);
},
),
```
@@ -35,7 +35,7 @@ LazyLoadScrollView(
/// The child could be any widget which dispatches [ScrollNotification]s.
/// For example [ListView], [GridView] or [CustomScrollView].
child: ListView.builder(
itemBuilder: ((context, index) => _buildListTile),
itemBuilder: (context, index) => _buildListTile,
),
)
```
@@ -31,8 +31,8 @@ A `MessageListController` is used to paginate data.
```dart
class ChannelPage extends StatelessWidget {
const ChannelPage({
Key key,
}) : super(key: key);
super.key,
});
@override
Widget build(BuildContext context) {
@@ -42,20 +42,20 @@ class ChannelPage extends StatelessWidget {
Expanded(
child: MessageListCore(
emptyBuilder: (context) {
return Center(
return const Center(
child: Text('Nothing here...'),
);
},
loadingBuilder: (context) {
return Center(
return const Center(
child: CircularProgressIndicator(),
);
},
messageListBuilder: (context, list) {
return MessagesPage(list);
},
errorWidgetBuilder: (context, err) {
return Center(
errorBuilder: (context, err) {
return const Center(
child: Text('Error'),
);
},
@@ -65,7 +65,7 @@ class _MyHomePageState extends State {
const Text('Usernames:'),
Expanded(
child: ListView(
children: userNames.map((it) => Text(it)).toList(),
children: userNames.map(Text.new).toList(),
),
),
if (nextPageKey != null)
@@ -75,7 +75,7 @@ class _MyHomePageState extends State {
),
],
),
loading: () => CircularProgressIndicator(),
loading: CircularProgressIndicator.new,
error: (e) => Text('Error: $e'),
);
},
@@ -25,7 +25,7 @@ First of all we should create an instance of the `StreamChannelListController` a
You can also add a `Filter`, a list of `SortOption`s and other pagination-related parameters.
```dart
class ChannelListPageState extends State<HomeScreen> {
class _MyChannelListPageState extends State<MyChannelListPage> {
/// Controller used for loading more data and controlling pagination in
/// [StreamChannelListController].
late final channelListController = StreamChannelListController(
@@ -40,6 +40,8 @@ class ChannelListPageState extends State<HomeScreen> {
),
]),
);
...
}
```
Make sure you call `channelListController.doInitialLoad()` to load the initial data and `channelListController.dispose()` when the controller is no longer required.
@@ -92,7 +94,7 @@ Widget build(BuildContext context) => Scaffold(
child: Text(error.message),
);
}
return CircularProgressIndicator();
return const CircularProgressIndicator();
}
final _item = channels[index];
@@ -44,20 +44,11 @@ Pass it down to the controller:
* Mix the `StreamChannelListEventHandler` into your widget state.
```dart
class _ChannelListPageState extends State<ChannelListPage>
with StreamChannelListEventHandler {
@override
void onConnectionRecovered(
Event event,
StreamChannelListController controller,
) {
// Write your own custom implementation here
}
class _ChannelListPageState extends State<ChannelListPage> {
late final _listController = StreamChannelListController(
client: StreamChat.of(context).client,
eventHandler: this,
eventHandler: MyCustomEventHandler(),
);
}
```
@@ -22,7 +22,7 @@ return MaterialApp(
home: HomeScreen(),
builder: (context, child) => StreamChatCore(
client: client,
child: child!,
child: child,
),
);
```
@@ -26,7 +26,7 @@ class MemberListPageState extends State<MemberListPage> {
/// Controller used for loading more data and controlling pagination in
/// [StreamMemberListController].
late final memberListController = StreamMemberListController(
client: StreamChatCore.of(context).client,
channel: StreamChannel.of(context).channel,
);
```
@@ -52,7 +52,7 @@ You can use a [`PagedValueListenableBuilder`](./paged_value_listenable_builder.m
```dart
@override
Widget build(BuildContext context) => Scaffold(
body: PagedValueListenableBuilder<int, List<Member>>(
body: PagedValueListenableBuilder<int, Member>(
valueListenable: memberListController,
builder: (context, value, child) {
return value.when(
@@ -80,12 +80,12 @@ Widget build(BuildContext context) => Scaffold(
child: Text(error.message),
);
}
return CircularProgressIndicator();
return const CircularProgressIndicator();
}
final _item = members[index];
return ListTile(
title: Text(_item.name ?? ''),
title: Text(_item.user?.name ?? ''),
);
},
),
@@ -47,7 +47,7 @@ Padding(
children: [
Expanded(
child: TextField(
controller: messageInputController.textEditingController,
controller: messageInputController.textFieldController,
onChanged: (s) => messageInputController.text = s,
decoration: const InputDecoration(
hintText: 'Enter your message',
@@ -66,7 +66,7 @@ Padding(
messageInputController.message,
);
messageInputController.clear();
if (mounted) {
if (context.mounted) {
_updateList();
}
}
@@ -59,8 +59,7 @@ Widget build(BuildContext context) => Scaffold(
/// In a real-world app you should throttle the search requests.
/// You can use our library [rate_limiter](https://pub.dev/packages/rate_limiter).
onChanged: (s) {
messageSearchListController.searchQuery = s;
messageSearchListController.doInitialLoad();
messageSearchListController..searchQuery = s..doInitialLoad();
},
),
Expanded(
@@ -92,7 +91,7 @@ Widget build(BuildContext context) => Scaffold(
child: Text(error.message),
);
}
return CircularProgressIndicator();
return const CircularProgressIndicator();
}
final _item = responses[index];
@@ -52,7 +52,7 @@ You can use a [`PagedValueListenableBuilder`](./paged_value_listenable_builder.m
```dart
@override
Widget build(BuildContext context) => Scaffold(
body: PagedValueListenableBuilder<int, List<User>>(
body: PagedValueListenableBuilder<int, User>(
valueListenable: userListController,
builder: (context, value, child) {
return value.when(
@@ -80,12 +80,12 @@ Widget build(BuildContext context) => Scaffold(
child: Text(error.message),
);
}
return CircularProgressIndicator();
return const CircularProgressIndicator();
}
final _item = users[index];
return ListTile(
title: Text(_item.name ?? ''),
title: Text(_item.name),
);
},
),
@@ -15,16 +15,16 @@ In this guide, you'll explore how you can use Firebase Auth as an authentication
generate Stream Chat user tokens.
You will use Stream's [NodeJS client](https://getstream.io/chat/docs/node/?language=javascript) for Stream account creation and
token generation, and [Flutter Cloud Functions for Firebase](https://firebase.flutter.dev/docs/functions/overview) to invoke the cloud functions
token generation, and [Flutter Cloud Functions for Firebase](https://firebase.google.com/docs/functions/callable?gen=2nd#dart) to invoke the cloud functions
from your Flutter app.
Stream supports several different [backend clients](https://getstream.io/chat/sdk/#backend-clients) to integrate with your server. This guide only shows an easy way to integrate Stream Chat authentication using Firebase and Flutter.
### Flutter Firebase
See the [Flutter Firebase getting started](https://firebase.flutter.dev/docs/overview) docs for setup and installation instructions.
See the [Flutter Firebase getting started](https://firebase.google.com/docs/flutter/setup) docs for setup and installation instructions.
You will also need to add the [Flutter Firebase Authentication](https://firebase.flutter.dev/docs/auth/overview), and [Flutter Firebase Cloud Functions](https://firebase.flutter.dev/docs/functions/overview) packages to your app. Depending on the platform that you target, there may be specific configurations that you need to do.
You will also need to add the [Flutter Firebase Authentication](https://firebase.google.com/docs/auth/flutter/start), and [Flutter Firebase Cloud Functions](https://firebase.google.com/docs/functions/callable?gen=2nd#dart) packages to your app. Depending on the platform that you target, there may be specific configurations that you need to do.
#### Starting Code
@@ -35,20 +35,20 @@ You will extend this later to execute cloud functions.
```dart
import 'package:cloud_functions/cloud_functions.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:flutter/material.dart';
import 'dart:async';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
return const MaterialApp(
home: Scaffold(
body: Auth(),
),
@@ -57,20 +57,20 @@ class MyApp extends StatelessWidget {
}
class Auth extends StatefulWidget {
Auth({Key? key}) : super(key: key);
const Auth({super.key});
@override
_AuthState createState() => _AuthState();
}
class _AuthState extends State<Auth> {
late FirebaseAuth auth;
late firebase_auth.FirebaseAuth auth;
late FirebaseFunctions functions;
@override
void initState() {
super.initState();
auth = FirebaseAuth.instance;
auth = firebase_auth.FirebaseAuth.instance;
functions = FirebaseFunctions.instance;
}
@@ -103,19 +103,26 @@ class _AuthState extends State<Auth> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
AuthenticationState(
streamUser: auth.authStateChanges(),
streamUser: auth.authStateChanges().map(
(firebaseUser) => firebaseUser != null
? User(
id: firebaseUser.uid,
// Map other user fields here
)
: null,
),
),
ElevatedButton(
onPressed: createAccount,
child: Text('Create account'),
child: const Text('Create account'),
),
ElevatedButton(
onPressed: signIn,
child: Text('Sign in'),
child: const Text('Sign in'),
),
ElevatedButton(
onPressed: signOut,
child: Text('Sign out'),
child: const Text('Sign out'),
),
],
),
@@ -125,9 +132,9 @@ class _AuthState extends State<Auth> {
class AuthenticationState extends StatelessWidget {
const AuthenticationState({
Key? key,
super.key,
required this.streamUser,
}) : super(key: key);
});
final Stream<User?> streamUser;
@@ -138,10 +145,10 @@ class AuthenticationState extends StatelessWidget {
builder: (context, snapshot) {
if (snapshot.hasData) {
return (snapshot.data != null)
? Text('Authenticated')
: Text('Not Authenticated');
? const Text('Authenticated')
: const Text('Not Authenticated');
}
return Text('Not Authenticated');
return const Text('Not Authenticated');
},
);
}
@@ -158,8 +165,8 @@ in the `createAccount`, `signIn` and `signOut` methods. There is a button to inv
The `FirebaseFunctions.instance` will be used later in this guide.
The `AuthenticationState` widget listens to `auth.authStateChanges()` to display a message
indicating if a user is authenticated.
The `AuthenticationState`` widget listens to `auth.authStateChanges()` (mapped to Stream's `User`)
to display a message indicating if a user is authenticated.
### Firebase Cloud Functions
@@ -168,7 +175,7 @@ Firebase Cloud Functions allows you to extend Firebase with custom operations th
- **External event**: For example, directly calling a cloud function from your Flutter application.
To set up your local environment to deploy cloud functions, please see the
[Cloud Functions getting started](https://firebase.flutter.dev/docs/overview) docs.
[Cloud Functions getting started](https://firebase.google.com/docs/flutter/setup) docs.
After initializing your project with cloud functions, you should have a **functions** folder in your project, including a `package.json` file.
@@ -61,6 +61,10 @@ void main() {
}
class MyApp extends StatelessWidget {
// Setup client and channel code here
...
@override
Widget build(BuildContext context) {
return MaterialApp(
@@ -208,9 +208,9 @@ void handleNotification(
flutterLocalNotificationsPlugin.show(
1,
'New message from ${response.message.user.name} in ${response.channel.name}',
'New message from ${response.message.user!.name} in ${response.channel!.name}',
response.message.text,
NotificationDetails(
const NotificationDetails(
android: AndroidNotificationDetails(
'new_message',
'New message notifications channel',
@@ -255,7 +255,7 @@ Make sure to read the [general push notification docs](https://getstream.io/chat
If you're not sure whether you've set up push notifications correctly, for example, you don't always receive them, or they dont work reliably, then you can follow these steps to make sure your configuration is correct and working:
1. Clone our repository for push testing: `git clone [email protected]:GetStream/chat-push-test.git`
2. `cd flutter`
2. `cd chat-push-test/flutter`
3. In that folder run `flutter pub get`
4. Input your API key and secret in `lib/main.dart`
5. Change the bundle identifier/application ID and development team/user so you can run the app on your physical device.**Do not** run on an iOS simulator, as it will not work. Testing on an Android emulator is fine.