♻️ Use bottom modal sheet for chat creation form

This commit is contained in:
2025-11-16 11:46:31 +08:00
parent 40c0e052cf
commit a1a7b34c86
4 changed files with 54 additions and 29 deletions

View File

@@ -30,7 +30,6 @@ import 'package:island/screens/account/me/profile_update.dart';
import 'package:island/screens/account/leveling.dart'; import 'package:island/screens/account/leveling.dart';
import 'package:island/screens/account/me/account_settings.dart'; import 'package:island/screens/account/me/account_settings.dart';
import 'package:island/screens/chat/chat.dart'; import 'package:island/screens/chat/chat.dart';
import 'package:island/screens/chat/chat_form.dart';
import 'package:island/screens/chat/room.dart'; import 'package:island/screens/chat/room.dart';
import 'package:island/screens/chat/room_detail.dart'; import 'package:island/screens/chat/room_detail.dart';
import 'package:island/screens/chat/call.dart'; import 'package:island/screens/chat/call.dart';
@@ -265,11 +264,6 @@ final routerProvider = Provider<GoRouter>((ref) {
path: '/chat', path: '/chat',
builder: (context, state) => const ChatListScreen(), builder: (context, state) => const ChatListScreen(),
), ),
GoRoute(
name: 'chatNew',
path: '/chat/new',
builder: (context, state) => const NewChatScreen(),
),
GoRoute( GoRoute(
name: 'chatRoom', name: 'chatRoom',
path: '/chat/:id', path: '/chat/:id',
@@ -278,14 +272,6 @@ final routerProvider = Provider<GoRouter>((ref) {
return ChatRoomScreen(id: id); return ChatRoomScreen(id: id);
}, },
), ),
GoRoute(
name: 'chatEdit',
path: '/chat/:id/edit',
builder: (context, state) {
final id = state.pathParameters['id']!;
return EditChatScreen(id: id);
},
),
GoRoute( GoRoute(
name: 'chatDetail', name: 'chatDetail',
path: '/chat/:id/detail', path: '/chat/:id/detail',

View File

@@ -16,10 +16,10 @@ import 'package:island/screens/realm/realms.dart';
import 'package:island/services/file.dart'; import 'package:island/services/file.dart';
import 'package:island/services/file_uploader.dart'; import 'package:island/services/file_uploader.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:island/widgets/content/sheet.dart';
class NewChatScreen extends StatelessWidget { class NewChatScreen extends StatelessWidget {
const NewChatScreen({super.key}); const NewChatScreen({super.key});
@@ -151,12 +151,10 @@ class EditChatScreen extends HookConsumerWidget {
} }
} }
return AppScaffold( return SheetScaffold(
appBar: AppBar( titleText: (id == null ? 'createChatRoom' : 'editChatRoom').tr(),
title: Text(id == null ? 'createChatRoom' : 'editChatRoom').tr(), onClose: () => context.pop(),
leading: const PageBackButton(), child: SingleChildScrollView(
),
body: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
AspectRatio( AspectRatio(
@@ -204,16 +202,24 @@ class EditChatScreen extends HookConsumerWidget {
children: [ children: [
TextFormField( TextFormField(
controller: nameController, controller: nameController,
decoration: const InputDecoration(labelText: 'Name'), decoration: InputDecoration(
labelText: 'Name',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
onTapOutside: onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(), (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
TextFormField( TextFormField(
controller: descriptionController, controller: descriptionController,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Description', labelText: 'Description',
alignLabelWithHint: true, alignLabelWithHint: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
), ),
minLines: 3, minLines: 3,
maxLines: null, maxLines: null,
@@ -223,7 +229,12 @@ class EditChatScreen extends HookConsumerWidget {
const SizedBox(height: 16), const SizedBox(height: 16),
DropdownButtonFormField<SnRealm>( DropdownButtonFormField<SnRealm>(
value: currentRealm.value, value: currentRealm.value,
decoration: InputDecoration(labelText: 'realm'.tr()), decoration: InputDecoration(
labelText: 'realm'.tr(),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
items: [ items: [
DropdownMenuItem<SnRealm>( DropdownMenuItem<SnRealm>(
value: null, value: null,

View File

@@ -142,6 +142,25 @@ class ChatRoomScreen extends HookConsumerWidget {
final messageController = useTextEditingController(); final messageController = useTextEditingController();
final scrollController = useScrollController(); final scrollController = useScrollController();
// Input height measurement for dynamic padding
final inputKey = useMemoized(() => GlobalKey());
final inputHeight = useState<double>(80.0);
// Periodic height measurement for dynamic sizing
useEffect(() {
final timer = Timer.periodic(const Duration(milliseconds: 50), (_) {
final renderBox =
inputKey.currentContext?.findRenderObject() as RenderBox?;
if (renderBox != null) {
final newHeight = renderBox.size.height;
if (newHeight != inputHeight.value) {
inputHeight.value = newHeight;
}
}
});
return timer.cancel;
}, []);
// Scroll animation notifiers // Scroll animation notifiers
final bottomGradientNotifier = useState(ValueNotifier<double>(0.0)); final bottomGradientNotifier = useState(ValueNotifier<double>(0.0));
@@ -600,7 +619,7 @@ class ChatRoomScreen extends HookConsumerWidget {
top: 16, top: 16,
bottom: bottom:
MediaQuery.of(context).padding.bottom + MediaQuery.of(context).padding.bottom +
80, // Leave space for chat input inputHeight.value, // Leave space for chat input
), ),
controller: scrollController, controller: scrollController,
reverse: true, // Show newest messages at the bottom reverse: true, // Show newest messages at the bottom
@@ -964,6 +983,7 @@ class ChatRoomScreen extends HookConsumerWidget {
child: chatRoom.when( child: chatRoom.when(
data: data:
(room) => Column( (room) => Column(
key: inputKey,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
ChatInput( ChatInput(

View File

@@ -16,6 +16,7 @@ import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/widgets/content/sheet.dart';
import 'package:island/screens/chat/chat_form.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
@@ -447,10 +448,17 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
if ((chatIdentity.value?.role ?? 0) >= 50) if ((chatIdentity.value?.role ?? 0) >= 50)
PopupMenuItem( PopupMenuItem(
onTap: () { onTap: () {
context.pushReplacementNamed( showModalBottomSheet(
'chatEdit', context: context,
pathParameters: {'id': id}, useRootNavigator: true,
); isScrollControlled: true,
builder: (context) => EditChatScreen(id: id),
).then((value) {
if (value != null) {
// Invalidate to refresh room data after edit
ref.invalidate(chatroomProvider(id));
}
});
}, },
child: Row( child: Row(
children: [ children: [