refactor: use gorouter for navigation
This commit is contained in:
@@ -32,7 +32,6 @@
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
@@ -251,6 +251,7 @@
|
||||
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/file_selector_ios/file_selector_ios.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_app_badger/flutter_app_badger.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework",
|
||||
@@ -282,6 +283,7 @@
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_selector_ios.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_app_badger.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework",
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:example/state/init_data.dart';
|
||||
import 'package:example/pages/choose_user_page.dart';
|
||||
import 'package:example/pages/home_page.dart';
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:example/pages/splash_screen.dart';
|
||||
import 'package:example/routes/app_routes.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:example/utils/app_config.dart';
|
||||
import 'package:example/utils/local_notification_observer.dart';
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_localizations/stream_chat_localizations.dart';
|
||||
import 'package:stream_chat_persistence/stream_chat_persistence.dart';
|
||||
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
|
||||
|
||||
import 'routes/app_routes.dart';
|
||||
import 'routes/routes.dart';
|
||||
|
||||
final chatPersistentClient = StreamChatPersistenceClient(
|
||||
logLevel: Level.SEVERE,
|
||||
connectionMode: ConnectionMode.regular,
|
||||
);
|
||||
|
||||
void sampleAppLogHandler(LogRecord record) async {
|
||||
void _sampleAppLogHandler(LogRecord record) async {
|
||||
if (kDebugMode) StreamChatClient.defaultLogHandler(record);
|
||||
|
||||
// report errors to setnry.io
|
||||
@@ -34,14 +37,17 @@ void sampleAppLogHandler(LogRecord record) async {
|
||||
}
|
||||
}
|
||||
|
||||
StreamChatClient buildStreamChatClient(
|
||||
String apiKey, {
|
||||
Level logLevel = Level.INFO,
|
||||
}) {
|
||||
StreamChatClient buildStreamChatClient(String apiKey) {
|
||||
late Level logLevel;
|
||||
if (kDebugMode) {
|
||||
logLevel = Level.INFO;
|
||||
} else {
|
||||
logLevel = Level.SEVERE;
|
||||
}
|
||||
return StreamChatClient(
|
||||
apiKey,
|
||||
logLevel: logLevel,
|
||||
logHandlerFunction: sampleAppLogHandler,
|
||||
logHandlerFunction: _sampleAppLogHandler,
|
||||
)..chatPersistenceClient = chatPersistentClient;
|
||||
}
|
||||
|
||||
@@ -54,7 +60,7 @@ class StreamChatSampleApp extends StatefulWidget {
|
||||
|
||||
class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
||||
with SplashScreenStateMixin, TickerProviderStateMixin {
|
||||
InitData? _initData;
|
||||
final InitNotifier _initNotifier = InitNotifier();
|
||||
|
||||
Future<InitData> _initConnection() async {
|
||||
String? apiKey, userId, token;
|
||||
@@ -66,7 +72,7 @@ class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
||||
token = await secureStorage.read(key: kStreamToken);
|
||||
}
|
||||
|
||||
final client = buildStreamChatClient(apiKey ?? kStreamApiKey);
|
||||
final client = buildStreamChatClient(apiKey ?? kDefaultStreamApiKey);
|
||||
|
||||
if (userId != null && token != null) {
|
||||
await client.connectUser(
|
||||
@@ -87,7 +93,7 @@ class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
||||
_initConnection().then(
|
||||
(initData) {
|
||||
setState(() {
|
||||
_initData = initData;
|
||||
_initNotifier.initData = initData;
|
||||
});
|
||||
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
@@ -107,67 +113,87 @@ class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
||||
super.initState();
|
||||
}
|
||||
|
||||
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey();
|
||||
LocalNotificationObserver? localNotificationObserver;
|
||||
|
||||
/// Conditionally sets up the router and adding an observer for the
|
||||
/// current chat client.
|
||||
GoRouter _setupRouter() {
|
||||
if (localNotificationObserver != null) {
|
||||
localNotificationObserver!.dispose();
|
||||
}
|
||||
localNotificationObserver = LocalNotificationObserver(
|
||||
_initNotifier.initData!.client, _navigatorKey);
|
||||
|
||||
return GoRouter(
|
||||
refreshListenable: _initNotifier,
|
||||
initialLocation: Routes.CHANNEL_LIST_PAGE.path,
|
||||
navigatorKey: _navigatorKey,
|
||||
observers: [localNotificationObserver!],
|
||||
redirect: (context, state) {
|
||||
final loggedIn =
|
||||
_initNotifier.initData?.client.state.currentUser != null;
|
||||
final loggingIn = state.subloc == Routes.CHOOSE_USER.path ||
|
||||
state.subloc == Routes.ADVANCED_OPTIONS.path;
|
||||
|
||||
if (!loggedIn) {
|
||||
return loggingIn ? null : Routes.CHOOSE_USER.path;
|
||||
}
|
||||
|
||||
// if the user is logged in but still on the login page, send them to
|
||||
// the home page
|
||||
if (loggedIn && state.subloc == Routes.CHOOSE_USER.path) {
|
||||
return Routes.CHANNEL_LIST_PAGE.path;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
routes: appRoutes,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
if (_initData != null)
|
||||
PreferenceBuilder<int>(
|
||||
preference: _initData!.preferences.getInt(
|
||||
'theme',
|
||||
defaultValue: 0,
|
||||
),
|
||||
builder: (context, snapshot) => MaterialApp(
|
||||
theme: ThemeData.light(),
|
||||
darkTheme: ThemeData.dark(),
|
||||
themeMode: {
|
||||
-1: ThemeMode.dark,
|
||||
0: ThemeMode.system,
|
||||
1: ThemeMode.light,
|
||||
}[snapshot],
|
||||
supportedLocales: const [
|
||||
Locale('en'),
|
||||
Locale('it'),
|
||||
],
|
||||
localizationsDelegates: const [
|
||||
AppLocalizationsDelegate(),
|
||||
GlobalStreamChatLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
builder: (context, child) => StreamChatConfiguration(
|
||||
data: StreamChatConfigurationData(),
|
||||
child: StreamChatTheme(
|
||||
data: StreamChatThemeData(
|
||||
brightness: Theme.of(context).brightness,
|
||||
if (_initNotifier.initData != null)
|
||||
ChangeNotifierProvider.value(
|
||||
value: _initNotifier,
|
||||
builder: (context, child) => Builder(
|
||||
builder: (context) {
|
||||
context.watch<InitNotifier>(); // rebuild on change
|
||||
return PreferenceBuilder<int>(
|
||||
preference: _initNotifier.initData!.preferences.getInt(
|
||||
'theme',
|
||||
defaultValue: 0,
|
||||
),
|
||||
child: child!,
|
||||
),
|
||||
),
|
||||
onGenerateRoute: AppRoutes.generateRoute,
|
||||
onGenerateInitialRoutes: (initialRouteName) {
|
||||
if (initialRouteName == Routes.HOME) {
|
||||
return [
|
||||
AppRoutes.generateRoute(
|
||||
RouteSettings(
|
||||
name: Routes.HOME,
|
||||
arguments: HomePageArgs(_initData!.client),
|
||||
),
|
||||
)!
|
||||
];
|
||||
}
|
||||
return [
|
||||
AppRoutes.generateRoute(
|
||||
const RouteSettings(
|
||||
name: Routes.CHOOSE_USER,
|
||||
builder: (context, snapshot) => MaterialApp.router(
|
||||
theme: ThemeData.light(),
|
||||
darkTheme: ThemeData.dark(),
|
||||
themeMode: const {
|
||||
-1: ThemeMode.dark,
|
||||
0: ThemeMode.system,
|
||||
1: ThemeMode.light,
|
||||
}[snapshot],
|
||||
supportedLocales: const [
|
||||
Locale('en'),
|
||||
Locale('it'),
|
||||
],
|
||||
localizationsDelegates: const [
|
||||
AppLocalizationsDelegate(),
|
||||
GlobalStreamChatLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
builder: (context, child) => StreamChat(
|
||||
client: _initNotifier.initData!.client,
|
||||
child: child,
|
||||
),
|
||||
)!
|
||||
];
|
||||
routerConfig: _setupRouter(),
|
||||
),
|
||||
);
|
||||
},
|
||||
initialRoute: _initData!.client.state.currentUser == null
|
||||
? Routes.CHOOSE_USER
|
||||
: Routes.HOME,
|
||||
),
|
||||
),
|
||||
if (!animationCompleted) buildAnimation(),
|
||||
@@ -175,10 +201,3 @@ class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InitData {
|
||||
final StreamChatClient client;
|
||||
final StreamingSharedPreferences preferences;
|
||||
|
||||
InitData(this.client, this.preferences);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,135 +1,127 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:example/pages/advanced_options_page.dart';
|
||||
import 'package:example/pages/channel_list_page.dart';
|
||||
import 'package:example/pages/channel_page.dart';
|
||||
import 'package:example/pages/chat_info_screen.dart';
|
||||
import 'package:example/pages/group_chat_details_screen.dart';
|
||||
import 'package:example/pages/group_info_screen.dart';
|
||||
import 'package:example/pages/new_chat_screen.dart';
|
||||
import 'package:example/pages/new_group_chat_screen.dart';
|
||||
import 'package:example/pages/thread_page.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:example/state/new_group_chat_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import '../app.dart';
|
||||
import '../pages/advanced_options_page.dart';
|
||||
import '../pages/channel_page.dart';
|
||||
import '../pages/chat_info_screen.dart';
|
||||
import '../pages/choose_user_page.dart';
|
||||
import '../pages/group_chat_details_screen.dart';
|
||||
import '../pages/group_info_screen.dart';
|
||||
import '../pages/home_page.dart';
|
||||
import '../pages/new_chat_screen.dart';
|
||||
import '../pages/new_group_chat_screen.dart';
|
||||
import '../pages/thread_page.dart';
|
||||
import 'routes.dart';
|
||||
|
||||
class AppRoutes {
|
||||
/// Add entry for new route here
|
||||
static Route<dynamic>? generateRoute(RouteSettings settings) {
|
||||
final args = settings.arguments;
|
||||
switch (settings.name) {
|
||||
case Routes.APP:
|
||||
return MaterialPageRoute(
|
||||
settings: RouteSettings(arguments: args, name: Routes.APP),
|
||||
builder: (_) {
|
||||
return const StreamChatSampleApp();
|
||||
});
|
||||
case Routes.HOME:
|
||||
return MaterialPageRoute(
|
||||
settings: RouteSettings(arguments: args, name: Routes.HOME),
|
||||
builder: (_) {
|
||||
final homePageArgs = args as HomePageArgs;
|
||||
return HomePage(
|
||||
chatClient: homePageArgs.chatClient,
|
||||
);
|
||||
});
|
||||
case Routes.CHOOSE_USER:
|
||||
return MaterialPageRoute(
|
||||
settings: RouteSettings(arguments: args, name: Routes.CHOOSE_USER),
|
||||
builder: (_) {
|
||||
return const ChooseUserPage();
|
||||
});
|
||||
case Routes.ADVANCED_OPTIONS:
|
||||
return MaterialPageRoute(
|
||||
settings:
|
||||
RouteSettings(arguments: args, name: Routes.ADVANCED_OPTIONS),
|
||||
builder: (_) => const AdvancedOptionsPage(),
|
||||
);
|
||||
case Routes.CHANNEL_PAGE:
|
||||
return MaterialPageRoute(
|
||||
settings: RouteSettings(arguments: args, name: Routes.CHANNEL_PAGE),
|
||||
builder: (context) {
|
||||
final channelPageArgs = args as ChannelPageArgs;
|
||||
final initialMessage = channelPageArgs.initialMessage;
|
||||
final appRoutes = [
|
||||
GoRoute(
|
||||
name: Routes.CHANNEL_LIST_PAGE.name,
|
||||
path: Routes.CHANNEL_LIST_PAGE.path,
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
const ChannelListPage(),
|
||||
routes: [
|
||||
GoRoute(
|
||||
name: Routes.CHANNEL_PAGE.name,
|
||||
path: Routes.CHANNEL_PAGE.path,
|
||||
builder: (context, state) {
|
||||
final channel =
|
||||
StreamChat.of(context).client.state.channels[state.params['cid']];
|
||||
final messageId = state.queryParams['mid'];
|
||||
final parentId = state.queryParams['pid'];
|
||||
|
||||
return StreamChannel(
|
||||
channel: channelPageArgs.channel!,
|
||||
initialMessageId: initialMessage?.id,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final parentId = initialMessage?.parentId;
|
||||
Message? parentMessage;
|
||||
if (parentId != null) {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
parentMessage = channel.state!.messages
|
||||
.firstWhereOrNull((it) => it.id == parentId);
|
||||
}
|
||||
if (parentMessage != null) {
|
||||
return ThreadPage(parent: parentMessage);
|
||||
}
|
||||
return ChannelPage(
|
||||
highlightInitialMessage:
|
||||
channelPageArgs.initialMessage != null,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
case Routes.NEW_CHAT:
|
||||
return MaterialPageRoute(
|
||||
settings: RouteSettings(arguments: args, name: Routes.NEW_CHAT),
|
||||
builder: (_) {
|
||||
return const NewChatScreen();
|
||||
});
|
||||
case Routes.NEW_GROUP_CHAT:
|
||||
return MaterialPageRoute(
|
||||
settings:
|
||||
RouteSettings(arguments: args, name: Routes.NEW_GROUP_CHAT),
|
||||
builder: (_) {
|
||||
return const NewGroupChatScreen();
|
||||
});
|
||||
case Routes.NEW_GROUP_CHAT_DETAILS:
|
||||
return MaterialPageRoute(
|
||||
settings: RouteSettings(
|
||||
arguments: args, name: Routes.NEW_GROUP_CHAT_DETAILS),
|
||||
builder: (_) {
|
||||
return GroupChatDetailsScreen(
|
||||
selectedUsers: args as List<User>?,
|
||||
Message? parentMessage;
|
||||
if (parentId != null) {
|
||||
parentMessage = channel?.state!.messages
|
||||
.firstWhereOrNull((it) => it.id == parentId);
|
||||
}
|
||||
|
||||
return StreamChannel(
|
||||
channel: channel!,
|
||||
initialMessageId: messageId,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return (parentMessage != null)
|
||||
? ThreadPage(parent: parentMessage)
|
||||
: ChannelPage(
|
||||
highlightInitialMessage: messageId != null,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
name: Routes.CHAT_INFO_SCREEN.name,
|
||||
path: Routes.CHAT_INFO_SCREEN.path,
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
final channel = StreamChat.of(context)
|
||||
.client
|
||||
.state
|
||||
.channels[state.params['cid']];
|
||||
return StreamChannel(
|
||||
channel: channel!,
|
||||
child: ChatInfoScreen(
|
||||
user: state.extra as User?,
|
||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
||||
),
|
||||
);
|
||||
});
|
||||
case Routes.CHAT_INFO_SCREEN:
|
||||
return MaterialPageRoute(
|
||||
settings:
|
||||
RouteSettings(arguments: args, name: Routes.CHAT_INFO_SCREEN),
|
||||
builder: (context) {
|
||||
return ChatInfoScreen(
|
||||
user: args as User?,
|
||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: Routes.GROUP_INFO_SCREEN.name,
|
||||
path: Routes.GROUP_INFO_SCREEN.path,
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
final channel = StreamChat.of(context)
|
||||
.client
|
||||
.state
|
||||
.channels[state.params['cid']];
|
||||
return StreamChannel(
|
||||
channel: channel!,
|
||||
child: GroupInfoScreen(
|
||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
||||
),
|
||||
);
|
||||
});
|
||||
case Routes.GROUP_INFO_SCREEN:
|
||||
return MaterialPageRoute(
|
||||
settings:
|
||||
RouteSettings(arguments: args, name: Routes.GROUP_INFO_SCREEN),
|
||||
builder: (context) {
|
||||
return GroupInfoScreen(
|
||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
||||
);
|
||||
});
|
||||
case Routes.CHANNEL_LIST_PAGE:
|
||||
return MaterialPageRoute(
|
||||
settings:
|
||||
RouteSettings(arguments: args, name: Routes.CHANNEL_LIST_PAGE),
|
||||
builder: (context) {
|
||||
return const ChannelListPage();
|
||||
});
|
||||
// Default case, should not reach here.
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
name: Routes.NEW_CHAT.name,
|
||||
path: Routes.NEW_CHAT.path,
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
return const NewChatScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: Routes.NEW_GROUP_CHAT.name,
|
||||
path: Routes.NEW_GROUP_CHAT.path,
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
return const NewGroupChatScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: Routes.NEW_GROUP_CHAT_DETAILS.name,
|
||||
path: Routes.NEW_GROUP_CHAT_DETAILS.path,
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
final groupChatState = state.extra as NewGroupChatState;
|
||||
return GroupChatDetailsScreen(groupChatState: groupChatState);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: Routes.CHOOSE_USER.name,
|
||||
path: Routes.CHOOSE_USER.path,
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
const ChooseUserPage(),
|
||||
),
|
||||
GoRoute(
|
||||
name: Routes.ADVANCED_OPTIONS.name,
|
||||
path: Routes.ADVANCED_OPTIONS.path,
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
const AdvancedOptionsPage(),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -1,16 +1,43 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// Application routes
|
||||
abstract class Routes {
|
||||
static const String APP = '/app';
|
||||
static const String HOME = '/home';
|
||||
static const String CHOOSE_USER = '/choose_user';
|
||||
static const String ADVANCED_OPTIONS = '/advance_options';
|
||||
static const String CHANNEL_PAGE = '/channel_page';
|
||||
static const String NEW_CHAT = '/new_chat';
|
||||
static const String NEW_GROUP_CHAT = '/new_group_chat';
|
||||
static const String NEW_GROUP_CHAT_DETAILS = '/new_group_chat_details';
|
||||
static const String CHAT_INFO_SCREEN = '/chat_info_screen';
|
||||
static const String GROUP_INFO_SCREEN = '/group_info_screen';
|
||||
static const String CHANNEL_LIST_PAGE = '/channel_list_page';
|
||||
static const RouteConfig CHOOSE_USER =
|
||||
RouteConfig(name: 'choose_user', path: '/users');
|
||||
static const RouteConfig ADVANCED_OPTIONS =
|
||||
RouteConfig(name: 'advanced_options', path: '/options');
|
||||
static const ChannelRouteConfig CHANNEL_PAGE =
|
||||
ChannelRouteConfig(name: 'channel_page', path: 'channel/:cid');
|
||||
static const RouteConfig NEW_CHAT =
|
||||
RouteConfig(name: 'new_chat', path: '/new_chat');
|
||||
static const RouteConfig NEW_GROUP_CHAT =
|
||||
RouteConfig(name: 'new_group_chat', path: '/new_group_chat');
|
||||
static const RouteConfig NEW_GROUP_CHAT_DETAILS = RouteConfig(
|
||||
name: 'new_group_chat_details', path: '/new_group_chat_details');
|
||||
static const ChannelRouteConfig CHAT_INFO_SCREEN =
|
||||
ChannelRouteConfig(name: 'chat_info_screen', path: 'chat_info_screen');
|
||||
static const ChannelRouteConfig GROUP_INFO_SCREEN =
|
||||
ChannelRouteConfig(name: 'group_info_screen', path: 'group_info_screen');
|
||||
static const RouteConfig CHANNEL_LIST_PAGE =
|
||||
RouteConfig(name: 'channel_list_page', path: '/channels');
|
||||
}
|
||||
|
||||
class RouteConfig {
|
||||
final String name;
|
||||
final String path;
|
||||
|
||||
const RouteConfig({required this.name, required this.path});
|
||||
}
|
||||
|
||||
class ChannelRouteConfig extends RouteConfig {
|
||||
const ChannelRouteConfig({required super.name, required super.path});
|
||||
|
||||
Map<String, String> params(Channel channel) => {'cid': channel.cid!};
|
||||
|
||||
Map<String, String> queryParams(Message message) => {
|
||||
'mid': message.id,
|
||||
if (message.parentId != null) 'pid': message.parentId!
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
|
||||
|
||||
/// {@template init_notifier}
|
||||
/// [ChangeNotifier] to store [InitData] and notify listeners on change.
|
||||
/// {@endtemplate}
|
||||
class InitNotifier extends ChangeNotifier {
|
||||
/// {@macro init_notifier}
|
||||
InitNotifier();
|
||||
|
||||
InitData? _initData;
|
||||
|
||||
set initData(InitData? data) {
|
||||
_initData = data;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
InitData? get initData => _initData;
|
||||
}
|
||||
|
||||
/// {@template init_data}
|
||||
/// Manages the initialization data for the sample application.
|
||||
///
|
||||
/// Stores a reference to the current [StreamChatClient].
|
||||
/// {@endtemplate}
|
||||
class InitData {
|
||||
/// {@macro init_data}
|
||||
InitData(this.client, this.preferences);
|
||||
|
||||
final StreamChatClient client;
|
||||
final StreamingSharedPreferences preferences;
|
||||
|
||||
InitData copyWith({required StreamChatClient client}) =>
|
||||
InitData(client, preferences);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
class NewGroupChatState extends ChangeNotifier {
|
||||
final users = <User>{};
|
||||
|
||||
void addUser(User user) {
|
||||
if (!users.contains(user)) {
|
||||
users.add(user);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void removeUser(User user) {
|
||||
if (users.contains(user)) {
|
||||
users.remove(user);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void addOrRemoveUser(User user) {
|
||||
if (users.contains(user)) {
|
||||
users.remove(user);
|
||||
} else {
|
||||
users.add(user);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:example/utils/notifications_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
class LocalNotificationObserver extends NavigatorObserver {
|
||||
Route? currentRoute;
|
||||
late final StreamSubscription _subscription;
|
||||
|
||||
LocalNotificationObserver(
|
||||
StreamChatClient client,
|
||||
GlobalKey<NavigatorState> navigatorKey,
|
||||
) {
|
||||
_subscription = client
|
||||
.on(
|
||||
EventType.messageNew,
|
||||
EventType.notificationMessageNew,
|
||||
)
|
||||
.listen((event) {
|
||||
_handleEvent(event, client, navigatorKey);
|
||||
});
|
||||
}
|
||||
|
||||
void _handleEvent(Event event, StreamChatClient client,
|
||||
GlobalKey<NavigatorState> navigatorKey) {
|
||||
if (event.message?.user?.id == client.state.currentUser?.id) {
|
||||
return;
|
||||
}
|
||||
final channelId = event.cid;
|
||||
if (currentRoute?.settings.name == Routes.CHANNEL_PAGE.name) {
|
||||
final args = currentRoute?.settings.arguments as Map<String, String>;
|
||||
if (args['cid'] == 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();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:example/pages/channel_page.dart';
|
||||
import 'package:example/utils/localizations.dart';
|
||||
import 'package:example/routes/routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart'
|
||||
hide Message;
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
void showLocalNotification(
|
||||
@@ -35,7 +35,7 @@ void showLocalNotification(
|
||||
onSelectNotification: (channelCid) async {
|
||||
if (channelCid != null) {
|
||||
final client = StreamChat.of(context).client;
|
||||
final navigator = Navigator.of(context);
|
||||
final router = GoRouter.of(context);
|
||||
|
||||
var channel = client.state.channels[channelCid];
|
||||
|
||||
@@ -50,11 +50,9 @@ void showLocalNotification(
|
||||
await channel.watch();
|
||||
}
|
||||
|
||||
navigator.pushNamed(
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
),
|
||||
router.pushNamed(
|
||||
Routes.CHANNEL_PAGE.name,
|
||||
params: Routes.CHANNEL_PAGE.params(channel),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,10 +5,10 @@ import 'package:example/routes/routes.dart';
|
||||
import 'package:example/widgets/search_text_field.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
|
||||
import '../pages/channel_page.dart';
|
||||
import '../pages/chat_info_screen.dart';
|
||||
import '../pages/group_info_screen.dart';
|
||||
|
||||
@@ -150,7 +150,7 @@ class _ChannelList extends State<ChannelList> {
|
||||
final messageResponse = messageResponses[index];
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
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,
|
||||
@@ -159,12 +159,10 @@ class _ChannelList extends State<ChannelList> {
|
||||
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),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -265,12 +263,9 @@ class _ChannelList extends State<ChannelList> {
|
||||
);
|
||||
},
|
||||
onChannelTap: (channel) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
),
|
||||
GoRouter.of(context).pushNamed(
|
||||
Routes.CHANNEL_PAGE.name,
|
||||
params: Routes.CHANNEL_PAGE.params(channel),
|
||||
);
|
||||
},
|
||||
emptyBuilder: (_) {
|
||||
@@ -286,10 +281,8 @@ class _ChannelList extends State<ChannelList> {
|
||||
),
|
||||
emptyTitle: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.NEW_CHAT,
|
||||
);
|
||||
GoRouter.of(context)
|
||||
.pushNamed(Routes.NEW_CHAT.name);
|
||||
},
|
||||
child: Text(
|
||||
'Start a chat',
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||
929AC42CE2CDA27FE9235234 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||
CDF4BDDD28F9A6920059DB87 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = "<group>"; };
|
||||
D4F4A1B883B395E86A7B745A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -142,6 +143,7 @@
|
||||
33FAB671232836740065AC1E /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CDF4BDDD28F9A6920059DB87 /* RunnerDebug.entitlements */,
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
|
||||
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
|
||||
@@ -159,7 +161,6 @@
|
||||
929AC42CE2CDA27FE9235234 /* Pods-Runner.release.xcconfig */,
|
||||
D4F4A1B883B395E86A7B745A /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -209,7 +210,6 @@
|
||||
33CC10EC2044A3C60003C045 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
LastSwiftMigration = 1100;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.Sandbox = {
|
||||
enabled = 1;
|
||||
@@ -419,6 +419,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -544,9 +545,11 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -565,8 +568,10 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -27,7 +27,7 @@ dependencies:
|
||||
path: packages/stream_chat_localizations
|
||||
flutter_local_notifications: ^9.0.0
|
||||
flutter_svg: ^1.0.3
|
||||
flutter_secure_storage: ^5.0.2
|
||||
flutter_secure_storage: ^6.0.0
|
||||
yaml: ^3.1.0
|
||||
uuid: ^3.0.5
|
||||
streaming_shared_preferences: ^2.0.0
|
||||
@@ -35,6 +35,8 @@ dependencies:
|
||||
collection: ^1.15.0
|
||||
sentry_flutter: ^6.5.0
|
||||
flutter_slidable: ^2.0.0
|
||||
go_router: ^5.0.5
|
||||
provider: ^6.0.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_launcher_icons: ^0.9.2
|
||||
|
||||
Reference in New Issue
Block a user