refactor: improve chat input and message list layout, enhance keyboard visibility handling
This commit is contained in:
@@ -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'),
|
||||||
|
|||||||
@@ -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>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user