♻️ 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/me/account_settings.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_detail.dart';
import 'package:island/screens/chat/call.dart';
@@ -265,11 +264,6 @@ final routerProvider = Provider<GoRouter>((ref) {
path: '/chat',
builder: (context, state) => const ChatListScreen(),
),
GoRoute(
name: 'chatNew',
path: '/chat/new',
builder: (context, state) => const NewChatScreen(),
),
GoRoute(
name: 'chatRoom',
path: '/chat/:id',
@@ -278,14 +272,6 @@ final routerProvider = Provider<GoRouter>((ref) {
return ChatRoomScreen(id: id);
},
),
GoRoute(
name: 'chatEdit',
path: '/chat/:id/edit',
builder: (context, state) {
final id = state.pathParameters['id']!;
return EditChatScreen(id: id);
},
),
GoRoute(
name: 'chatDetail',
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_uploader.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:island/widgets/content/sheet.dart';
class NewChatScreen extends StatelessWidget {
const NewChatScreen({super.key});
@@ -151,12 +151,10 @@ class EditChatScreen extends HookConsumerWidget {
}
}
return AppScaffold(
appBar: AppBar(
title: Text(id == null ? 'createChatRoom' : 'editChatRoom').tr(),
leading: const PageBackButton(),
),
body: SingleChildScrollView(
return SheetScaffold(
titleText: (id == null ? 'createChatRoom' : 'editChatRoom').tr(),
onClose: () => context.pop(),
child: SingleChildScrollView(
child: Column(
children: [
AspectRatio(
@@ -204,16 +202,24 @@ class EditChatScreen extends HookConsumerWidget {
children: [
TextFormField(
controller: nameController,
decoration: const InputDecoration(labelText: 'Name'),
decoration: InputDecoration(
labelText: 'Name',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
TextFormField(
controller: descriptionController,
decoration: const InputDecoration(
decoration: InputDecoration(
labelText: 'Description',
alignLabelWithHint: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
minLines: 3,
maxLines: null,
@@ -223,7 +229,12 @@ class EditChatScreen extends HookConsumerWidget {
const SizedBox(height: 16),
DropdownButtonFormField<SnRealm>(
value: currentRealm.value,
decoration: InputDecoration(labelText: 'realm'.tr()),
decoration: InputDecoration(
labelText: 'realm'.tr(),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
items: [
DropdownMenuItem<SnRealm>(
value: null,

View File

@@ -142,6 +142,25 @@ class ChatRoomScreen extends HookConsumerWidget {
final messageController = useTextEditingController();
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
final bottomGradientNotifier = useState(ValueNotifier<double>(0.0));
@@ -600,7 +619,7 @@ class ChatRoomScreen extends HookConsumerWidget {
top: 16,
bottom:
MediaQuery.of(context).padding.bottom +
80, // Leave space for chat input
inputHeight.value, // Leave space for chat input
),
controller: scrollController,
reverse: true, // Show newest messages at the bottom
@@ -964,6 +983,7 @@ class ChatRoomScreen extends HookConsumerWidget {
child: chatRoom.when(
data:
(room) => Column(
key: inputKey,
mainAxisSize: MainAxisSize.min,
children: [
ChatInput(

View File

@@ -16,6 +16,7 @@ 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/sheet.dart';
import 'package:island/screens/chat/chat_form.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
@@ -447,10 +448,17 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
if ((chatIdentity.value?.role ?? 0) >= 50)
PopupMenuItem(
onTap: () {
context.pushReplacementNamed(
'chatEdit',
pathParameters: {'id': id},
);
showModalBottomSheet(
context: context,
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(
children: [