diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml
index 7b5df2ba..95f48ee9 100644
--- a/.github/workflows/stream_flutter_workflow.yml
+++ b/.github/workflows/stream_flutter_workflow.yml
@@ -27,7 +27,7 @@ jobs:
./.github/workflows/scripts/install-tools.sh
flutter pub global activate tuneup
- name: 'Bootstrap Workspace'
- run: melos bootstrap
+ run: melos bootstrap --verbose
- name: 'Dart Analyze'
run: |
melos exec -c 3 --ignore="*example*" -- \
@@ -50,7 +50,7 @@ jobs:
run: |
./.github/workflows/scripts/install-tools.sh
- name: 'Bootstrap Workspace'
- run: melos bootstrap
+ run: melos bootstrap --verbose
- name: 'Dart'
run: |
melos exec -c 1 -- \
@@ -72,7 +72,7 @@ jobs:
flutter pub global activate coverage
flutter pub global activate remove_from_coverage
- name: 'Bootstrap Workspace'
- run: melos bootstrap
+ run: melos bootstrap --verbose
- name: 'Dart Test'
run: |
cd packages/stream_chat
diff --git a/melos.yaml b/melos.yaml
index 6a6c74dc..3fcad03f 100644
--- a/melos.yaml
+++ b/melos.yaml
@@ -59,5 +59,4 @@ dev_dependencies:
pedantic: 1.9.2
environment:
- sdk: ">=2.7.0 <3.0.0"
- flutter: ">=1.22.4 <2.0.0"
\ No newline at end of file
+ sdk: ">=2.12.0 <3.0.0"
\ No newline at end of file
diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md
index d6d08a3c..2e6c3a78 100644
--- a/packages/stream_chat/CHANGELOG.md
+++ b/packages/stream_chat/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 2.0.0-nullsafety.0
+
+- Migrate this package to null safety
+- Added typed filters
+
## 1.5.3
- fix: `StreamChatClient.connect` returns quicker when you're using the persistence package
diff --git a/packages/stream_chat/analysis_options.yaml b/packages/stream_chat/analysis_options.yaml
index ddc691d0..c0e14133 100644
--- a/packages/stream_chat/analysis_options.yaml
+++ b/packages/stream_chat/analysis_options.yaml
@@ -3,7 +3,6 @@ analyzer:
- lib/**/*.g.dart
- lib/**/*.freezed.dart
- example/*
- - test/*
linter:
rules:
- always_use_package_imports
diff --git a/packages/stream_chat/build.yaml b/packages/stream_chat/build.yaml
index ddbd70dd..d439bddb 100644
--- a/packages/stream_chat/build.yaml
+++ b/packages/stream_chat/build.yaml
@@ -4,5 +4,4 @@ targets:
json_serializable:
options:
explicit_to_json: true
- field_rename: snake
- any_map: true
+ field_rename: snake
\ No newline at end of file
diff --git a/packages/stream_chat/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/stream_chat/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 1d526a16..919434a6 100644
--- a/packages/stream_chat/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/packages/stream_chat/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
+ location = "self:">
diff --git a/packages/stream_chat/example/lib/main.dart b/packages/stream_chat/example/lib/main.dart
index 2d0a529f..11e9779f 100644
--- a/packages/stream_chat/example/lib/main.dart
+++ b/packages/stream_chat/example/lib/main.dart
@@ -2,12 +2,9 @@ import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
Future main() async {
- /// Create a new instance of [StreamChatClient] passing the apikey obtained from your
- /// project dashboard.
- final client = StreamChatClient(
- 'b67pax5b2wdq',
- logLevel: Level.INFO,
- );
+ /// Create a new instance of [StreamChatClient]
+ /// by passing the apikey obtained from your project dashboard.
+ final client = StreamChatClient('b67pax5b2wdq', logLevel: Level.INFO);
/// Set the current user. In a production scenario, this should be done using
/// a backend to generate a user token using our server SDK.
@@ -21,7 +18,7 @@ Future main() async {
'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow',
},
),
- 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo',
+ '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo''',
);
/// Creates a channel using the type `messaging` and `godevs`.
@@ -44,15 +41,16 @@ Future main() async {
/// Example using Stream's Low Level Dart client.
class StreamExample extends StatelessWidget {
- /// To initialize this example, an instance of [client] and [channel] is required.
+ /// To initialize this example, an instance of
+ /// [client] and [channel] is required.
const StreamExample({
- Key key,
- @required this.client,
- @required this.channel,
+ Key? key,
+ required this.client,
+ required this.channel,
}) : super(key: key);
- /// Instance of [StreamChatClient] we created earlier. This contains information about
- /// our application and connection state.
+ /// 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.
@@ -71,28 +69,31 @@ class StreamExample extends StatelessWidget {
/// containing the channel name and a [MessageView] displaying recent messages.
class HomeScreen extends StatelessWidget {
/// [HomeScreen] is constructed using the [Channel] we defined earlier.
- const HomeScreen({Key key, @required this.channel}) : super(key: key);
+ const HomeScreen({
+ Key? key,
+ required this.channel,
+ }) : super(key: key);
/// Channel object containing the [Channel.id] we'd like to observe.
final Channel channel;
@override
Widget build(BuildContext context) {
- final messages = channel.state.channelStateStream;
+ final messages = channel.state!.channelStateStream;
return Scaffold(
appBar: AppBar(
title: Text('Channel: ${channel.id}'),
),
body: SafeArea(
- child: StreamBuilder(
+ child: StreamBuilder(
stream: messages,
builder: (
BuildContext context,
- AsyncSnapshot snapshot,
+ AsyncSnapshot snapshot,
) {
if (snapshot.hasData && snapshot.data != null) {
return MessageView(
- messages: snapshot.data.messages.reversed.toList(),
+ messages: snapshot.data!.messages.reversed.toList(),
channel: channel,
);
} else if (snapshot.hasError) {
@@ -104,8 +105,8 @@ class HomeScreen extends StatelessWidget {
}
return const Center(
child: SizedBox(
- width: 100.0,
- height: 100.0,
+ width: 100,
+ height: 100,
child: CircularProgressIndicator(),
),
);
@@ -121,9 +122,9 @@ class HomeScreen extends StatelessWidget {
class MessageView extends StatefulWidget {
/// Message takes the latest list of messages and the current channel.
const MessageView({
- Key key,
- @required this.messages,
- @required this.channel,
+ Key? key,
+ required this.messages,
+ required this.channel,
}) : super(key: key);
/// List of messages sent in the given channel.
@@ -137,8 +138,8 @@ class MessageView extends StatefulWidget {
}
class _MessageViewState extends State {
- TextEditingController _controller;
- ScrollController _scrollController;
+ late final TextEditingController _controller;
+ late final ScrollController _scrollController;
List get _messages => widget.messages;
@@ -176,20 +177,20 @@ class _MessageViewState extends State {
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = _messages[index];
- if (item.user.id == widget.channel.client.uid) {
+ if (item.user?.id == widget.channel.client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Text(item.text),
+ padding: const EdgeInsets.all(8),
+ child: Text(item.text ?? ''),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Text(item.text),
+ padding: const EdgeInsets.all(8),
+ child: Text(item.text ?? ''),
),
);
}
@@ -197,7 +198,7 @@ class _MessageViewState extends State {
),
),
Padding(
- padding: const EdgeInsets.all(8.0),
+ padding: const EdgeInsets.all(8),
child: Row(
children: [
Expanded(
@@ -245,7 +246,8 @@ class _MessageViewState extends State {
}
}
-/// Helper extension for quickly retrieving the current user id from a [StreamChatClient].
+/// Helper extension for quickly retrieving
+/// the current user id from a [StreamChatClient].
extension on StreamChatClient {
- String get uid => state.user.id;
+ String get uid => state.user!.id;
}
diff --git a/packages/stream_chat/example/pubspec.yaml b/packages/stream_chat/example/pubspec.yaml
index 2a8b7eb2..1b092b3e 100644
--- a/packages/stream_chat/example/pubspec.yaml
+++ b/packages/stream_chat/example/pubspec.yaml
@@ -1,21 +1,22 @@
name: example
description: A new Flutter project.
-publish_to: 'none'
+publish_to: "none"
version: 1.0.0+1
environment:
- sdk: ">=2.7.0 <3.0.0"
+ sdk: '>=2.12.0 <3.0.0'
dependencies:
+ cupertino_icons: ^1.0.0
flutter:
sdk: flutter
- cupertino_icons: ^1.0.0
- stream_chat:
+ stream_chat:
path: ../
dev_dependencies:
flutter_test:
sdk: flutter
+
flutter:
- uses-material-design: true
\ No newline at end of file
+ uses-material-design: true
diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart
index b424f6f1..1674e5eb 100644
--- a/packages/stream_chat/lib/src/api/channel.dart
+++ b/packages/stream_chat/lib/src/api/channel.dart
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:math';
+import 'package:collection/collection.dart' show IterableExtension;
import 'package:dio/dio.dart';
import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart';
@@ -18,52 +19,58 @@ class Channel {
/// Create a channel client instance.
Channel(
this._client,
- this.type,
- this._id,
- this._extraData,
- ) : _cid = _id != null ? '$type:$_id' : null {
+ this._type,
+ this._id, {
+ Map extraData = const {},
+ }) : _cid = _id != null ? '$_type:$_id' : null,
+ _extraData = extraData {
_client.logger.info('New Channel instance not initialized created');
}
/// Create a channel client instance from a [ChannelState] object
- Channel.fromState(this._client, ChannelState channelState) {
- _cid = channelState.channel.cid;
- _id = channelState.channel.id;
- type = channelState.channel.type;
-
+ Channel.fromState(this._client, ChannelState channelState)
+ : assert(
+ channelState.channel != null,
+ 'No channel found inside channel state',
+ ),
+ _id = channelState.channel!.id,
+ _type = channelState.channel!.type,
+ _cid = channelState.channel!.cid,
+ _extraData = channelState.channel!.extraData {
state = ChannelClientState(this, channelState);
_initializedCompleter.complete(true);
_client.logger.info('New Channel instance initialized created');
}
/// This client state
- ChannelClientState state;
+ ChannelClientState? state;
/// The channel type
- String type;
+ final String _type;
- String _id;
- String _cid;
- Map _extraData;
+ String? _id;
+ String? _cid;
+ final Map _extraData;
set extraData(Map extraData) {
if (_initializedCompleter.isCompleted) {
- throw Exception(
- 'Once the channel is initialized you should use channel.update '
- 'to update channel data');
+ throw StateError(
+ 'Once the channel is initialized you should use channel.update '
+ 'to update channel data',
+ );
}
- _extraData = extraData;
+ _extraData.addAll(extraData);
}
/// Returns true if the channel is muted
bool get isMuted =>
_client.state.user?.channelMutes
- ?.any((element) => element.channel.cid == cid) ==
+ .any((element) => element.channel.cid == cid) ==
true;
/// Returns true if the channel is muted as a stream
- Stream get isMutedStream => _client.state.userStream?.map((event) =>
- event.channelMutes?.any((element) => element.channel.cid == cid) == true);
+ Stream? get isMutedStream => _client.state.userStream.map((event) =>
+ event!.channelMutes.any((element) => element.channel.cid == cid) == true);
/// True if the channel is a group
bool get isGroup => memberCount != 2;
@@ -72,85 +79,135 @@ class Channel {
bool get isDistinct => id?.startsWith('!members') == true;
/// Channel configuration
- ChannelConfig get config => state?._channelState?.channel?.config;
+ ChannelConfig? get config {
+ _checkInitialized();
+ return state?._channelState.channel?.config;
+ }
/// Channel configuration as a stream
- Stream get configStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.config);
+ Stream? get configStream {
+ _checkInitialized();
+ return state?.channelStateStream.map((cs) => cs.channel?.config);
+ }
/// Channel user creator
- User get createdBy => state?._channelState?.channel?.createdBy;
+ User? get createdBy {
+ _checkInitialized();
+ return state?._channelState.channel?.createdBy;
+ }
/// Channel user creator as a stream
- Stream get createdByStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.createdBy);
+ Stream? get createdByStream {
+ _checkInitialized();
+ return state?.channelStateStream.map((cs) => cs.channel?.createdBy);
+ }
/// Channel frozen status
- bool get frozen => state?._channelState?.channel?.frozen;
+ bool? get frozen {
+ _checkInitialized();
+ return state?._channelState.channel?.frozen;
+ }
/// Channel frozen status as a stream
- Stream get frozenStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.frozen);
+ Stream? get frozenStream {
+ _checkInitialized();
+ return state?.channelStateStream.map((cs) => cs.channel?.frozen);
+ }
/// Channel creation date
- DateTime get createdAt => state?._channelState?.channel?.createdAt;
+ DateTime? get createdAt {
+ _checkInitialized();
+ return state?._channelState.channel?.createdAt;
+ }
/// Channel creation date as a stream
- Stream get createdAtStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.createdAt);
+ Stream? get createdAtStream {
+ _checkInitialized();
+ return state?.channelStateStream.map((cs) => cs.channel?.createdAt);
+ }
/// Channel last message date
- DateTime get lastMessageAt => state?._channelState?.channel?.lastMessageAt;
+ DateTime? get lastMessageAt {
+ _checkInitialized();
+
+ return state?._channelState.channel?.lastMessageAt;
+ }
/// Channel last message date as a stream
- Stream get lastMessageAtStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.lastMessageAt);
+ Stream? get lastMessageAtStream {
+ _checkInitialized();
+
+ return state?.channelStateStream.map((cs) => cs.channel?.lastMessageAt);
+ }
/// Channel updated date
- DateTime get updatedAt => state?._channelState?.channel?.updatedAt;
+ DateTime? get updatedAt {
+ _checkInitialized();
+
+ return state?._channelState.channel?.updatedAt;
+ }
/// Channel updated date as a stream
- Stream get updatedAtStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.updatedAt);
+ Stream? get updatedAtStream {
+ _checkInitialized();
+
+ return state?.channelStateStream.map((cs) => cs.channel?.updatedAt);
+ }
/// Channel deletion date
- DateTime get deletedAt => state?._channelState?.channel?.deletedAt;
+ DateTime? get deletedAt {
+ _checkInitialized();
+
+ return state?._channelState.channel?.deletedAt;
+ }
/// Channel deletion date as a stream
- Stream get deletedAtStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.deletedAt);
+ Stream? get deletedAtStream {
+ _checkInitialized();
+
+ return state?.channelStateStream.map((cs) => cs.channel?.deletedAt);
+ }
/// Channel member count
- int get memberCount => state?._channelState?.channel?.memberCount;
+ int? get memberCount {
+ _checkInitialized();
+
+ return state?._channelState.channel?.memberCount;
+ }
/// Channel member count as a stream
- Stream get memberCountStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.memberCount);
+ Stream? get memberCountStream {
+ _checkInitialized();
+
+ return state?.channelStateStream.map((cs) => cs.channel?.memberCount);
+ }
/// Channel id
- String get id => state?._channelState?.channel?.id ?? _id;
+ String? get id => state?._channelState.channel?.id ?? _id;
- /// Channel id as a stream
- Stream get idStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.id ?? _id);
+ /// Channel type
+ String get type => state?._channelState.channel?.type ?? _type;
/// Channel cid
- String get cid => state?._channelState?.channel?.cid ?? _cid;
+ String? get cid => state?._channelState.channel?.cid ?? _cid;
/// Channel team
- String get team => state?._channelState?.channel?.team;
-
- /// Channel cid as a stream
- Stream get cidStream =>
- state?.channelStateStream?.map((cs) => cs.channel?.cid ?? _cid);
+ String? get team {
+ _checkInitialized();
+ return state?._channelState.channel?.team;
+ }
/// Channel extra data
Map get extraData =>
- state?._channelState?.channel?.extraData ?? _extraData;
+ state?._channelState.channel?.extraData ?? _extraData;
/// Channel extra data as a stream
- Stream