refactor: improve chat input and message list layout, enhance keyboard visibility handling

This commit is contained in:
cogwheel0
2025-08-25 16:31:08 +05:30
parent 0a09372c4a
commit 265c7026af
2 changed files with 342 additions and 333 deletions

View File

@@ -9,7 +9,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_animate/flutter_animate.dart';
import 'dart:io' show Platform, File; import 'dart:io' show Platform, File;
import 'dart:async'; import 'dart:async';
import 'dart:ui' show ImageFilter;
import '../../../core/providers/app_providers.dart'; import '../../../core/providers/app_providers.dart';
import '../providers/chat_providers.dart'; import '../providers/chat_providers.dart';
import '../../../core/utils/debug_logger.dart'; import '../../../core/utils/debug_logger.dart';
@@ -941,6 +940,9 @@ class _ChatPageState extends ConsumerState<ChatPage> {
// Watch reviewer mode and auto-select model if needed // Watch reviewer mode and auto-select model if needed
final isReviewerMode = ref.watch(reviewerModeProvider); final isReviewerMode = ref.watch(reviewerModeProvider);
// Keyboard visibility
final keyboardVisible = MediaQuery.of(context).viewInsets.bottom > 0;
// Auto-select model when in reviewer mode with no selection // Auto-select model when in reviewer mode with no selection
if (isReviewerMode && selectedModel == null) { if (isReviewerMode && selectedModel == null) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -1340,10 +1342,12 @@ class _ChatPageState extends ConsumerState<ChatPage> {
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: () => onTap: () =>
FocusManager.instance.primaryFocus?.unfocus(), FocusManager.instance.primaryFocus?.unfocus(),
child: RepaintBoundary(
child: _buildMessagesList(theme), child: _buildMessagesList(theme),
), ),
), ),
), ),
),
// File attachments // File attachments
const FileAttachmentWidget(), const FileAttachmentWidget(),
@@ -1352,7 +1356,8 @@ class _ChatPageState extends ConsumerState<ChatPage> {
const ChatOfflineOverlay(), const ChatOfflineOverlay(),
// Modern Input (root matches input background including safe area) // Modern Input (root matches input background including safe area)
ModernChatInput( RepaintBoundary(
child: ModernChatInput(
enabled: enabled:
selectedModel != null && selectedModel != null &&
(isOnline || ref.watch(reviewerModeProvider)), (isOnline || ref.watch(reviewerModeProvider)),
@@ -1364,6 +1369,7 @@ class _ChatPageState extends ConsumerState<ChatPage> {
onCameraCapture: () => onCameraCapture: () =>
_handleImageAttachment(fromCamera: true), _handleImageAttachment(fromCamera: true),
), ),
),
], ],
), ),
@@ -1390,14 +1396,13 @@ class _ChatPageState extends ConsumerState<ChatPage> {
}, },
child: child:
(_showScrollToBottom && (_showScrollToBottom &&
!keyboardVisible &&
ref.watch(chatMessagesProvider).isNotEmpty) ref.watch(chatMessagesProvider).isNotEmpty)
? ClipRRect( ? ClipRRect(
key: const ValueKey('scroll_to_bottom_visible'), key: const ValueKey('scroll_to_bottom_visible'),
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
AppBorderRadius.floatingButton, AppBorderRadius.floatingButton,
), ),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: context color: context
@@ -1431,7 +1436,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
), ),
), ),
), ),
),
) )
: const SizedBox.shrink( : const SizedBox.shrink(
key: ValueKey('scroll_to_bottom_hidden'), key: ValueKey('scroll_to_bottom_hidden'),

View File

@@ -231,7 +231,6 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
children: [ children: [
// Main input area with unified 2-row design // Main input area with unified 2-row design
Container( Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration( decoration: BoxDecoration(
color: context.conduitTheme.inputBackground, color: context.conduitTheme.inputBackground,
borderRadius: const BorderRadius.vertical( borderRadius: const BorderRadius.vertical(
@@ -269,6 +268,7 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: SingleChildScrollView( child: SingleChildScrollView(
physics: const ClampingScrollPhysics(), physics: const ClampingScrollPhysics(),
child: RepaintBoundary(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -324,10 +324,12 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
TextCapitalization.sentences, TextCapitalization.sentences,
textInputAction: TextInputAction.newline, textInputAction: TextInputAction.newline,
showCursor: true, showCursor: true,
cursorColor: context.conduitTheme.inputText, cursorColor:
context.conduitTheme.inputText,
style: AppTypography.chatMessageStyle style: AppTypography.chatMessageStyle
.copyWith( .copyWith(
color: context.conduitTheme.inputText, color:
context.conduitTheme.inputText,
), ),
decoration: InputDecoration( decoration: InputDecoration(
hintText: AppLocalizations.of( hintText: AppLocalizations.of(
@@ -497,7 +499,8 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
const double buttonSize = const double buttonSize =
TouchTarget.comfortable; TouchTarget.comfortable;
final double t = _isRecording final double t = _isRecording
? (_intensity.clamp(0, 10) / 10.0) ? (_intensity.clamp(0, 10) /
10.0)
: 0.0; : 0.0;
final double ringMaxExtra = 16.0; final double ringMaxExtra = 16.0;
final double ringSize = final double ringSize =
@@ -575,7 +578,8 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
icon: Icons.bug_report, icon: Icons.bug_report,
onTap: widget.enabled onTap: widget.enabled
? () async { ? () async {
final result = await _voiceService final result =
await _voiceService
.testOnDeviceStt(); .testOnDeviceStt();
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of( ScaffoldMessenger.of(
@@ -609,6 +613,7 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
), ),
), ),
), ),
),
], ],
), ),
); );