refactor: use gorouter for navigation
This commit is contained in:
@@ -32,7 +32,6 @@
|
|||||||
/build/
|
/build/
|
||||||
|
|
||||||
# Web related
|
# Web related
|
||||||
lib/generated_plugin_registrant.dart
|
|
||||||
|
|
||||||
# Symbolication related
|
# Symbolication related
|
||||||
app.*.symbols
|
app.*.symbols
|
||||||
|
|||||||
@@ -251,6 +251,7 @@
|
|||||||
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
|
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
|
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.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_app_badger/flutter_app_badger.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework",
|
"${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.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}/SwiftyGif.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.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_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_app_badger.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework",
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:example/state/init_data.dart';
|
||||||
import 'package:example/pages/choose_user_page.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/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/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.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:sentry_flutter/sentry_flutter.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_localizations/stream_chat_localizations.dart';
|
import 'package:stream_chat_localizations/stream_chat_localizations.dart';
|
||||||
import 'package:stream_chat_persistence/stream_chat_persistence.dart';
|
import 'package:stream_chat_persistence/stream_chat_persistence.dart';
|
||||||
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
|
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
|
||||||
|
|
||||||
import 'routes/app_routes.dart';
|
|
||||||
import 'routes/routes.dart';
|
|
||||||
|
|
||||||
final chatPersistentClient = StreamChatPersistenceClient(
|
final chatPersistentClient = StreamChatPersistenceClient(
|
||||||
logLevel: Level.SEVERE,
|
logLevel: Level.SEVERE,
|
||||||
connectionMode: ConnectionMode.regular,
|
connectionMode: ConnectionMode.regular,
|
||||||
);
|
);
|
||||||
|
|
||||||
void sampleAppLogHandler(LogRecord record) async {
|
void _sampleAppLogHandler(LogRecord record) async {
|
||||||
if (kDebugMode) StreamChatClient.defaultLogHandler(record);
|
if (kDebugMode) StreamChatClient.defaultLogHandler(record);
|
||||||
|
|
||||||
// report errors to setnry.io
|
// report errors to setnry.io
|
||||||
@@ -34,14 +37,17 @@ void sampleAppLogHandler(LogRecord record) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamChatClient buildStreamChatClient(
|
StreamChatClient buildStreamChatClient(String apiKey) {
|
||||||
String apiKey, {
|
late Level logLevel;
|
||||||
Level logLevel = Level.INFO,
|
if (kDebugMode) {
|
||||||
}) {
|
logLevel = Level.INFO;
|
||||||
|
} else {
|
||||||
|
logLevel = Level.SEVERE;
|
||||||
|
}
|
||||||
return StreamChatClient(
|
return StreamChatClient(
|
||||||
apiKey,
|
apiKey,
|
||||||
logLevel: logLevel,
|
logLevel: logLevel,
|
||||||
logHandlerFunction: sampleAppLogHandler,
|
logHandlerFunction: _sampleAppLogHandler,
|
||||||
)..chatPersistenceClient = chatPersistentClient;
|
)..chatPersistenceClient = chatPersistentClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +60,7 @@ class StreamChatSampleApp extends StatefulWidget {
|
|||||||
|
|
||||||
class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
||||||
with SplashScreenStateMixin, TickerProviderStateMixin {
|
with SplashScreenStateMixin, TickerProviderStateMixin {
|
||||||
InitData? _initData;
|
final InitNotifier _initNotifier = InitNotifier();
|
||||||
|
|
||||||
Future<InitData> _initConnection() async {
|
Future<InitData> _initConnection() async {
|
||||||
String? apiKey, userId, token;
|
String? apiKey, userId, token;
|
||||||
@@ -66,7 +72,7 @@ class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
|||||||
token = await secureStorage.read(key: kStreamToken);
|
token = await secureStorage.read(key: kStreamToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
final client = buildStreamChatClient(apiKey ?? kStreamApiKey);
|
final client = buildStreamChatClient(apiKey ?? kDefaultStreamApiKey);
|
||||||
|
|
||||||
if (userId != null && token != null) {
|
if (userId != null && token != null) {
|
||||||
await client.connectUser(
|
await client.connectUser(
|
||||||
@@ -87,7 +93,7 @@ class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
|||||||
_initConnection().then(
|
_initConnection().then(
|
||||||
(initData) {
|
(initData) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_initData = initData;
|
_initNotifier.initData = initData;
|
||||||
});
|
});
|
||||||
|
|
||||||
final now = DateTime.now().millisecondsSinceEpoch;
|
final now = DateTime.now().millisecondsSinceEpoch;
|
||||||
@@ -107,67 +113,87 @@ class _StreamChatSampleAppState extends State<StreamChatSampleApp>
|
|||||||
super.initState();
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(
|
return Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (_initData != null)
|
if (_initNotifier.initData != null)
|
||||||
PreferenceBuilder<int>(
|
ChangeNotifierProvider.value(
|
||||||
preference: _initData!.preferences.getInt(
|
value: _initNotifier,
|
||||||
'theme',
|
builder: (context, child) => Builder(
|
||||||
defaultValue: 0,
|
builder: (context) {
|
||||||
),
|
context.watch<InitNotifier>(); // rebuild on change
|
||||||
builder: (context, snapshot) => MaterialApp(
|
return PreferenceBuilder<int>(
|
||||||
theme: ThemeData.light(),
|
preference: _initNotifier.initData!.preferences.getInt(
|
||||||
darkTheme: ThemeData.dark(),
|
'theme',
|
||||||
themeMode: {
|
defaultValue: 0,
|
||||||
-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,
|
|
||||||
),
|
),
|
||||||
child: child!,
|
builder: (context, snapshot) => MaterialApp.router(
|
||||||
),
|
theme: ThemeData.light(),
|
||||||
),
|
darkTheme: ThemeData.dark(),
|
||||||
onGenerateRoute: AppRoutes.generateRoute,
|
themeMode: const {
|
||||||
onGenerateInitialRoutes: (initialRouteName) {
|
-1: ThemeMode.dark,
|
||||||
if (initialRouteName == Routes.HOME) {
|
0: ThemeMode.system,
|
||||||
return [
|
1: ThemeMode.light,
|
||||||
AppRoutes.generateRoute(
|
}[snapshot],
|
||||||
RouteSettings(
|
supportedLocales: const [
|
||||||
name: Routes.HOME,
|
Locale('en'),
|
||||||
arguments: HomePageArgs(_initData!.client),
|
Locale('it'),
|
||||||
),
|
],
|
||||||
)!
|
localizationsDelegates: const [
|
||||||
];
|
AppLocalizationsDelegate(),
|
||||||
}
|
GlobalStreamChatLocalizations.delegate,
|
||||||
return [
|
GlobalMaterialLocalizations.delegate,
|
||||||
AppRoutes.generateRoute(
|
GlobalWidgetsLocalizations.delegate,
|
||||||
const RouteSettings(
|
],
|
||||||
name: Routes.CHOOSE_USER,
|
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(),
|
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/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/utils/localizations.dart';
|
||||||
import 'package:example/routes/routes.dart';
|
import 'package:example/routes/routes.dart';
|
||||||
import 'package:example/widgets/stream_version.dart';
|
import 'package:example/widgets/stream_version.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.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:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'choose_user_page.dart';
|
import 'package:example/pages/choose_user_page.dart';
|
||||||
|
|
||||||
class AdvancedOptionsPage extends StatefulWidget {
|
class AdvancedOptionsPage extends StatefulWidget {
|
||||||
const AdvancedOptionsPage({super.key});
|
const AdvancedOptionsPage({super.key});
|
||||||
@@ -41,6 +43,86 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
|||||||
super.dispose();
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -260,6 +342,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
onPressed: _login,
|
||||||
child: Text(
|
child: Text(
|
||||||
AppLocalizations.of(context).login,
|
AppLocalizations.of(context).login,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -271,84 +354,6 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
|||||||
: Colors.white,
|
: 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(),
|
const StreamVersion(),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:example/utils/localizations.dart';
|
import 'package:example/app.dart';
|
||||||
import 'package:example/routes/routes.dart';
|
import 'package:example/state/init_data.dart';
|
||||||
import 'package:example/pages/user_mentions_page.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/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app_badger/flutter_app_badger.dart';
|
import 'package:flutter_app_badger/flutter_app_badger.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.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:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
|
import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
|
||||||
|
|
||||||
import 'package:example/widgets/channel_list.dart';
|
|
||||||
|
|
||||||
class ChannelListPage extends StatefulWidget {
|
class ChannelListPage extends StatefulWidget {
|
||||||
const ChannelListPage({
|
const ChannelListPage({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -71,12 +75,10 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
||||||
appBar: StreamChannelListHeader(
|
appBar: StreamChannelListHeader(
|
||||||
onNewChatButtonTap: () {
|
onNewChatButtonTap: () =>
|
||||||
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
GoRouter.of(context).pushNamed(Routes.NEW_CHAT.name),
|
||||||
},
|
preNavigationCallback: () =>
|
||||||
preNavigationCallback: () {
|
FocusScope.of(context).requestFocus(FocusNode()),
|
||||||
FocusScope.of(context).requestFocus(FocusNode());
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
drawer: LeftDrawer(
|
drawer: LeftDrawer(
|
||||||
user: user,
|
user: user,
|
||||||
@@ -188,10 +190,8 @@ class LeftDrawer extends StatelessWidget {
|
|||||||
.withOpacity(.5),
|
.withOpacity(.5),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.popAndPushNamed(
|
Navigator.of(context).pop();
|
||||||
context,
|
GoRouter.of(context).pushNamed(Routes.NEW_CHAT.name);
|
||||||
Routes.NEW_CHAT,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
title: Text(
|
title: Text(
|
||||||
AppLocalizations.of(context).newDirectMessage,
|
AppLocalizations.of(context).newDirectMessage,
|
||||||
@@ -208,10 +208,8 @@ class LeftDrawer extends StatelessWidget {
|
|||||||
.withOpacity(.5),
|
.withOpacity(.5),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.popAndPushNamed(
|
Navigator.of(context).pop();
|
||||||
context,
|
GoRouter.of(context).pushNamed(Routes.NEW_GROUP_CHAT.name);
|
||||||
Routes.NEW_GROUP_CHAT,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
title: Text(
|
title: Text(
|
||||||
AppLocalizations.of(context).newGroup,
|
AppLocalizations.of(context).newGroup,
|
||||||
@@ -226,22 +224,21 @@ class LeftDrawer extends StatelessWidget {
|
|||||||
child: ListTile(
|
child: ListTile(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
final navigator =
|
final router = GoRouter.of(context);
|
||||||
Navigator.of(context, rootNavigator: true);
|
final initNotifier = context.read<InitNotifier>();
|
||||||
Navigator.pop(context);
|
|
||||||
|
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
const secureStorage = FlutterSecureStorage();
|
const secureStorage = FlutterSecureStorage();
|
||||||
await secureStorage.deleteAll();
|
await secureStorage.deleteAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
client.disconnectUser();
|
await client.disconnectUser(flushChatPersistence: true);
|
||||||
await client.dispose();
|
await client.dispose();
|
||||||
|
initNotifier.initData = initNotifier.initData!.copyWith(
|
||||||
|
client:
|
||||||
|
buildStreamChatClient(kDefaultStreamApiKey));
|
||||||
|
|
||||||
await navigator.pushNamedAndRemoveUntil(
|
router.goNamed(Routes.CHOOSE_USER.name);
|
||||||
Routes.CHOOSE_USER,
|
|
||||||
ModalRoute.withName(Routes.CHOOSE_USER),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
leading: StreamSvgIcon.user(
|
leading: StreamSvgIcon.user(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:example/utils/localizations.dart';
|
import 'package:example/utils/localizations.dart';
|
||||||
import 'package:example/routes/routes.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
import 'channel_page.dart';
|
import '../routes/routes.dart';
|
||||||
|
|
||||||
class ChannelMediaDisplayScreen extends StatefulWidget {
|
class ChannelMediaDisplayScreen extends StatefulWidget {
|
||||||
final StreamMessageThemeData messageTheme;
|
final StreamMessageThemeData messageTheme;
|
||||||
@@ -156,23 +156,16 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
|||||||
startIndex: position,
|
startIndex: position,
|
||||||
userName: media[position].message.user!.name,
|
userName: media[position].message.user!.name,
|
||||||
onShowMessage: (m, c) async {
|
onShowMessage: (m, c) async {
|
||||||
final client =
|
final router = GoRouter.of(context);
|
||||||
StreamChat.of(context).client;
|
|
||||||
final navigator = Navigator.of(context);
|
|
||||||
final message = m;
|
|
||||||
final channel = client.channel(
|
|
||||||
c.type,
|
|
||||||
id: c.id,
|
|
||||||
);
|
|
||||||
if (channel.state == null) {
|
if (channel.state == null) {
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
}
|
}
|
||||||
navigator.pushNamed(
|
router.pushNamed(
|
||||||
Routes.CHANNEL_PAGE,
|
Routes.CHANNEL_PAGE.name,
|
||||||
arguments: ChannelPageArgs(
|
params:
|
||||||
channel: channel,
|
Routes.CHANNEL_PAGE.params(channel),
|
||||||
initialMessage: message,
|
queryParams:
|
||||||
),
|
Routes.CHANNEL_PAGE.queryParams(m),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,21 +2,9 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:example/routes/routes.dart';
|
import 'package:example/routes/routes.dart';
|
||||||
import 'package:example/pages/thread_page.dart';
|
import 'package:example/pages/thread_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.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 {
|
class ChannelPage extends StatefulWidget {
|
||||||
final int? initialScrollIndex;
|
final int? initialScrollIndex;
|
||||||
final double? initialAlignment;
|
final double? initialAlignment;
|
||||||
@@ -63,9 +51,10 @@ class _ChannelPageState extends State<ChannelPage> {
|
|||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
||||||
appBar: StreamChannelHeader(
|
appBar: StreamChannelHeader(
|
||||||
showTypingIndicator: false,
|
showTypingIndicator: false,
|
||||||
|
onBackPressed: () => GoRouter.of(context).pop(),
|
||||||
onImageTap: () async {
|
onImageTap: () async {
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
|
|
||||||
if (channel.memberCount == 2 && channel.isDistinct) {
|
if (channel.memberCount == 2 && channel.isDistinct) {
|
||||||
final currentUser = StreamChat.of(context).currentUser;
|
final currentUser = StreamChat.of(context).currentUser;
|
||||||
@@ -73,34 +62,16 @@ class _ChannelPageState extends State<ChannelPage> {
|
|||||||
(element) => element.user!.id != currentUser!.id,
|
(element) => element.user!.id != currentUser!.id,
|
||||||
);
|
);
|
||||||
if (otherUser != null) {
|
if (otherUser != null) {
|
||||||
final pop = await Navigator.push(
|
router.pushNamed(
|
||||||
context,
|
Routes.CHAT_INFO_SCREEN.name,
|
||||||
MaterialPageRoute(
|
params: Routes.CHAT_INFO_SCREEN.params(channel),
|
||||||
builder: (context) => StreamChannel(
|
extra: otherUser.user,
|
||||||
channel: channel,
|
|
||||||
child: ChatInfoScreen(
|
|
||||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
|
||||||
user: otherUser.user,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pop == true) {
|
|
||||||
navigator.pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await Navigator.push(
|
GoRouter.of(context).pushNamed(
|
||||||
context,
|
Routes.GROUP_INFO_SCREEN.name,
|
||||||
MaterialPageRoute(
|
params: Routes.GROUP_INFO_SCREEN.params(channel),
|
||||||
builder: (context) => StreamChannel(
|
|
||||||
channel: channel,
|
|
||||||
child: GroupInfoScreen(
|
|
||||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -117,7 +88,7 @@ class _ChannelPageState extends State<ChannelPage> {
|
|||||||
onMessageSwiped: _reply,
|
onMessageSwiped: _reply,
|
||||||
messageFilter: defaultFilter,
|
messageFilter: defaultFilter,
|
||||||
messageBuilder: (context, details, messages, defaultMessage) {
|
messageBuilder: (context, details, messages, defaultMessage) {
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
return defaultMessage.copyWith(
|
return defaultMessage.copyWith(
|
||||||
onReplyTap: _reply,
|
onReplyTap: _reply,
|
||||||
onShowMessage: (m, c) async {
|
onShowMessage: (m, c) async {
|
||||||
@@ -130,12 +101,10 @@ class _ChannelPageState extends State<ChannelPage> {
|
|||||||
if (channel.state == null) {
|
if (channel.state == null) {
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
}
|
}
|
||||||
navigator.pushReplacementNamed(
|
router.goNamed(
|
||||||
Routes.CHANNEL_PAGE,
|
Routes.CHANNEL_PAGE.name,
|
||||||
arguments: ChannelPageArgs(
|
params: Routes.CHANNEL_PAGE.params(channel),
|
||||||
channel: channel,
|
queryParams: Routes.CHANNEL_PAGE.queryParams(message),
|
||||||
initialMessage: message,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
deletedBottomRowBuilder: (context, 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/utils/app_config.dart';
|
||||||
import 'package:example/pages/home_page.dart';
|
|
||||||
import 'package:example/utils/localizations.dart';
|
import 'package:example/utils/localizations.dart';
|
||||||
import 'package:example/widgets/stream_version.dart';
|
import 'package:example/widgets/stream_version.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.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 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import '../routes/routes.dart';
|
import '../routes/routes.dart';
|
||||||
@@ -94,12 +95,10 @@ class ChooseUserPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final client = StreamChatClient(
|
final client =
|
||||||
kDefaultStreamApiKey,
|
context.read<InitNotifier>().initData!.client;
|
||||||
logLevel: Level.INFO,
|
|
||||||
)..chatPersistenceClient = chatPersistentClient;
|
|
||||||
|
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
|
|
||||||
await client.connectUser(
|
await client.connectUser(
|
||||||
user,
|
user,
|
||||||
@@ -121,11 +120,7 @@ class ChooseUserPage extends StatelessWidget {
|
|||||||
value: token,
|
value: token,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
navigator.pushNamedAndRemoveUntil(
|
router.replaceNamed(Routes.CHANNEL_LIST_PAGE.name);
|
||||||
Routes.HOME,
|
|
||||||
ModalRoute.withName(Routes.HOME),
|
|
||||||
arguments: HomePageArgs(client),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
leading: StreamUserAvatar(
|
leading: StreamUserAvatar(
|
||||||
user: user,
|
user: user,
|
||||||
@@ -157,9 +152,8 @@ class ChooseUserPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () => GoRouter.of(context)
|
||||||
Navigator.pushNamed(context, Routes.ADVANCED_OPTIONS);
|
.pushNamed(Routes.ADVANCED_OPTIONS.name),
|
||||||
},
|
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
StreamChatTheme.of(context).colorTheme.borders,
|
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:example/utils/localizations.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'channel_page.dart';
|
|
||||||
import '../routes/routes.dart';
|
import '../routes/routes.dart';
|
||||||
|
|
||||||
class GroupChatDetailsScreen extends StatefulWidget {
|
class GroupChatDetailsScreen extends StatefulWidget {
|
||||||
final List<User>? selectedUsers;
|
final NewGroupChatState groupChatState;
|
||||||
|
|
||||||
const GroupChatDetailsScreen({
|
const GroupChatDetailsScreen({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.selectedUsers,
|
required this.groupChatState,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -18,14 +19,12 @@ class GroupChatDetailsScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||||
late final _selectedUsers = <User>[...?widget.selectedUsers];
|
|
||||||
|
|
||||||
late final TextEditingController _groupNameController =
|
late final TextEditingController _groupNameController =
|
||||||
TextEditingController()..addListener(_groupNameListener);
|
TextEditingController()..addListener(_groupNameListener);
|
||||||
|
|
||||||
bool _isGroupNameEmpty = true;
|
bool _isGroupNameEmpty = true;
|
||||||
|
|
||||||
int get _totalUsers => _selectedUsers.length;
|
int get _totalUsers => widget.groupChatState.users.length;
|
||||||
|
|
||||||
void _groupNameListener() {
|
void _groupNameListener() {
|
||||||
final name = _groupNameController.text;
|
final name = _groupNameController.text;
|
||||||
@@ -48,7 +47,7 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
Navigator.pop(context, _selectedUsers);
|
GoRouter.of(context).pop();
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
@@ -123,21 +122,21 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
|||||||
try {
|
try {
|
||||||
final groupName = _groupNameController.text;
|
final groupName = _groupNameController.text;
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
final channel = client.channel('messaging',
|
final channel = client.channel('messaging',
|
||||||
id: const Uuid().v4(),
|
id: const Uuid().v4(),
|
||||||
extraData: {
|
extraData: {
|
||||||
'members': [
|
'members': [
|
||||||
client.state.currentUser!.id,
|
client.state.currentUser!.id,
|
||||||
..._selectedUsers.map((e) => e.id),
|
...widget.groupChatState.users
|
||||||
|
.map((e) => e.id),
|
||||||
],
|
],
|
||||||
'name': groupName,
|
'name': groupName,
|
||||||
});
|
});
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
navigator.pushNamedAndRemoveUntil(
|
router.goNamed(
|
||||||
Routes.CHANNEL_PAGE,
|
Routes.CHANNEL_PAGE.name,
|
||||||
ModalRoute.withName(Routes.CHANNEL_LIST_PAGE),
|
params: Routes.CHANNEL_PAGE.params(channel),
|
||||||
arguments: ChannelPageArgs(channel: channel),
|
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
_showErrorAlert();
|
_showErrorAlert();
|
||||||
@@ -192,67 +191,73 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
AnimatedBuilder(
|
||||||
child: GestureDetector(
|
animation: widget.groupChatState,
|
||||||
behavior: HitTestBehavior.opaque,
|
builder: (context, child) {
|
||||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
return Expanded(
|
||||||
child: ListView.separated(
|
child: GestureDetector(
|
||||||
itemCount: _selectedUsers.length + 1,
|
behavior: HitTestBehavior.opaque,
|
||||||
separatorBuilder: (_, __) => Container(
|
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||||
height: 1,
|
child: ListView.separated(
|
||||||
color: StreamChatTheme.of(context).colorTheme.borders,
|
itemCount: widget.groupChatState.users.length + 1,
|
||||||
),
|
separatorBuilder: (_, __) => Container(
|
||||||
itemBuilder: (_, index) {
|
height: 1,
|
||||||
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,
|
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
.textHighEmphasis,
|
.borders,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(0),
|
itemBuilder: (_, index) {
|
||||||
splashRadius: 24,
|
if (index ==
|
||||||
onPressed: () {
|
widget.groupChatState.users.length) {
|
||||||
setState(() {
|
return Container(
|
||||||
_selectedUsers.remove(user);
|
height: 1,
|
||||||
});
|
color: StreamChatTheme.of(context)
|
||||||
if (_selectedUsers.isEmpty) {
|
.colorTheme
|
||||||
Navigator.pop(context, _selectedUsers);
|
.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,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
TextButton(
|
||||||
|
onPressed: GoRouter.of(context).pop,
|
||||||
child: Text(
|
child: Text(
|
||||||
AppLocalizations.of(context).ok,
|
AppLocalizations.of(context).ok,
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
@@ -318,9 +324,6 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
|||||||
.colorTheme
|
.colorTheme
|
||||||
.accentPrimary),
|
.accentPrimary),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:collection/collection.dart' show IterableExtension;
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:example/pages/channel_file_display_screen.dart';
|
import 'package:example/pages/channel_file_display_screen.dart';
|
||||||
|
import 'package:example/routes/routes.dart';
|
||||||
import 'package:example/utils/localizations.dart';
|
import 'package:example/utils/localizations.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'channel_media_display_screen.dart';
|
import 'channel_media_display_screen.dart';
|
||||||
import 'channel_page.dart';
|
|
||||||
import 'chat_info_screen.dart';
|
|
||||||
import 'pinned_messages_screen.dart';
|
import 'pinned_messages_screen.dart';
|
||||||
|
|
||||||
class GroupInfoScreen extends StatefulWidget {
|
class GroupInfoScreen extends StatefulWidget {
|
||||||
@@ -647,7 +647,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
final streamChannel = StreamChannel.of(context);
|
final streamChannel = StreamChannel.of(context);
|
||||||
final streamChat = StreamChat.of(context);
|
final streamChat = StreamChat.of(context);
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
final res = await showConfirmationBottomSheet(
|
final res = await showConfirmationBottomSheet(
|
||||||
context,
|
context,
|
||||||
title: AppLocalizations.of(context).leaveConversation,
|
title: AppLocalizations.of(context).leaveConversation,
|
||||||
@@ -662,7 +662,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
if (res == true) {
|
if (res == true) {
|
||||||
final channel = streamChannel.channel;
|
final channel = streamChannel.channel;
|
||||||
await channel.removeMembers([streamChat.currentUser!.id]);
|
await channel.removeMembers([streamChat.currentUser!.id]);
|
||||||
navigator.pop();
|
router.pop();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -865,7 +865,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
AppLocalizations.of(context).viewInfo,
|
AppLocalizations.of(context).viewInfo,
|
||||||
() async {
|
() async {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
|
|
||||||
final c = client.channel('messaging', extraData: {
|
final c = client.channel('messaging', extraData: {
|
||||||
'members': [
|
'members': [
|
||||||
@@ -876,16 +876,10 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
|
|
||||||
await c.watch();
|
await c.watch();
|
||||||
|
|
||||||
await navigator.push(
|
router.pushNamed(
|
||||||
MaterialPageRoute(
|
Routes.CHAT_INFO_SCREEN.name,
|
||||||
builder: (context) => StreamChannel(
|
params: Routes.CHAT_INFO_SCREEN.params(c),
|
||||||
channel: c,
|
extra: user,
|
||||||
child: ChatInfoScreen(
|
|
||||||
messageTheme: widget.messageTheme,
|
|
||||||
user: user,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -901,7 +895,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
AppLocalizations.of(context).message,
|
AppLocalizations.of(context).message,
|
||||||
() async {
|
() async {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
|
|
||||||
final c = client.channel('messaging', extraData: {
|
final c = client.channel('messaging', extraData: {
|
||||||
'members': [
|
'members': [
|
||||||
@@ -912,13 +906,9 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
|
|
||||||
await c.watch();
|
await c.watch();
|
||||||
|
|
||||||
await navigator.push(
|
router.pushNamed(
|
||||||
MaterialPageRoute(
|
Routes.CHANNEL_PAGE.name,
|
||||||
builder: (context) => StreamChannel(
|
params: Routes.CHANNEL_PAGE.params(c),
|
||||||
channel: c,
|
|
||||||
child: const ChannelPage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -934,7 +924,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
size: 24.0,
|
size: 24.0,
|
||||||
),
|
),
|
||||||
AppLocalizations.of(context).removeFromGroup, () async {
|
AppLocalizations.of(context).removeFromGroup, () async {
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
final res = await showConfirmationBottomSheet(
|
final res = await showConfirmationBottomSheet(
|
||||||
context,
|
context,
|
||||||
title: AppLocalizations.of(context).removeMember,
|
title: AppLocalizations.of(context).removeMember,
|
||||||
@@ -949,21 +939,23 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
if (res == true) {
|
if (res == true) {
|
||||||
await channel.removeMembers([user.id]);
|
await channel.removeMembers([user.id]);
|
||||||
}
|
}
|
||||||
navigator.pop();
|
router.pop();
|
||||||
},
|
},
|
||||||
color:
|
color:
|
||||||
StreamChatTheme.of(context).colorTheme.accentError),
|
StreamChatTheme.of(context).colorTheme.accentError),
|
||||||
_buildModalListTile(
|
_buildModalListTile(
|
||||||
context,
|
context,
|
||||||
StreamSvgIcon.closeSmall(
|
StreamSvgIcon.closeSmall(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
.textLowEmphasis,
|
.textLowEmphasis,
|
||||||
size: 24.0,
|
size: 24.0,
|
||||||
),
|
),
|
||||||
AppLocalizations.of(context).cancel, () {
|
AppLocalizations.of(context).cancel,
|
||||||
Navigator.pop(context);
|
() {
|
||||||
}),
|
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:example/utils/localizations.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'channel_page.dart';
|
|
||||||
import '../widgets/chips_input_text_field.dart';
|
import '../widgets/chips_input_text_field.dart';
|
||||||
import '../routes/routes.dart';
|
import '../routes/routes.dart';
|
||||||
|
|
||||||
@@ -61,13 +61,14 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
|||||||
_userNameQuery = _controller.text;
|
_userNameQuery = _controller.text;
|
||||||
_isSearchActive = _userNameQuery.isNotEmpty;
|
_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)
|
if (!_isSearchActive && !_selectedUsers.isNotEmpty)
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
context,
|
Routes.NEW_GROUP_CHAT.name,
|
||||||
Routes.NEW_GROUP_CHAT,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -311,8 +311,6 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
|||||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||||
child: StreamUserListView(
|
child: StreamUserListView(
|
||||||
controller: userListController,
|
controller: userListController,
|
||||||
// groupAlphabetically:
|
|
||||||
// _isSearchActive ? false : true,
|
|
||||||
onUserTap: (user) {
|
onUserTap: (user) {
|
||||||
_controller.clear();
|
_controller.clear();
|
||||||
if (!_selectedUsers.contains(user)) {
|
if (!_selectedUsers.contains(user)) {
|
||||||
@@ -409,11 +407,9 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
|||||||
return message;
|
return message;
|
||||||
},
|
},
|
||||||
onMessageSent: (m) {
|
onMessageSent: (m) {
|
||||||
Navigator.pushNamedAndRemoveUntil(
|
GoRouter.of(context).goNamed(
|
||||||
context,
|
Routes.CHANNEL_PAGE.name,
|
||||||
Routes.CHANNEL_PAGE,
|
params: Routes.CHANNEL_PAGE.params(channel!),
|
||||||
ModalRoute.withName(Routes.CHANNEL_LIST_PAGE),
|
|
||||||
arguments: ChannelPageArgs(channel: channel),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:example/state/new_group_chat_state.dart';
|
||||||
import 'package:example/utils/localizations.dart';
|
import 'package:example/utils/localizations.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import '../routes/routes.dart';
|
import '../routes/routes.dart';
|
||||||
@@ -20,7 +22,7 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
|||||||
|
|
||||||
String _userNameQuery = '';
|
String _userNameQuery = '';
|
||||||
|
|
||||||
final _selectedUsers = <User>{};
|
final groupChatState = NewGroupChatState();
|
||||||
|
|
||||||
bool _isSearchActive = false;
|
bool _isSearchActive = false;
|
||||||
|
|
||||||
@@ -69,250 +71,240 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return AnimatedBuilder(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
animation: groupChatState,
|
||||||
appBar: AppBar(
|
builder: (context, child) {
|
||||||
elevation: 1,
|
final state = groupChatState;
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
|
return Scaffold(
|
||||||
leading: const StreamBackButton(),
|
backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
|
||||||
title: Text(
|
appBar: AppBar(
|
||||||
AppLocalizations.of(context).addGroupMembers,
|
elevation: 1,
|
||||||
style: TextStyle(
|
backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
|
||||||
color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
|
leading: const StreamBackButton(),
|
||||||
fontSize: 16,
|
title: Text(
|
||||||
),
|
AppLocalizations.of(context).addGroupMembers,
|
||||||
),
|
style: TextStyle(
|
||||||
centerTitle: true,
|
color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
|
||||||
actions: [
|
fontSize: 16,
|
||||||
if (_selectedUsers.isNotEmpty)
|
|
||||||
IconButton(
|
|
||||||
icon: StreamSvgIcon.arrowRight(
|
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentPrimary,
|
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
),
|
||||||
final updatedList = await Navigator.pushNamed(
|
centerTitle: true,
|
||||||
context,
|
actions: [
|
||||||
Routes.NEW_GROUP_CHAT_DETAILS,
|
if (state.users.isNotEmpty)
|
||||||
arguments: _selectedUsers.toList(growable: false),
|
IconButton(
|
||||||
);
|
icon: StreamSvgIcon.arrowRight(
|
||||||
if (updatedList != null) {
|
color: StreamChatTheme.of(context).colorTheme.accentPrimary,
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (_selectedUsers.isNotEmpty)
|
onPressed: () async {
|
||||||
SliverToBoxAdapter(
|
GoRouter.of(context).pushNamed(
|
||||||
child: SizedBox(
|
Routes.NEW_GROUP_CHAT_DETAILS.name,
|
||||||
height: 104,
|
extra: state,
|
||||||
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]),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onUserTap: (user) {
|
)
|
||||||
if (!_selectedUsers.contains(user)) {
|
],
|
||||||
setState(() {
|
),
|
||||||
_selectedUsers.add(user);
|
body: StreamConnectionStatusBuilder(
|
||||||
});
|
statusBuilder: (context, status) {
|
||||||
} else {
|
String statusString = '';
|
||||||
setState(() {
|
bool showStatus = true;
|
||||||
_selectedUsers.remove(user);
|
|
||||||
});
|
switch (status) {
|
||||||
}
|
case ConnectionStatus.connected:
|
||||||
},
|
statusString = AppLocalizations.of(context).connected;
|
||||||
emptyBuilder: (_) {
|
showStatus = false;
|
||||||
return LayoutBuilder(
|
break;
|
||||||
builder: (context, viewportConstraints) {
|
case ConnectionStatus.connecting:
|
||||||
return SingleChildScrollView(
|
statusString = AppLocalizations.of(context).reconnecting;
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
break;
|
||||||
child: ConstrainedBox(
|
case ConnectionStatus.disconnected:
|
||||||
constraints: BoxConstraints(
|
statusString = AppLocalizations.of(context).disconnected;
|
||||||
minHeight: viewportConstraints.maxHeight,
|
break;
|
||||||
),
|
}
|
||||||
child: Center(
|
return StreamInfoTile(
|
||||||
child: Column(
|
showMessage: showStatus,
|
||||||
children: [
|
tileAnchor: Alignment.topCenter,
|
||||||
Padding(
|
childAnchor: Alignment.topCenter,
|
||||||
padding: const EdgeInsets.all(24),
|
message: statusString,
|
||||||
child: StreamSvgIcon.search(
|
child: NestedScrollView(
|
||||||
size: 96,
|
floatHeaderSlivers: true,
|
||||||
color: StreamChatTheme.of(context)
|
headerSliverBuilder:
|
||||||
.colorTheme
|
(BuildContext context, bool innerBoxIsScrolled) {
|
||||||
.textLowEmphasis,
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context)
|
user.name.split(' ')[0],
|
||||||
.noUserMatchesTheseKeywords,
|
style: const TextStyle(
|
||||||
style: StreamChatTheme.of(context)
|
fontWeight: FontWeight.bold,
|
||||||
.textTheme
|
fontSize: 12,
|
||||||
.footnote
|
),
|
||||||
.copyWith(
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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)
|
color: StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
.textLowEmphasis,
|
.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/utils/localizations.dart';
|
||||||
import 'package:example/routes/routes.dart';
|
import 'package:example/routes/routes.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'channel_page.dart';
|
|
||||||
|
|
||||||
class PinnedMessagesScreen extends StatefulWidget {
|
class PinnedMessagesScreen extends StatefulWidget {
|
||||||
const PinnedMessagesScreen({super.key});
|
const PinnedMessagesScreen({super.key});
|
||||||
|
|
||||||
@@ -103,7 +102,7 @@ class _PinnedMessagesScreenState extends State<PinnedMessagesScreen> {
|
|||||||
},
|
},
|
||||||
onMessageTap: (messageResponse) async {
|
onMessageTap: (messageResponse) async {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
final message = messageResponse.message;
|
final message = messageResponse.message;
|
||||||
final channel = client.channel(
|
final channel = client.channel(
|
||||||
messageResponse.channel!.type,
|
messageResponse.channel!.type,
|
||||||
@@ -112,12 +111,10 @@ class _PinnedMessagesScreenState extends State<PinnedMessagesScreen> {
|
|||||||
if (channel.state == null) {
|
if (channel.state == null) {
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
}
|
}
|
||||||
navigator.pushNamed(
|
router.pushNamed(
|
||||||
Routes.CHANNEL_PAGE,
|
Routes.CHANNEL_PAGE.name,
|
||||||
arguments: ChannelPageArgs(
|
params: Routes.CHANNEL_PAGE.params(channel),
|
||||||
channel: channel,
|
queryParams: Routes.CHANNEL_PAGE.queryParams(message),
|
||||||
initialMessage: message,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import 'package:example/utils/localizations.dart';
|
import 'package:example/utils/localizations.dart';
|
||||||
import 'package:example/routes/routes.dart';
|
import 'package:example/routes/routes.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
import 'channel_page.dart';
|
|
||||||
|
|
||||||
class UserMentionsPage extends StatefulWidget {
|
class UserMentionsPage extends StatefulWidget {
|
||||||
const UserMentionsPage({super.key});
|
const UserMentionsPage({super.key});
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ class _UserMentionsPageState extends State<UserMentionsPage> {
|
|||||||
},
|
},
|
||||||
onMessageTap: (messageResponse) async {
|
onMessageTap: (messageResponse) async {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
final message = messageResponse.message;
|
final message = messageResponse.message;
|
||||||
final channel = client.channel(
|
final channel = client.channel(
|
||||||
messageResponse.channel!.type,
|
messageResponse.channel!.type,
|
||||||
@@ -81,12 +80,10 @@ class _UserMentionsPageState extends State<UserMentionsPage> {
|
|||||||
if (channel.state == null) {
|
if (channel.state == null) {
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
}
|
}
|
||||||
navigator.pushNamed(
|
router.pushNamed(
|
||||||
Routes.CHANNEL_PAGE,
|
Routes.CHANNEL_PAGE.name,
|
||||||
arguments: ChannelPageArgs(
|
params: Routes.CHANNEL_PAGE.params(channel),
|
||||||
channel: channel,
|
queryParams: Routes.CHANNEL_PAGE.queryParams(message),
|
||||||
initialMessage: message,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,135 +1,127 @@
|
|||||||
import 'package:collection/collection.dart';
|
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_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:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.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/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 {
|
final appRoutes = [
|
||||||
/// Add entry for new route here
|
GoRoute(
|
||||||
static Route<dynamic>? generateRoute(RouteSettings settings) {
|
name: Routes.CHANNEL_LIST_PAGE.name,
|
||||||
final args = settings.arguments;
|
path: Routes.CHANNEL_LIST_PAGE.path,
|
||||||
switch (settings.name) {
|
builder: (BuildContext context, GoRouterState state) =>
|
||||||
case Routes.APP:
|
const ChannelListPage(),
|
||||||
return MaterialPageRoute(
|
routes: [
|
||||||
settings: RouteSettings(arguments: args, name: Routes.APP),
|
GoRoute(
|
||||||
builder: (_) {
|
name: Routes.CHANNEL_PAGE.name,
|
||||||
return const StreamChatSampleApp();
|
path: Routes.CHANNEL_PAGE.path,
|
||||||
});
|
builder: (context, state) {
|
||||||
case Routes.HOME:
|
final channel =
|
||||||
return MaterialPageRoute(
|
StreamChat.of(context).client.state.channels[state.params['cid']];
|
||||||
settings: RouteSettings(arguments: args, name: Routes.HOME),
|
final messageId = state.queryParams['mid'];
|
||||||
builder: (_) {
|
final parentId = state.queryParams['pid'];
|
||||||
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;
|
|
||||||
|
|
||||||
return StreamChannel(
|
Message? parentMessage;
|
||||||
channel: channelPageArgs.channel!,
|
if (parentId != null) {
|
||||||
initialMessageId: initialMessage?.id,
|
parentMessage = channel?.state!.messages
|
||||||
child: Builder(
|
.firstWhereOrNull((it) => it.id == parentId);
|
||||||
builder: (context) {
|
}
|
||||||
final parentId = initialMessage?.parentId;
|
|
||||||
Message? parentMessage;
|
return StreamChannel(
|
||||||
if (parentId != null) {
|
channel: channel!,
|
||||||
final channel = StreamChannel.of(context).channel;
|
initialMessageId: messageId,
|
||||||
parentMessage = channel.state!.messages
|
child: Builder(
|
||||||
.firstWhereOrNull((it) => it.id == parentId);
|
builder: (context) {
|
||||||
}
|
return (parentMessage != null)
|
||||||
if (parentMessage != null) {
|
? ThreadPage(parent: parentMessage)
|
||||||
return ThreadPage(parent: parentMessage);
|
: ChannelPage(
|
||||||
}
|
highlightInitialMessage: messageId != null,
|
||||||
return ChannelPage(
|
);
|
||||||
highlightInitialMessage:
|
},
|
||||||
channelPageArgs.initialMessage != null,
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
routes: [
|
||||||
);
|
GoRoute(
|
||||||
},
|
name: Routes.CHAT_INFO_SCREEN.name,
|
||||||
);
|
path: Routes.CHAT_INFO_SCREEN.path,
|
||||||
case Routes.NEW_CHAT:
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
return MaterialPageRoute(
|
final channel = StreamChat.of(context)
|
||||||
settings: RouteSettings(arguments: args, name: Routes.NEW_CHAT),
|
.client
|
||||||
builder: (_) {
|
.state
|
||||||
return const NewChatScreen();
|
.channels[state.params['cid']];
|
||||||
});
|
return StreamChannel(
|
||||||
case Routes.NEW_GROUP_CHAT:
|
channel: channel!,
|
||||||
return MaterialPageRoute(
|
child: ChatInfoScreen(
|
||||||
settings:
|
user: state.extra as User?,
|
||||||
RouteSettings(arguments: args, name: Routes.NEW_GROUP_CHAT),
|
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
||||||
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>?,
|
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
case Routes.CHAT_INFO_SCREEN:
|
),
|
||||||
return MaterialPageRoute(
|
GoRoute(
|
||||||
settings:
|
name: Routes.GROUP_INFO_SCREEN.name,
|
||||||
RouteSettings(arguments: args, name: Routes.CHAT_INFO_SCREEN),
|
path: Routes.GROUP_INFO_SCREEN.path,
|
||||||
builder: (context) {
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
return ChatInfoScreen(
|
final channel = StreamChat.of(context)
|
||||||
user: args as User?,
|
.client
|
||||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
.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(
|
GoRoute(
|
||||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
name: Routes.NEW_CHAT.name,
|
||||||
);
|
path: Routes.NEW_CHAT.path,
|
||||||
});
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
case Routes.CHANNEL_LIST_PAGE:
|
return const NewChatScreen();
|
||||||
return MaterialPageRoute(
|
},
|
||||||
settings:
|
),
|
||||||
RouteSettings(arguments: args, name: Routes.CHANNEL_LIST_PAGE),
|
GoRoute(
|
||||||
builder: (context) {
|
name: Routes.NEW_GROUP_CHAT.name,
|
||||||
return const ChannelListPage();
|
path: Routes.NEW_GROUP_CHAT.path,
|
||||||
});
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
// Default case, should not reach here.
|
return const NewGroupChatScreen();
|
||||||
default:
|
},
|
||||||
return null;
|
),
|
||||||
}
|
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
|
// ignore_for_file: constant_identifier_names
|
||||||
|
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// Application routes
|
/// Application routes
|
||||||
abstract class Routes {
|
abstract class Routes {
|
||||||
static const String APP = '/app';
|
static const RouteConfig CHOOSE_USER =
|
||||||
static const String HOME = '/home';
|
RouteConfig(name: 'choose_user', path: '/users');
|
||||||
static const String CHOOSE_USER = '/choose_user';
|
static const RouteConfig ADVANCED_OPTIONS =
|
||||||
static const String ADVANCED_OPTIONS = '/advance_options';
|
RouteConfig(name: 'advanced_options', path: '/options');
|
||||||
static const String CHANNEL_PAGE = '/channel_page';
|
static const ChannelRouteConfig CHANNEL_PAGE =
|
||||||
static const String NEW_CHAT = '/new_chat';
|
ChannelRouteConfig(name: 'channel_page', path: 'channel/:cid');
|
||||||
static const String NEW_GROUP_CHAT = '/new_group_chat';
|
static const RouteConfig NEW_CHAT =
|
||||||
static const String NEW_GROUP_CHAT_DETAILS = '/new_group_chat_details';
|
RouteConfig(name: 'new_chat', path: '/new_chat');
|
||||||
static const String CHAT_INFO_SCREEN = '/chat_info_screen';
|
static const RouteConfig NEW_GROUP_CHAT =
|
||||||
static const String GROUP_INFO_SCREEN = '/group_info_screen';
|
RouteConfig(name: 'new_group_chat', path: '/new_group_chat');
|
||||||
static const String CHANNEL_LIST_PAGE = '/channel_list_page';
|
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/utils/localizations.dart';
|
||||||
import 'package:example/routes/routes.dart';
|
import 'package:example/routes/routes.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart'
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart'
|
||||||
hide Message;
|
hide Message;
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
void showLocalNotification(
|
void showLocalNotification(
|
||||||
@@ -35,7 +35,7 @@ void showLocalNotification(
|
|||||||
onSelectNotification: (channelCid) async {
|
onSelectNotification: (channelCid) async {
|
||||||
if (channelCid != null) {
|
if (channelCid != null) {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
|
|
||||||
var channel = client.state.channels[channelCid];
|
var channel = client.state.channels[channelCid];
|
||||||
|
|
||||||
@@ -50,11 +50,9 @@ void showLocalNotification(
|
|||||||
await channel.watch();
|
await channel.watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.pushNamed(
|
router.pushNamed(
|
||||||
Routes.CHANNEL_PAGE,
|
Routes.CHANNEL_PAGE.name,
|
||||||
arguments: ChannelPageArgs(
|
params: Routes.CHANNEL_PAGE.params(channel),
|
||||||
channel: channel,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import 'package:example/routes/routes.dart';
|
|||||||
import 'package:example/widgets/search_text_field.dart';
|
import 'package:example/widgets/search_text_field.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
|
||||||
import '../pages/channel_page.dart';
|
|
||||||
import '../pages/chat_info_screen.dart';
|
import '../pages/chat_info_screen.dart';
|
||||||
import '../pages/group_info_screen.dart';
|
import '../pages/group_info_screen.dart';
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ class _ChannelList extends State<ChannelList> {
|
|||||||
final messageResponse = messageResponses[index];
|
final messageResponse = messageResponses[index];
|
||||||
FocusScope.of(context).requestFocus(FocusNode());
|
FocusScope.of(context).requestFocus(FocusNode());
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
final navigator = Navigator.of(context);
|
final router = GoRouter.of(context);
|
||||||
final message = messageResponse.message;
|
final message = messageResponse.message;
|
||||||
final channel = client.channel(
|
final channel = client.channel(
|
||||||
messageResponse.channel!.type,
|
messageResponse.channel!.type,
|
||||||
@@ -159,12 +159,10 @@ class _ChannelList extends State<ChannelList> {
|
|||||||
if (channel.state == null) {
|
if (channel.state == null) {
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
}
|
}
|
||||||
navigator.pushNamed(
|
router.pushNamed(
|
||||||
Routes.CHANNEL_PAGE,
|
Routes.CHANNEL_PAGE.name,
|
||||||
arguments: ChannelPageArgs(
|
params: Routes.CHANNEL_PAGE.params(channel),
|
||||||
channel: channel,
|
queryParams: Routes.CHANNEL_PAGE.queryParams(message),
|
||||||
initialMessage: message,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -265,12 +263,9 @@ class _ChannelList extends State<ChannelList> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
onChannelTap: (channel) {
|
onChannelTap: (channel) {
|
||||||
Navigator.pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
context,
|
Routes.CHANNEL_PAGE.name,
|
||||||
Routes.CHANNEL_PAGE,
|
params: Routes.CHANNEL_PAGE.params(channel),
|
||||||
arguments: ChannelPageArgs(
|
|
||||||
channel: channel,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
emptyBuilder: (_) {
|
emptyBuilder: (_) {
|
||||||
@@ -286,10 +281,8 @@ class _ChannelList extends State<ChannelList> {
|
|||||||
),
|
),
|
||||||
emptyTitle: TextButton(
|
emptyTitle: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pushNamed(
|
GoRouter.of(context)
|
||||||
context,
|
.pushNamed(Routes.NEW_CHAT.name);
|
||||||
Routes.NEW_CHAT,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Start a chat',
|
'Start a chat',
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@@ -142,6 +143,7 @@
|
|||||||
33FAB671232836740065AC1E /* Runner */ = {
|
33FAB671232836740065AC1E /* Runner */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CDF4BDDD28F9A6920059DB87 /* RunnerDebug.entitlements */,
|
||||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
|
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
|
||||||
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
|
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
|
||||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
|
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
|
||||||
@@ -159,7 +161,6 @@
|
|||||||
929AC42CE2CDA27FE9235234 /* Pods-Runner.release.xcconfig */,
|
929AC42CE2CDA27FE9235234 /* Pods-Runner.release.xcconfig */,
|
||||||
D4F4A1B883B395E86A7B745A /* Pods-Runner.profile.xcconfig */,
|
D4F4A1B883B395E86A7B745A /* Pods-Runner.profile.xcconfig */,
|
||||||
);
|
);
|
||||||
name = Pods;
|
|
||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -209,7 +210,6 @@
|
|||||||
33CC10EC2044A3C60003C045 = {
|
33CC10EC2044A3C60003C045 = {
|
||||||
CreatedOnToolsVersion = 9.2;
|
CreatedOnToolsVersion = 9.2;
|
||||||
LastSwiftMigration = 1100;
|
LastSwiftMigration = 1100;
|
||||||
ProvisioningStyle = Automatic;
|
|
||||||
SystemCapabilities = {
|
SystemCapabilities = {
|
||||||
com.apple.Sandbox = {
|
com.apple.Sandbox = {
|
||||||
enabled = 1;
|
enabled = 1;
|
||||||
@@ -419,6 +419,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -544,9 +545,11 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
DEVELOPMENT_TEAM = "";
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -565,8 +568,10 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
DEVELOPMENT_TEAM = "";
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.cs.allow-jit</key>
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.server</key>
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.files.user-selected.read-write</key>
|
<key>com.apple.security.network.server</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.files.user-selected.read-write</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</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
|
path: packages/stream_chat_localizations
|
||||||
flutter_local_notifications: ^9.0.0
|
flutter_local_notifications: ^9.0.0
|
||||||
flutter_svg: ^1.0.3
|
flutter_svg: ^1.0.3
|
||||||
flutter_secure_storage: ^5.0.2
|
flutter_secure_storage: ^6.0.0
|
||||||
yaml: ^3.1.0
|
yaml: ^3.1.0
|
||||||
uuid: ^3.0.5
|
uuid: ^3.0.5
|
||||||
streaming_shared_preferences: ^2.0.0
|
streaming_shared_preferences: ^2.0.0
|
||||||
@@ -35,6 +35,8 @@ dependencies:
|
|||||||
collection: ^1.15.0
|
collection: ^1.15.0
|
||||||
sentry_flutter: ^6.5.0
|
sentry_flutter: ^6.5.0
|
||||||
flutter_slidable: ^2.0.0
|
flutter_slidable: ^2.0.0
|
||||||
|
go_router: ^5.0.5
|
||||||
|
provider: ^6.0.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_launcher_icons: ^0.9.2
|
flutter_launcher_icons: ^0.9.2
|
||||||
|
|||||||
Reference in New Issue
Block a user