refactor: use gorouter for navigation
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import 'package:example/app.dart';
|
||||
import 'package:example/pages/home_page.dart';
|
||||
import 'package:example/state/init_data.dart';
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:example/widgets/stream_version.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'choose_user_page.dart';
|
||||
import 'package:example/pages/choose_user_page.dart';
|
||||
|
||||
class AdvancedOptionsPage extends StatefulWidget {
|
||||
const AdvancedOptionsPage({super.key});
|
||||
@@ -41,6 +43,86 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _login() async {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final apiKey = _apiKeyController.text;
|
||||
final userId = _userIdController.text;
|
||||
final userToken = _userTokenController.text;
|
||||
final username = _usernameController.text;
|
||||
|
||||
loading = true;
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
||||
builder: (context) => Center(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: StreamChatTheme.of(context).colorTheme.barsBg,
|
||||
),
|
||||
height: 100,
|
||||
width: 100,
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final client = buildStreamChatClient(apiKey);
|
||||
final router = GoRouter.of(context);
|
||||
final initNotifier = context.read<InitNotifier>();
|
||||
|
||||
try {
|
||||
await client.connectUser(
|
||||
User(
|
||||
id: userId,
|
||||
extraData: {
|
||||
'name': username,
|
||||
},
|
||||
),
|
||||
userToken,
|
||||
);
|
||||
|
||||
const secureStorage = FlutterSecureStorage();
|
||||
await Future.wait([
|
||||
secureStorage.write(
|
||||
key: kStreamApiKey,
|
||||
value: apiKey,
|
||||
),
|
||||
secureStorage.write(
|
||||
key: kStreamUserId,
|
||||
value: userId,
|
||||
),
|
||||
secureStorage.write(
|
||||
key: kStreamToken,
|
||||
value: userToken,
|
||||
),
|
||||
]);
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
var errorText = AppLocalizations.of(context).errorConnecting;
|
||||
if (e is Map) {
|
||||
errorText = e['message'] ?? errorText;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
setState(() {
|
||||
_apiKeyError = errorText.toUpperCase();
|
||||
});
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
loading = false;
|
||||
initNotifier.initData = initNotifier.initData!.copyWith(client: client);
|
||||
|
||||
router.goNamed(Routes.CHOOSE_USER.name);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -260,6 +342,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: _login,
|
||||
child: Text(
|
||||
AppLocalizations.of(context).login,
|
||||
style: TextStyle(
|
||||
@@ -271,84 +354,6 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
: Colors.white,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final apiKey = _apiKeyController.text;
|
||||
final userId = _userIdController.text;
|
||||
final userToken = _userTokenController.text;
|
||||
final username = _usernameController.text;
|
||||
|
||||
loading = true;
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
barrierColor:
|
||||
StreamChatTheme.of(context).colorTheme.overlay,
|
||||
builder: (context) => Center(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.barsBg,
|
||||
),
|
||||
height: 100,
|
||||
width: 100,
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final client = buildStreamChatClient(apiKey);
|
||||
final navigator = Navigator.of(context);
|
||||
|
||||
try {
|
||||
await client.connectUser(
|
||||
User(id: userId, extraData: {
|
||||
'name': username,
|
||||
}),
|
||||
userToken,
|
||||
);
|
||||
|
||||
const secureStorage = FlutterSecureStorage();
|
||||
secureStorage.write(
|
||||
key: kStreamApiKey,
|
||||
value: apiKey,
|
||||
);
|
||||
secureStorage.write(
|
||||
key: kStreamUserId,
|
||||
value: userId,
|
||||
);
|
||||
secureStorage.write(
|
||||
key: kStreamToken,
|
||||
value: userToken,
|
||||
);
|
||||
} catch (e) {
|
||||
var errorText =
|
||||
AppLocalizations.of(context).errorConnecting;
|
||||
if (e is Map) {
|
||||
errorText = e['message'] ?? errorText;
|
||||
}
|
||||
Navigator.pop(context);
|
||||
setState(() {
|
||||
_apiKeyError = errorText.toUpperCase();
|
||||
});
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
loading = false;
|
||||
await navigator.pushNamedAndRemoveUntil(
|
||||
Routes.HOME,
|
||||
ModalRoute.withName(Routes.HOME),
|
||||
arguments: HomePageArgs(client),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
const StreamVersion(),
|
||||
],
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:example/app.dart';
|
||||
import 'package:example/state/init_data.dart';
|
||||
import 'package:example/pages/user_mentions_page.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:example/utils/app_config.dart';
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:example/widgets/channel_list.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_app_badger/flutter_app_badger.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
|
||||
|
||||
import 'package:example/widgets/channel_list.dart';
|
||||
|
||||
class ChannelListPage extends StatefulWidget {
|
||||
const ChannelListPage({
|
||||
Key? key,
|
||||
@@ -71,12 +75,10 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
||||
appBar: StreamChannelListHeader(
|
||||
onNewChatButtonTap: () {
|
||||
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
||||
},
|
||||
preNavigationCallback: () {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
},
|
||||
onNewChatButtonTap: () =>
|
||||
GoRouter.of(context).pushNamed(Routes.NEW_CHAT.name),
|
||||
preNavigationCallback: () =>
|
||||
FocusScope.of(context).requestFocus(FocusNode()),
|
||||
),
|
||||
drawer: LeftDrawer(
|
||||
user: user,
|
||||
@@ -188,10 +190,8 @@ class LeftDrawer extends StatelessWidget {
|
||||
.withOpacity(.5),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.popAndPushNamed(
|
||||
context,
|
||||
Routes.NEW_CHAT,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
GoRouter.of(context).pushNamed(Routes.NEW_CHAT.name);
|
||||
},
|
||||
title: Text(
|
||||
AppLocalizations.of(context).newDirectMessage,
|
||||
@@ -208,10 +208,8 @@ class LeftDrawer extends StatelessWidget {
|
||||
.withOpacity(.5),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.popAndPushNamed(
|
||||
context,
|
||||
Routes.NEW_GROUP_CHAT,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
GoRouter.of(context).pushNamed(Routes.NEW_GROUP_CHAT.name);
|
||||
},
|
||||
title: Text(
|
||||
AppLocalizations.of(context).newGroup,
|
||||
@@ -226,22 +224,21 @@ class LeftDrawer extends StatelessWidget {
|
||||
child: ListTile(
|
||||
onTap: () async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final navigator =
|
||||
Navigator.of(context, rootNavigator: true);
|
||||
Navigator.pop(context);
|
||||
final router = GoRouter.of(context);
|
||||
final initNotifier = context.read<InitNotifier>();
|
||||
|
||||
if (!kIsWeb) {
|
||||
const secureStorage = FlutterSecureStorage();
|
||||
await secureStorage.deleteAll();
|
||||
}
|
||||
|
||||
client.disconnectUser();
|
||||
await client.disconnectUser(flushChatPersistence: true);
|
||||
await client.dispose();
|
||||
initNotifier.initData = initNotifier.initData!.copyWith(
|
||||
client:
|
||||
buildStreamChatClient(kDefaultStreamApiKey));
|
||||
|
||||
await navigator.pushNamedAndRemoveUntil(
|
||||
Routes.CHOOSE_USER,
|
||||
ModalRoute.withName(Routes.CHOOSE_USER),
|
||||
);
|
||||
router.goNamed(Routes.CHOOSE_USER.name);
|
||||
},
|
||||
leading: StreamSvgIcon.user(
|
||||
color: StreamChatTheme.of(context)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import 'channel_page.dart';
|
||||
import '../routes/routes.dart';
|
||||
|
||||
class ChannelMediaDisplayScreen extends StatefulWidget {
|
||||
final StreamMessageThemeData messageTheme;
|
||||
@@ -156,23 +156,16 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
startIndex: position,
|
||||
userName: media[position].message.user!.name,
|
||||
onShowMessage: (m, c) async {
|
||||
final client =
|
||||
StreamChat.of(context).client;
|
||||
final navigator = Navigator.of(context);
|
||||
final message = m;
|
||||
final channel = client.channel(
|
||||
c.type,
|
||||
id: c.id,
|
||||
);
|
||||
final router = GoRouter.of(context);
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
navigator.pushNamed(
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
router.pushNamed(
|
||||
Routes.CHANNEL_PAGE.name,
|
||||
params:
|
||||
Routes.CHANNEL_PAGE.params(channel),
|
||||
queryParams:
|
||||
Routes.CHANNEL_PAGE.queryParams(m),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -2,21 +2,9 @@ import 'package:collection/collection.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:example/pages/thread_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'chat_info_screen.dart';
|
||||
import 'group_info_screen.dart';
|
||||
|
||||
class ChannelPageArgs {
|
||||
final Channel? channel;
|
||||
final Message? initialMessage;
|
||||
|
||||
const ChannelPageArgs({
|
||||
this.channel,
|
||||
this.initialMessage,
|
||||
});
|
||||
}
|
||||
|
||||
class ChannelPage extends StatefulWidget {
|
||||
final int? initialScrollIndex;
|
||||
final double? initialAlignment;
|
||||
@@ -63,9 +51,10 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
||||
appBar: StreamChannelHeader(
|
||||
showTypingIndicator: false,
|
||||
onBackPressed: () => GoRouter.of(context).pop(),
|
||||
onImageTap: () async {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
|
||||
if (channel.memberCount == 2 && channel.isDistinct) {
|
||||
final currentUser = StreamChat.of(context).currentUser;
|
||||
@@ -73,34 +62,16 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
(element) => element.user!.id != currentUser!.id,
|
||||
);
|
||||
if (otherUser != null) {
|
||||
final pop = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: ChatInfoScreen(
|
||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
||||
user: otherUser.user,
|
||||
),
|
||||
),
|
||||
),
|
||||
router.pushNamed(
|
||||
Routes.CHAT_INFO_SCREEN.name,
|
||||
params: Routes.CHAT_INFO_SCREEN.params(channel),
|
||||
extra: otherUser.user,
|
||||
);
|
||||
|
||||
if (pop == true) {
|
||||
navigator.pop();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: GroupInfoScreen(
|
||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
||||
),
|
||||
),
|
||||
),
|
||||
GoRouter.of(context).pushNamed(
|
||||
Routes.GROUP_INFO_SCREEN.name,
|
||||
params: Routes.GROUP_INFO_SCREEN.params(channel),
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -117,7 +88,7 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
onMessageSwiped: _reply,
|
||||
messageFilter: defaultFilter,
|
||||
messageBuilder: (context, details, messages, defaultMessage) {
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
return defaultMessage.copyWith(
|
||||
onReplyTap: _reply,
|
||||
onShowMessage: (m, c) async {
|
||||
@@ -130,12 +101,10 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
navigator.pushReplacementNamed(
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
router.goNamed(
|
||||
Routes.CHANNEL_PAGE.name,
|
||||
params: Routes.CHANNEL_PAGE.params(channel),
|
||||
queryParams: Routes.CHANNEL_PAGE.queryParams(message),
|
||||
);
|
||||
},
|
||||
deletedBottomRowBuilder: (context, message) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'package:example/app.dart';
|
||||
import 'package:example/state/init_data.dart';
|
||||
import 'package:example/utils/app_config.dart';
|
||||
import 'package:example/pages/home_page.dart';
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:example/widgets/stream_version.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import '../routes/routes.dart';
|
||||
@@ -94,12 +95,10 @@ class ChooseUserPage extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
|
||||
final client = StreamChatClient(
|
||||
kDefaultStreamApiKey,
|
||||
logLevel: Level.INFO,
|
||||
)..chatPersistenceClient = chatPersistentClient;
|
||||
final client =
|
||||
context.read<InitNotifier>().initData!.client;
|
||||
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
|
||||
await client.connectUser(
|
||||
user,
|
||||
@@ -121,11 +120,7 @@ class ChooseUserPage extends StatelessWidget {
|
||||
value: token,
|
||||
);
|
||||
}
|
||||
navigator.pushNamedAndRemoveUntil(
|
||||
Routes.HOME,
|
||||
ModalRoute.withName(Routes.HOME),
|
||||
arguments: HomePageArgs(client),
|
||||
);
|
||||
router.replaceNamed(Routes.CHANNEL_LIST_PAGE.name);
|
||||
},
|
||||
leading: StreamUserAvatar(
|
||||
user: user,
|
||||
@@ -157,9 +152,8 @@ class ChooseUserPage extends StatelessWidget {
|
||||
);
|
||||
}),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(context, Routes.ADVANCED_OPTIONS);
|
||||
},
|
||||
onTap: () => GoRouter.of(context)
|
||||
.pushNamed(Routes.ADVANCED_OPTIONS.name),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).colorTheme.borders,
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import 'package:example/state/new_group_chat_state.dart';
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'channel_page.dart';
|
||||
import '../routes/routes.dart';
|
||||
|
||||
class GroupChatDetailsScreen extends StatefulWidget {
|
||||
final List<User>? selectedUsers;
|
||||
final NewGroupChatState groupChatState;
|
||||
|
||||
const GroupChatDetailsScreen({
|
||||
Key? key,
|
||||
required this.selectedUsers,
|
||||
required this.groupChatState,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -18,14 +19,12 @@ class GroupChatDetailsScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
late final _selectedUsers = <User>[...?widget.selectedUsers];
|
||||
|
||||
late final TextEditingController _groupNameController =
|
||||
TextEditingController()..addListener(_groupNameListener);
|
||||
|
||||
bool _isGroupNameEmpty = true;
|
||||
|
||||
int get _totalUsers => _selectedUsers.length;
|
||||
int get _totalUsers => widget.groupChatState.users.length;
|
||||
|
||||
void _groupNameListener() {
|
||||
final name = _groupNameController.text;
|
||||
@@ -48,7 +47,7 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
Navigator.pop(context, _selectedUsers);
|
||||
GoRouter.of(context).pop();
|
||||
return false;
|
||||
},
|
||||
child: Scaffold(
|
||||
@@ -123,21 +122,21 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
try {
|
||||
final groupName = _groupNameController.text;
|
||||
final client = StreamChat.of(context).client;
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
final channel = client.channel('messaging',
|
||||
id: const Uuid().v4(),
|
||||
extraData: {
|
||||
'members': [
|
||||
client.state.currentUser!.id,
|
||||
..._selectedUsers.map((e) => e.id),
|
||||
...widget.groupChatState.users
|
||||
.map((e) => e.id),
|
||||
],
|
||||
'name': groupName,
|
||||
});
|
||||
await channel.watch();
|
||||
navigator.pushNamedAndRemoveUntil(
|
||||
Routes.CHANNEL_PAGE,
|
||||
ModalRoute.withName(Routes.CHANNEL_LIST_PAGE),
|
||||
arguments: ChannelPageArgs(channel: channel),
|
||||
router.goNamed(
|
||||
Routes.CHANNEL_PAGE.name,
|
||||
params: Routes.CHANNEL_PAGE.params(channel),
|
||||
);
|
||||
} catch (err) {
|
||||
_showErrorAlert();
|
||||
@@ -192,67 +191,73 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: ListView.separated(
|
||||
itemCount: _selectedUsers.length + 1,
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
color: StreamChatTheme.of(context).colorTheme.borders,
|
||||
),
|
||||
itemBuilder: (_, index) {
|
||||
if (index == _selectedUsers.length) {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.borders,
|
||||
);
|
||||
}
|
||||
final user = _selectedUsers[index];
|
||||
return ListTile(
|
||||
key: ObjectKey(user),
|
||||
leading: StreamUserAvatar(
|
||||
user: user,
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
width: 40,
|
||||
height: 40,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
user.name,
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: Icon(
|
||||
Icons.clear_rounded,
|
||||
AnimatedBuilder(
|
||||
animation: widget.groupChatState,
|
||||
builder: (context, child) {
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: ListView.separated(
|
||||
itemCount: widget.groupChatState.users.length + 1,
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textHighEmphasis,
|
||||
.borders,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
splashRadius: 24,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_selectedUsers.remove(user);
|
||||
});
|
||||
if (_selectedUsers.isEmpty) {
|
||||
Navigator.pop(context, _selectedUsers);
|
||||
itemBuilder: (_, index) {
|
||||
if (index ==
|
||||
widget.groupChatState.users.length) {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.borders,
|
||||
);
|
||||
}
|
||||
final user = widget.groupChatState.users
|
||||
.elementAt(index);
|
||||
return ListTile(
|
||||
key: ObjectKey(user),
|
||||
leading: StreamUserAvatar(
|
||||
user: user,
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
width: 40,
|
||||
height: 40,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
user.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: Icon(
|
||||
Icons.clear_rounded,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textHighEmphasis,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
splashRadius: 24,
|
||||
onPressed: () {
|
||||
widget.groupChatState.removeUser(user);
|
||||
if (widget.groupChatState.users.isEmpty) {
|
||||
GoRouter.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -308,6 +313,7 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: GoRouter.of(context).pop,
|
||||
child: Text(
|
||||
AppLocalizations.of(context).ok,
|
||||
style: StreamChatTheme.of(context)
|
||||
@@ -318,9 +324,6 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
.colorTheme
|
||||
.accentPrimary),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -2,14 +2,14 @@ import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:example/pages/channel_file_display_screen.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'channel_media_display_screen.dart';
|
||||
import 'channel_page.dart';
|
||||
import 'chat_info_screen.dart';
|
||||
import 'pinned_messages_screen.dart';
|
||||
|
||||
class GroupInfoScreen extends StatefulWidget {
|
||||
@@ -647,7 +647,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
onTap: () async {
|
||||
final streamChannel = StreamChannel.of(context);
|
||||
final streamChat = StreamChat.of(context);
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
final res = await showConfirmationBottomSheet(
|
||||
context,
|
||||
title: AppLocalizations.of(context).leaveConversation,
|
||||
@@ -662,7 +662,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
if (res == true) {
|
||||
final channel = streamChannel.channel;
|
||||
await channel.removeMembers([streamChat.currentUser!.id]);
|
||||
navigator.pop();
|
||||
router.pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -865,7 +865,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
AppLocalizations.of(context).viewInfo,
|
||||
() async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
|
||||
final c = client.channel('messaging', extraData: {
|
||||
'members': [
|
||||
@@ -876,16 +876,10 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
|
||||
await c.watch();
|
||||
|
||||
await navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: c,
|
||||
child: ChatInfoScreen(
|
||||
messageTheme: widget.messageTheme,
|
||||
user: user,
|
||||
),
|
||||
),
|
||||
),
|
||||
router.pushNamed(
|
||||
Routes.CHAT_INFO_SCREEN.name,
|
||||
params: Routes.CHAT_INFO_SCREEN.params(c),
|
||||
extra: user,
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -901,7 +895,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
AppLocalizations.of(context).message,
|
||||
() async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
|
||||
final c = client.channel('messaging', extraData: {
|
||||
'members': [
|
||||
@@ -912,13 +906,9 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
|
||||
await c.watch();
|
||||
|
||||
await navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: c,
|
||||
child: const ChannelPage(),
|
||||
),
|
||||
),
|
||||
router.pushNamed(
|
||||
Routes.CHANNEL_PAGE.name,
|
||||
params: Routes.CHANNEL_PAGE.params(c),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -934,7 +924,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
size: 24.0,
|
||||
),
|
||||
AppLocalizations.of(context).removeFromGroup, () async {
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
final res = await showConfirmationBottomSheet(
|
||||
context,
|
||||
title: AppLocalizations.of(context).removeMember,
|
||||
@@ -949,21 +939,23 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
if (res == true) {
|
||||
await channel.removeMembers([user.id]);
|
||||
}
|
||||
navigator.pop();
|
||||
router.pop();
|
||||
},
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.accentError),
|
||||
_buildModalListTile(
|
||||
context,
|
||||
StreamSvgIcon.closeSmall(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textLowEmphasis,
|
||||
size: 24.0,
|
||||
),
|
||||
AppLocalizations.of(context).cancel, () {
|
||||
Navigator.pop(context);
|
||||
}),
|
||||
context,
|
||||
StreamSvgIcon.closeSmall(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textLowEmphasis,
|
||||
size: 24.0,
|
||||
),
|
||||
AppLocalizations.of(context).cancel,
|
||||
() {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:example/pages/channel_page.dart';
|
||||
import 'package:example/utils/notifications_service.dart';
|
||||
import 'package:example/routes/app_routes.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
class MyObserver extends NavigatorObserver {
|
||||
Route? currentRoute;
|
||||
late final StreamSubscription _subscription;
|
||||
|
||||
MyObserver(
|
||||
StreamChatClient client,
|
||||
GlobalKey<NavigatorState> navigatorKey,
|
||||
) {
|
||||
_subscription = client
|
||||
.on(
|
||||
EventType.messageNew,
|
||||
EventType.notificationMessageNew,
|
||||
)
|
||||
.listen((event) {
|
||||
if (event.message?.user?.id == client.state.currentUser?.id) {
|
||||
return;
|
||||
}
|
||||
final channelId = event.channelId;
|
||||
if (currentRoute?.settings.name == Routes.CHANNEL_PAGE) {
|
||||
final args = currentRoute?.settings.arguments as ChannelPageArgs;
|
||||
if (args.channel?.id == channelId) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
showLocalNotification(
|
||||
event,
|
||||
client.state.currentUser!.id,
|
||||
navigatorKey.currentState!.context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didPop(Route route, Route? previousRoute) {
|
||||
currentRoute = route;
|
||||
}
|
||||
|
||||
@override
|
||||
void didPush(Route route, Route? previousRoute) {
|
||||
currentRoute = route;
|
||||
}
|
||||
|
||||
@override
|
||||
void didRemove(Route route, Route? previousRoute) {
|
||||
currentRoute = route;
|
||||
}
|
||||
|
||||
@override
|
||||
void didReplace({Route? newRoute, Route? oldRoute}) {
|
||||
currentRoute = newRoute;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_subscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
class HomePageArgs {
|
||||
final StreamChatClient chatClient;
|
||||
|
||||
HomePageArgs(this.chatClient);
|
||||
}
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({
|
||||
Key? key,
|
||||
required this.chatClient,
|
||||
}) : super(key: key);
|
||||
|
||||
final StreamChatClient chatClient;
|
||||
|
||||
@override
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey();
|
||||
MyObserver? _observer;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamChat(
|
||||
client: widget.chatClient,
|
||||
child: WillPopScope(
|
||||
onWillPop: () async {
|
||||
final canPop = await _navigatorKey.currentState?.maybePop() ?? false;
|
||||
return !canPop;
|
||||
},
|
||||
child: Navigator(
|
||||
key: _navigatorKey,
|
||||
onGenerateRoute: AppRoutes.generateRoute,
|
||||
initialRoute: Routes.CHANNEL_LIST_PAGE,
|
||||
observers: [_observer!],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
_observer?.dispose();
|
||||
_observer = MyObserver(widget.chatClient, _navigatorKey);
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@ import 'dart:async';
|
||||
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'channel_page.dart';
|
||||
import '../widgets/chips_input_text_field.dart';
|
||||
import '../routes/routes.dart';
|
||||
|
||||
@@ -61,13 +61,14 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
_userNameQuery = _controller.text;
|
||||
_isSearchActive = _userNameQuery.isNotEmpty;
|
||||
});
|
||||
|
||||
userListController.filter = Filter.and([
|
||||
if (_userNameQuery.isNotEmpty)
|
||||
Filter.autoComplete('name', _userNameQuery),
|
||||
Filter.notEqual('id', StreamChat.of(context).currentUser!.id),
|
||||
]);
|
||||
userListController.doInitialLoad();
|
||||
}
|
||||
userListController.filter = Filter.and([
|
||||
if (_userNameQuery.isNotEmpty)
|
||||
Filter.autoComplete('name', _userNameQuery),
|
||||
Filter.notEqual('id', StreamChat.of(context).currentUser!.id),
|
||||
]);
|
||||
userListController.doInitialLoad();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -248,9 +249,8 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
if (!_isSearchActive && !_selectedUsers.isNotEmpty)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.NEW_GROUP_CHAT,
|
||||
GoRouter.of(context).pushNamed(
|
||||
Routes.NEW_GROUP_CHAT.name,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
@@ -311,8 +311,6 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: StreamUserListView(
|
||||
controller: userListController,
|
||||
// groupAlphabetically:
|
||||
// _isSearchActive ? false : true,
|
||||
onUserTap: (user) {
|
||||
_controller.clear();
|
||||
if (!_selectedUsers.contains(user)) {
|
||||
@@ -409,11 +407,9 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
return message;
|
||||
},
|
||||
onMessageSent: (m) {
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
ModalRoute.withName(Routes.CHANNEL_LIST_PAGE),
|
||||
arguments: ChannelPageArgs(channel: channel),
|
||||
GoRouter.of(context).goNamed(
|
||||
Routes.CHANNEL_PAGE.name,
|
||||
params: Routes.CHANNEL_PAGE.params(channel!),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:example/state/new_group_chat_state.dart';
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import '../routes/routes.dart';
|
||||
@@ -20,7 +22,7 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
|
||||
String _userNameQuery = '';
|
||||
|
||||
final _selectedUsers = <User>{};
|
||||
final groupChatState = NewGroupChatState();
|
||||
|
||||
bool _isSearchActive = false;
|
||||
|
||||
@@ -69,250 +71,240 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
||||
appBar: AppBar(
|
||||
elevation: 1,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
|
||||
leading: const StreamBackButton(),
|
||||
title: Text(
|
||||
AppLocalizations.of(context).addGroupMembers,
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
if (_selectedUsers.isNotEmpty)
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.arrowRight(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentPrimary,
|
||||
return AnimatedBuilder(
|
||||
animation: groupChatState,
|
||||
builder: (context, child) {
|
||||
final state = groupChatState;
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
||||
appBar: AppBar(
|
||||
elevation: 1,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
|
||||
leading: const StreamBackButton(),
|
||||
title: Text(
|
||||
AppLocalizations.of(context).addGroupMembers,
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
|
||||
fontSize: 16,
|
||||
),
|
||||
onPressed: () async {
|
||||
final updatedList = await Navigator.pushNamed(
|
||||
context,
|
||||
Routes.NEW_GROUP_CHAT_DETAILS,
|
||||
arguments: _selectedUsers.toList(growable: false),
|
||||
);
|
||||
if (updatedList != null) {
|
||||
setState(() {
|
||||
_selectedUsers
|
||||
..clear()
|
||||
..addAll(updatedList as Iterable<User>);
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: StreamConnectionStatusBuilder(
|
||||
statusBuilder: (context, status) {
|
||||
String statusString = '';
|
||||
bool showStatus = true;
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatus.connected:
|
||||
statusString = AppLocalizations.of(context).connected;
|
||||
showStatus = false;
|
||||
break;
|
||||
case ConnectionStatus.connecting:
|
||||
statusString = AppLocalizations.of(context).reconnecting;
|
||||
break;
|
||||
case ConnectionStatus.disconnected:
|
||||
statusString = AppLocalizations.of(context).disconnected;
|
||||
break;
|
||||
}
|
||||
return StreamInfoTile(
|
||||
showMessage: showStatus,
|
||||
tileAnchor: Alignment.topCenter,
|
||||
childAnchor: Alignment.topCenter,
|
||||
message: statusString,
|
||||
child: NestedScrollView(
|
||||
floatHeaderSlivers: true,
|
||||
headerSliverBuilder:
|
||||
(BuildContext context, bool innerBoxIsScrolled) {
|
||||
return <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: SearchTextField(
|
||||
controller: _controller,
|
||||
hintText: AppLocalizations.of(context).search,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
if (state.users.isNotEmpty)
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.arrowRight(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentPrimary,
|
||||
),
|
||||
if (_selectedUsers.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 104,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: _selectedUsers.length,
|
||||
padding: const EdgeInsets.all(8),
|
||||
separatorBuilder: (_, __) =>
|
||||
const SizedBox(width: 16),
|
||||
itemBuilder: (_, index) {
|
||||
final user = _selectedUsers.elementAt(index);
|
||||
return Column(
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
StreamUserAvatar(
|
||||
onlineIndicatorAlignment:
|
||||
const Alignment(0.9, 0.9),
|
||||
user: user,
|
||||
showOnlineStatus: true,
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
constraints:
|
||||
const BoxConstraints.tightFor(
|
||||
height: 64,
|
||||
width: 64,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: -4,
|
||||
right: -4,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (_selectedUsers.contains(user)) {
|
||||
setState(() =>
|
||||
_selectedUsers.remove(user));
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.appBg,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.appBg,
|
||||
),
|
||||
),
|
||||
child: StreamSvgIcon.close(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textHighEmphasis,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
user.name.split(' ')[0],
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverPersistentHeader(
|
||||
pinned: true,
|
||||
delegate: _HeaderDelegate(
|
||||
height: 32,
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
decoration: BoxDecoration(
|
||||
gradient:
|
||||
StreamChatTheme.of(context).colorTheme.bgGradient,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Text(
|
||||
_isSearchActive
|
||||
? '${AppLocalizations.of(context).matchesFor} "$_userNameQuery"'
|
||||
: AppLocalizations.of(context).onThePlatorm,
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textLowEmphasis,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
body: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: StreamUserListView(
|
||||
controller: userListController,
|
||||
itemBuilder: (context, items, index, defaultWidget) {
|
||||
return defaultWidget.copyWith(
|
||||
selected: _selectedUsers.contains(items[index]),
|
||||
onPressed: () async {
|
||||
GoRouter.of(context).pushNamed(
|
||||
Routes.NEW_GROUP_CHAT_DETAILS.name,
|
||||
extra: state,
|
||||
);
|
||||
},
|
||||
onUserTap: (user) {
|
||||
if (!_selectedUsers.contains(user)) {
|
||||
setState(() {
|
||||
_selectedUsers.add(user);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_selectedUsers.remove(user);
|
||||
});
|
||||
}
|
||||
},
|
||||
emptyBuilder: (_) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textLowEmphasis,
|
||||
)
|
||||
],
|
||||
),
|
||||
body: StreamConnectionStatusBuilder(
|
||||
statusBuilder: (context, status) {
|
||||
String statusString = '';
|
||||
bool showStatus = true;
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatus.connected:
|
||||
statusString = AppLocalizations.of(context).connected;
|
||||
showStatus = false;
|
||||
break;
|
||||
case ConnectionStatus.connecting:
|
||||
statusString = AppLocalizations.of(context).reconnecting;
|
||||
break;
|
||||
case ConnectionStatus.disconnected:
|
||||
statusString = AppLocalizations.of(context).disconnected;
|
||||
break;
|
||||
}
|
||||
return StreamInfoTile(
|
||||
showMessage: showStatus,
|
||||
tileAnchor: Alignment.topCenter,
|
||||
childAnchor: Alignment.topCenter,
|
||||
message: statusString,
|
||||
child: NestedScrollView(
|
||||
floatHeaderSlivers: true,
|
||||
headerSliverBuilder:
|
||||
(BuildContext context, bool innerBoxIsScrolled) {
|
||||
return <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: SearchTextField(
|
||||
controller: _controller,
|
||||
hintText: AppLocalizations.of(context).search,
|
||||
),
|
||||
),
|
||||
if (state.users.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 104,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.users.length,
|
||||
padding: const EdgeInsets.all(8),
|
||||
separatorBuilder: (_, __) =>
|
||||
const SizedBox(width: 16),
|
||||
itemBuilder: (_, index) {
|
||||
final user = state.users.elementAt(index);
|
||||
return Column(
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
StreamUserAvatar(
|
||||
onlineIndicatorAlignment:
|
||||
const Alignment(0.9, 0.9),
|
||||
user: user,
|
||||
showOnlineStatus: true,
|
||||
borderRadius:
|
||||
BorderRadius.circular(32),
|
||||
constraints:
|
||||
const BoxConstraints.tightFor(
|
||||
height: 64,
|
||||
width: 64,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: -4,
|
||||
right: -4,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
groupChatState.removeUser(user);
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.appBg,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: StreamChatTheme.of(
|
||||
context)
|
||||
.colorTheme
|
||||
.appBg,
|
||||
),
|
||||
),
|
||||
child: StreamSvgIcon.close(
|
||||
color:
|
||||
StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textHighEmphasis,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
AppLocalizations.of(context)
|
||||
.noUserMatchesTheseKeywords,
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
user.name.split(' ')[0],
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverPersistentHeader(
|
||||
pinned: true,
|
||||
delegate: _HeaderDelegate(
|
||||
height: 32,
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
decoration: BoxDecoration(
|
||||
gradient: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.bgGradient,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Text(
|
||||
_isSearchActive
|
||||
? '${AppLocalizations.of(context).matchesFor} "$_userNameQuery"'
|
||||
: AppLocalizations.of(context).onThePlatorm,
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textLowEmphasis,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
body: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: StreamUserListView(
|
||||
controller: userListController,
|
||||
itemBuilder: (context, items, index, defaultWidget) {
|
||||
return defaultWidget.copyWith(
|
||||
selected: state.users.contains(items[index]),
|
||||
);
|
||||
},
|
||||
onUserTap: groupChatState.addOrRemoveUser,
|
||||
emptyBuilder: (_) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textLowEmphasis,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
AppLocalizations.of(context)
|
||||
.noUserMatchesTheseKeywords,
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.textLowEmphasis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'channel_page.dart';
|
||||
|
||||
class PinnedMessagesScreen extends StatefulWidget {
|
||||
const PinnedMessagesScreen({super.key});
|
||||
|
||||
@@ -103,7 +102,7 @@ class _PinnedMessagesScreenState extends State<PinnedMessagesScreen> {
|
||||
},
|
||||
onMessageTap: (messageResponse) async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
final message = messageResponse.message;
|
||||
final channel = client.channel(
|
||||
messageResponse.channel!.type,
|
||||
@@ -112,12 +111,10 @@ class _PinnedMessagesScreenState extends State<PinnedMessagesScreen> {
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
navigator.pushNamed(
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
router.pushNamed(
|
||||
Routes.CHANNEL_PAGE.name,
|
||||
params: Routes.CHANNEL_PAGE.params(channel),
|
||||
queryParams: Routes.CHANNEL_PAGE.queryParams(message),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'channel_page.dart';
|
||||
|
||||
class UserMentionsPage extends StatefulWidget {
|
||||
const UserMentionsPage({super.key});
|
||||
|
||||
@@ -72,7 +71,7 @@ class _UserMentionsPageState extends State<UserMentionsPage> {
|
||||
},
|
||||
onMessageTap: (messageResponse) async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
final message = messageResponse.message;
|
||||
final channel = client.channel(
|
||||
messageResponse.channel!.type,
|
||||
@@ -81,12 +80,10 @@ class _UserMentionsPageState extends State<UserMentionsPage> {
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
navigator.pushNamed(
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
router.pushNamed(
|
||||
Routes.CHANNEL_PAGE.name,
|
||||
params: Routes.CHANNEL_PAGE.params(channel),
|
||||
queryParams: Routes.CHANNEL_PAGE.queryParams(message),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user