1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
| | import 'package:flutter/material.dart';
| |
| | import '../providers/providers.dart';
| | import '../theme/app_theme.dart';
| | import 'voice_button.dart';
| |
| | /// Bottom input area: voice mode (replay | talk | Aa) or text mode (mic | text | send).
| | class InputBar extends StatelessWidget {
| | final InputMode mode;
| | final bool isRecording;
| | final TextEditingController textController;
| | final VoidCallback onToggleMode;
| | final VoidCallback onRecordStart;
| | final VoidCallback onRecordStop;
| | final VoidCallback onRecordCancel;
| | final VoidCallback onReplay;
| | final VoidCallback onSendText;
| |
| | const InputBar({
| | super.key,
| | required this.mode,
| | required this.isRecording,
| | required this.textController,
| | required this.onToggleMode,
| | required this.onRecordStart,
| | required this.onRecordStop,
| | required this.onRecordCancel,
| | required this.onReplay,
| | required this.onSendText,
| | });
| |
| | @override
| | Widget build(BuildContext context) {
| | final isDark = Theme.of(context).brightness == Brightness.dark;
| |
| | return Container(
| | padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
| | decoration: BoxDecoration(
| | color: isDark ? AppColors.darkSurface : AppColors.lightSurface,
| | border: Border(
| | top: BorderSide(
| | color: isDark ? Colors.white10 : Colors.black12,
| | width: 0.5,
| | ),
| | ),
| | ),
| | child: SafeArea(
| | top: false,
| | child: mode == InputMode.voice
| | ? _buildVoiceMode(context)
| | : _buildTextMode(context),
| | ),
| | );
| | }
| |
| | Widget _buildVoiceMode(BuildContext context) {
| | return Row(
| | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
| | children: [
| | // Replay button
| | IconButton(
| | icon: const Icon(Icons.replay, size: 28),
| | onPressed: onReplay,
| | tooltip: 'Replay last',
| | ),
| | // Talk button
| | VoiceButton(
| | isRecording: isRecording,
| | onTapStart: onRecordStart,
| | onTapStop: onRecordStop,
| | onCancel: onRecordCancel,
| | ),
| | // Toggle to text mode
| | IconButton(
| | icon: const Text(
| | 'Aa',
| | style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
| | ),
| | onPressed: onToggleMode,
| | tooltip: 'Text mode',
| | ),
| | ],
| | );
| | }
| |
| | Widget _buildTextMode(BuildContext context) {
| | final isDark = Theme.of(context).brightness == Brightness.dark;
| |
| | return Row(
| | children: [
| | // Toggle to voice mode
| | IconButton(
| | icon: const Icon(Icons.mic, size: 24),
| | onPressed: onToggleMode,
| | tooltip: 'Voice mode',
| | ),
| | const SizedBox(width: 8),
| | // Text field
| | Expanded(
| | child: TextField(
| | controller: textController,
| | decoration: InputDecoration(
| | hintText: 'Type a message...',
| | filled: true,
| | fillColor: isDark ? AppColors.darkInputBg : AppColors.lightInputBg,
| | border: OutlineInputBorder(
| | borderRadius: BorderRadius.circular(24),
| | borderSide: BorderSide.none,
| | ),
| | contentPadding:
| | const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
| | isDense: true,
| | ),
| | textInputAction: TextInputAction.newline,
| | maxLines: 4,
| | minLines: 1,
| | ),
| | ),
| | const SizedBox(width: 8),
| | // Send button
| | IconButton(
| | icon: const Icon(Icons.send, color: AppColors.accent),
| | onPressed: onSendText,
| | tooltip: 'Send',
| | ),
| | ],
| | );
| | }
| | }
|
|