@@ -17,10 +17,11 @@ import 'autocomplete.dart';
17
17
import 'dialog.dart' ;
18
18
import 'icons.dart' ;
19
19
import 'store.dart' ;
20
+ import 'text.dart' ;
20
21
import 'theme.dart' ;
21
22
22
- const double _inputVerticalPadding = 8 ;
23
- const double _sendButtonSize = 36 ;
23
+ const double _composeButtonWidth = 44 ;
24
+ const double _composeButtonHeight = 42 ;
24
25
25
26
/// A [TextEditingController] for use in the compose box.
26
27
///
@@ -285,32 +286,33 @@ class _ContentInput extends StatelessWidget {
285
286
286
287
@override
287
288
Widget build (BuildContext context) {
288
- ColorScheme colorScheme = Theme .of (context).colorScheme;
289
-
290
- return InputDecorator (
291
- decoration: const InputDecoration (),
292
- child: ConstrainedBox (
293
- constraints: const BoxConstraints (
294
- minHeight: _sendButtonSize - 2 * _inputVerticalPadding,
295
-
296
- // TODO constrain this adaptively (i.e. not hard-coded 200)
297
- maxHeight: 200 ,
298
- ),
299
- child: ComposeAutocomplete (
300
- narrow: narrow,
289
+ final designVariables = DesignVariables .of (context);
290
+ const topPadding = 8.0 ;
291
+ const contentLineHeight = 22.0 ;
292
+
293
+ return ConstrainedBox (
294
+ constraints: const BoxConstraints (
295
+ // Reserve space to fully show the first 7th lines and just partially
296
+ // clip the 8th line, where the height matches the spec of 178 logical
297
+ // pixels. The partial line hints that the content input is scrollable.
298
+ maxHeight: topPadding + contentLineHeight * 7 + contentLineHeight * 0.727 ),
299
+ child: ComposeAutocomplete (
300
+ narrow: narrow,
301
+ controller: controller,
302
+ focusNode: focusNode,
303
+ fieldViewBuilder: (context) => TextField (
301
304
controller: controller,
302
305
focusNode: focusNode,
303
- fieldViewBuilder: (context) {
304
- return TextField (
305
- controller: controller,
306
- focusNode: focusNode,
307
- style: TextStyle (color: colorScheme.onSurface),
308
- decoration: InputDecoration .collapsed (hintText: hintText),
309
- maxLines: null ,
310
- textCapitalization: TextCapitalization .sentences,
311
- );
312
- }),
313
- ));
306
+ decoration: InputDecoration .collapsed (
307
+ hintText: hintText,
308
+ hintStyle: TextStyle (color: designVariables.textInput.withValues (alpha: 0.5 ))),
309
+ minLines: 2 ,
310
+ maxLines: null ,
311
+ textCapitalization: TextCapitalization .sentences,
312
+ style: TextStyle (
313
+ fontSize: 17 ,
314
+ height: (contentLineHeight / 17 ),
315
+ color: designVariables.textInput))));
314
316
}
315
317
}
316
318
@@ -391,20 +393,42 @@ class _TopicInput extends StatelessWidget {
391
393
392
394
@override
393
395
Widget build (BuildContext context) {
396
+ const textFieldHeight = 42 ;
397
+ const lineHeight = 22 ;
394
398
final zulipLocalizations = ZulipLocalizations .of (context);
395
- ColorScheme colorScheme = Theme .of (context).colorScheme;
399
+ final designVariables = DesignVariables .of (context);
400
+ TextStyle topicTextStyle = TextStyle (
401
+ fontSize: 22 ,
402
+ height: lineHeight / 22 ,
403
+ color: designVariables.textInput,
404
+ ).merge (weightVariableTextStyle (context, wght: 600 ));
396
405
397
406
return TopicAutocomplete (
398
407
streamId: streamId,
399
408
controller: controller,
400
409
focusNode: focusNode,
401
410
contentFocusNode: contentFocusNode,
402
- fieldViewBuilder: (context) => TextField (
403
- controller: controller,
404
- focusNode: focusNode,
405
- textInputAction: TextInputAction .next,
406
- style: TextStyle (color: colorScheme.onSurface),
407
- decoration: InputDecoration (hintText: zulipLocalizations.composeBoxTopicHintText),
411
+ fieldViewBuilder: (context) => Stack (
412
+ children: [
413
+ TextField (
414
+ controller: controller,
415
+ focusNode: focusNode,
416
+ textInputAction: TextInputAction .next,
417
+ style: topicTextStyle,
418
+ decoration: InputDecoration (
419
+ isDense: true ,
420
+ contentPadding: const EdgeInsets .symmetric (
421
+ vertical: (textFieldHeight - lineHeight) / 2 ),
422
+ border: InputBorder .none,
423
+ hintText: zulipLocalizations.composeBoxTopicHintText,
424
+ hintStyle: topicTextStyle.copyWith (
425
+ color: designVariables.textInput.withValues (alpha: 0.5 )))),
426
+ Positioned (bottom: 0 , left: 0 , right: 0 ,
427
+ child: Container (height: 1 , decoration: BoxDecoration (
428
+ border: Border (
429
+ bottom: BorderSide (width: 1 ,
430
+ color: designVariables.foreground.withValues (alpha: 0.2 )))))),
431
+ ],
408
432
));
409
433
}
410
434
}
@@ -578,10 +602,13 @@ abstract class _AttachUploadsButton extends StatelessWidget {
578
602
@override
579
603
Widget build (BuildContext context) {
580
604
final zulipLocalizations = ZulipLocalizations .of (context);
581
- return IconButton (
582
- icon: Icon (icon),
583
- tooltip: tooltip (zulipLocalizations),
584
- onPressed: () => _handlePress (context));
605
+ return SizedBox (
606
+ width: _composeButtonWidth,
607
+ child: IconButton (
608
+ icon: Icon (icon),
609
+ tooltip: tooltip (zulipLocalizations),
610
+ onPressed: () => _handlePress (context),
611
+ style: const ButtonStyle (splashFactory: NoSplash .splashFactory)));
585
612
}
586
613
}
587
614
@@ -841,39 +868,20 @@ class _SendButtonState extends State<_SendButton> {
841
868
842
869
@override
843
870
Widget build (BuildContext context) {
844
- final disabled = _hasValidationErrors;
845
- final colorScheme = Theme .of (context).colorScheme;
871
+ final designVariables = DesignVariables .of (context);
846
872
final zulipLocalizations = ZulipLocalizations .of (context);
847
873
848
- // Copy FilledButton defaults (_FilledButtonDefaultsM3.backgroundColor)
849
- final backgroundColor = disabled
850
- ? colorScheme.onSurface.withValues (alpha: 0.12 )
851
- : colorScheme.primary;
852
-
853
- // Copy FilledButton defaults (_FilledButtonDefaultsM3.foregroundColor)
854
- final foregroundColor = disabled
855
- ? colorScheme.onSurface.withValues (alpha: 0.38 )
856
- : colorScheme.onPrimary;
857
-
858
- return Ink (
859
- decoration: BoxDecoration (
860
- borderRadius: const BorderRadius .all (Radius .circular (8.0 )),
861
- color: backgroundColor,
862
- ),
874
+ return SizedBox (
875
+ width: _composeButtonWidth,
863
876
child: IconButton (
864
877
tooltip: zulipLocalizations.composeBoxSendTooltip,
865
- style: const ButtonStyle (
866
- // Match the height of the content input.
867
- minimumSize: WidgetStatePropertyAll (Size .square (_sendButtonSize)),
868
- // With the default of [MaterialTapTargetSize.padded], not just the
869
- // tap target but the visual button would get padded to 48px square.
870
- // It would be nice if the tap target extended invisibly out from the
871
- // button, to make a 48px square, but that's not the behavior we get.
872
- tapTargetSize: MaterialTapTargetSize .shrinkWrap,
873
- ),
874
- color: foregroundColor,
878
+ color: _hasValidationErrors
879
+ // TODO(design): need send button color when disabled
880
+ ? designVariables.icon.withValues (alpha: 0.5 )
881
+ : designVariables.icon,
875
882
icon: const Icon (ZulipIcons .send),
876
- onPressed: _send));
883
+ onPressed: _send,
884
+ style: const ButtonStyle (splashFactory: NoSplash .splashFactory)));
877
885
}
878
886
}
879
887
@@ -884,18 +892,16 @@ class _ComposeBoxContainer extends StatelessWidget {
884
892
885
893
@override
886
894
Widget build (BuildContext context) {
887
- ColorScheme colorScheme = Theme .of (context).colorScheme ;
895
+ final designVariables = DesignVariables .of (context);
888
896
889
897
// TODO(design): Maybe put a max width on the compose box, like we do on
890
898
// the message list itself
891
- return SizedBox (width: double .infinity,
899
+ return Container (width: double .infinity,
900
+ decoration: BoxDecoration (
901
+ border: Border (top: BorderSide (color: designVariables.borderBar))),
892
902
child: Material (
893
- color: colorScheme.surfaceContainerHighest,
894
- child: SafeArea (
895
- minimum: const EdgeInsets .fromLTRB (8 , 0 , 8 , 8 ),
896
- child: Padding (
897
- padding: const EdgeInsets .only (top: 8.0 ),
898
- child: child))));
903
+ color: designVariables.bgComposeBox,
904
+ child: SafeArea (child: child)));
899
905
}
900
906
}
901
907
@@ -916,45 +922,32 @@ class _ComposeBoxLayout extends StatelessWidget {
916
922
917
923
@override
918
924
Widget build (BuildContext context) {
919
- ThemeData themeData = Theme .of (context);
920
- ColorScheme colorScheme = themeData.colorScheme;
921
-
922
- final inputThemeData = themeData.copyWith (
923
- inputDecorationTheme: InputDecorationTheme (
924
- // Both [contentPadding] and [isDense] combine to make the layout compact.
925
- isDense: true ,
926
- contentPadding: const EdgeInsets .symmetric (
927
- horizontal: 12.0 , vertical: _inputVerticalPadding),
928
- border: const OutlineInputBorder (
929
- borderRadius: BorderRadius .all (Radius .circular (4.0 )),
930
- borderSide: BorderSide .none),
931
- filled: true ,
932
- fillColor: colorScheme.surface,
933
- ),
934
- );
925
+ final themeData = Theme .of (context);
926
+ final designVariables = DesignVariables .of (context);
935
927
936
928
return _ComposeBoxContainer (
937
929
child: Column (children: [
938
- Row (crossAxisAlignment: CrossAxisAlignment .end, children: [
939
- Expanded (
940
- child: Theme (
941
- data: inputThemeData,
942
- child: Column (children: [
943
- if (topicInput != null ) topicInput! ,
944
- if (topicInput != null ) const SizedBox (height: 8 ),
945
- contentInput,
946
- ]))),
947
- const SizedBox (width: 8 ),
948
- sendButton,
949
- ]),
950
- Theme (
951
- data: themeData.copyWith (
952
- iconTheme: themeData.iconTheme.copyWith (color: colorScheme.onSurface.withOpacity (0.5 ))),
953
- child: Row (children: [
954
- _AttachFileButton (contentController: contentController, contentFocusNode: contentFocusNode),
955
- _AttachMediaButton (contentController: contentController, contentFocusNode: contentFocusNode),
956
- _AttachFromCameraButton (contentController: contentController, contentFocusNode: contentFocusNode),
957
- ])),
930
+ if (topicInput != null )
931
+ Padding (padding: const EdgeInsets .symmetric (horizontal: 16 ),
932
+ child: topicInput! ),
933
+ Padding (padding: const EdgeInsets .symmetric (horizontal: 16 ),
934
+ child: contentInput),
935
+ Container (
936
+ padding: const EdgeInsets .symmetric (horizontal: 8 ),
937
+ height: _composeButtonHeight,
938
+ child: Row (mainAxisAlignment: MainAxisAlignment .spaceBetween,
939
+ children: [
940
+ Theme (
941
+ data: themeData.copyWith (
942
+ iconTheme: themeData.iconTheme.copyWith (
943
+ color: designVariables.foreground.withValues (alpha: 0.5 ))),
944
+ child: Row (children: [
945
+ _AttachFileButton (contentController: contentController, contentFocusNode: contentFocusNode),
946
+ _AttachMediaButton (contentController: contentController, contentFocusNode: contentFocusNode),
947
+ _AttachFromCameraButton (contentController: contentController, contentFocusNode: contentFocusNode),
948
+ ])),
949
+ sendButton,
950
+ ])),
958
951
]));
959
952
}
960
953
}
0 commit comments