@@ -17,9 +17,9 @@ 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
23
const double _sendButtonSize = 36 ;
24
24
25
25
/// A [TextEditingController] for use in the compose box.
@@ -285,32 +285,40 @@ class _ContentInput extends StatelessWidget {
285
285
286
286
@override
287
287
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,
301
- controller: controller,
302
- focusNode: focusNode,
303
- fieldViewBuilder: (context) {
304
- return TextField (
288
+ final designVariables = DesignVariables .of (context);
289
+ const topPadding = 8.0 ;
290
+ const contentLineHeight = 22.0 ;
291
+
292
+ return ConstrainedBox (
293
+ constraints: const BoxConstraints (
294
+ // Reserve space to fully show the first 7th lines and just partially
295
+ // clip the 8th line, where the height matches the spec of 170 logical
296
+ // pixels. This hints that the content input is scrollable.
297
+ maxHeight: topPadding + contentLineHeight * 7 + contentLineHeight * 0.727 ),
298
+ child: ComposeAutocomplete (
299
+ narrow: narrow,
300
+ controller: controller,
301
+ focusNode: focusNode,
302
+ fieldViewBuilder: (context) => SingleChildScrollView (
303
+ // While the [TextField] is scrollable, we need to wrap it with
304
+ // [SingleChildScrollView] to insert a fixed-height padding that can
305
+ // be scrolled along with the text.
306
+ child: Padding (
307
+ padding: const EdgeInsets .only (top: topPadding),
308
+ child: TextField (
305
309
controller: controller,
306
310
focusNode: focusNode,
307
- style: TextStyle (color: colorScheme.onSurface),
308
- decoration: InputDecoration .collapsed (hintText: hintText),
311
+ decoration: InputDecoration .collapsed (
312
+ border: InputBorder .none,
313
+ hintText: hintText,
314
+ hintStyle: TextStyle (color: designVariables.textInput.withOpacity (0.5 ))),
315
+ minLines: 2 ,
309
316
maxLines: null ,
310
317
textCapitalization: TextCapitalization .sentences,
311
- );
312
- }),
313
- ));
318
+ style: TextStyle (
319
+ fontSize: 17 ,
320
+ height: (contentLineHeight / 17 ),
321
+ color: designVariables.textInput))))));
314
322
}
315
323
}
316
324
@@ -392,7 +400,11 @@ class _TopicInput extends StatelessWidget {
392
400
@override
393
401
Widget build (BuildContext context) {
394
402
final zulipLocalizations = ZulipLocalizations .of (context);
395
- ColorScheme colorScheme = Theme .of (context).colorScheme;
403
+ final designVariables = DesignVariables .of (context);
404
+ TextStyle topicTextStyle = TextStyle (
405
+ fontSize: 22 ,
406
+ color: designVariables.textInput,
407
+ ).merge (weightVariableTextStyle (context, wght: 600 ));
396
408
397
409
return TopicAutocomplete (
398
410
streamId: streamId,
@@ -403,9 +415,13 @@ class _TopicInput extends StatelessWidget {
403
415
controller: controller,
404
416
focusNode: focusNode,
405
417
textInputAction: TextInputAction .next,
406
- style: TextStyle (color: colorScheme.onSurface),
407
- decoration: InputDecoration (hintText: zulipLocalizations.composeBoxTopicHintText),
408
- ));
418
+ style: topicTextStyle,
419
+ decoration: InputDecoration (
420
+ isDense: true ,
421
+ border: const UnderlineInputBorder (),
422
+ contentPadding: const EdgeInsets .fromLTRB (0 , 10 , 0 , 10 ),
423
+ hintStyle: topicTextStyle.copyWith (color: designVariables.textInput.withOpacity (0.5 )),
424
+ hintText: zulipLocalizations.composeBoxTopicHintText)));
409
425
}
410
426
}
411
427
@@ -842,38 +858,27 @@ class _SendButtonState extends State<_SendButton> {
842
858
@override
843
859
Widget build (BuildContext context) {
844
860
final disabled = _hasValidationErrors;
845
- final colorScheme = Theme .of (context).colorScheme;
846
861
final zulipLocalizations = ZulipLocalizations .of (context);
862
+ final designVariables = DesignVariables .of (context);
847
863
848
- // Copy FilledButton defaults (_FilledButtonDefaultsM3.backgroundColor)
849
- final backgroundColor = disabled
850
- ? colorScheme.onSurface.withOpacity (0.12 )
851
- : colorScheme.primary;
852
-
853
- // Copy FilledButton defaults (_FilledButtonDefaultsM3.foregroundColor)
854
- final foregroundColor = disabled
855
- ? colorScheme.onSurface.withOpacity (0.38 )
856
- : colorScheme.onPrimary;
864
+ final buttonColor = disabled
865
+ ? designVariables.icon.withOpacity (0.5 )
866
+ : designVariables.icon;
857
867
858
- return Ink (
859
- decoration: BoxDecoration (
860
- borderRadius: const BorderRadius .all (Radius .circular (8.0 )),
861
- color: backgroundColor,
868
+ return IconButton (
869
+ tooltip: zulipLocalizations.composeBoxSendTooltip,
870
+ style: const ButtonStyle (
871
+ // Match the height of the content input.
872
+ minimumSize: WidgetStatePropertyAll (Size .square (_sendButtonSize)),
873
+ // With the default of [MaterialTapTargetSize.padded], not just the
874
+ // tap target but the visual button would get padded to 48px square.
875
+ // It would be nice if the tap target extended invisibly out from the
876
+ // button, to make a 48px square, but that's not the behavior we get.
877
+ tapTargetSize: MaterialTapTargetSize .shrinkWrap,
862
878
),
863
- child: IconButton (
864
- 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,
875
- icon: const Icon (ZulipIcons .send),
876
- onPressed: _send));
879
+ color: buttonColor,
880
+ icon: const Icon (ZulipIcons .send),
881
+ onPressed: _send);
877
882
}
878
883
}
879
884
@@ -884,18 +889,21 @@ class _ComposeBoxContainer extends StatelessWidget {
884
889
885
890
@override
886
891
Widget build (BuildContext context) {
887
- ColorScheme colorScheme = Theme .of (context).colorScheme ;
892
+ final designVariables = DesignVariables .of (context);
888
893
889
894
// TODO(design): Maybe put a max width on the compose box, like we do on
890
895
// the message list itself
891
- return SizedBox (width: double .infinity,
896
+ return Container (width: double .infinity,
897
+ decoration: BoxDecoration (
898
+ border: Border (top: BorderSide (color: designVariables.borderBar))),
892
899
child: Material (
893
- color: colorScheme.surfaceContainerHighest ,
900
+ color: designVariables.bgComposeBox ,
894
901
child: SafeArea (
895
- minimum: const EdgeInsets .fromLTRB (8 , 0 , 8 , 8 ),
896
902
child: Padding (
897
- padding: const EdgeInsets .only (top: 8.0 ),
898
- child: child))));
903
+ padding: const EdgeInsets .symmetric (horizontal: 16.0 ),
904
+ child: child,
905
+ )),
906
+ ));
899
907
}
900
908
}
901
909
@@ -916,45 +924,26 @@ class _ComposeBoxLayout extends StatelessWidget {
916
924
917
925
@override
918
926
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
- );
935
-
936
927
return _ComposeBoxContainer (
937
928
child: Column (children: [
938
929
Row (crossAxisAlignment: CrossAxisAlignment .end, children: [
939
930
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,
931
+ child: Column (children: [
932
+ if (topicInput != null ) topicInput! ,
933
+ contentInput,
934
+ ])),
949
935
]),
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
- ])),
936
+ Row (
937
+ mainAxisAlignment: MainAxisAlignment .spaceBetween,
938
+ children: [
939
+ Row (children: [
940
+ _AttachFileButton (contentController: contentController, contentFocusNode: contentFocusNode),
941
+ _AttachMediaButton (contentController: contentController, contentFocusNode: contentFocusNode),
942
+ _AttachFromCameraButton (contentController: contentController, contentFocusNode: contentFocusNode),
943
+ ]),
944
+ sendButton,
945
+ ],
946
+ ),
958
947
]));
959
948
}
960
949
}
0 commit comments