Skip to content

Commit

Permalink
compose_box: Implement content input redesign
Browse files Browse the repository at this point in the history
`ClipRect`'s size is determined by the `ConstrainedBox`.  This is
mainly for showing the content through the `contentPadding` of the
`TextField`, so that our `InsetShadowBox` can fade it smoothly there.
The shadow is always there, but it is only visible when the
`TextField` is long enough to be scrollable.

See also:

  - #928 (comment),
    which elaborates on the issue we intend to solve with the
    `ClipRect` setup.
  - https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3954-13395
  - https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3862-14350

Fixes: #915

Signed-off-by: Zixuan James Li <[email protected]>
  • Loading branch information
PIG208 committed Nov 1, 2024
1 parent 99cfed8 commit 68aa675
Showing 1 changed file with 56 additions and 20 deletions.
76 changes: 56 additions & 20 deletions lib/widgets/compose_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'autocomplete.dart';
import 'color.dart';
import 'dialog.dart';
import 'icons.dart';
import 'inset_shadow.dart';
import 'store.dart';
import 'text.dart';
import 'theme.dart';
Expand Down Expand Up @@ -287,30 +288,65 @@ class _ContentInput extends StatelessWidget {

@override
Widget build(BuildContext context) {
ColorScheme colorScheme = Theme.of(context).colorScheme;

return InputDecorator(
decoration: const InputDecoration(),
child: ConstrainedBox(
constraints: const BoxConstraints(
// TODO constrain this adaptively (i.e. not hard-coded 200)
maxHeight: 200,
),
child: ComposeAutocomplete(
narrow: narrow,
controller: controller,
focusNode: focusNode,
fieldViewBuilder: (context) {
return TextField(
const verticalPadding = 8.0;
const contentLineHeight = 22.0;
// Prevent the content input from getting too tall,
// otherwise it might overflow at the bottom,
// pushing compose buttons and the send button out of screen.
final textScaler = MediaQuery.textScalerOf(context).clamp(maxScaleFactor: 1.5);

final designVariables = DesignVariables.of(context);

return ComposeAutocomplete(
narrow: narrow,
controller: controller,
focusNode: focusNode,
fieldViewBuilder: (context) => ConstrainedBox(
constraints: BoxConstraints(
// Reserve space to fully show the first 7th lines and just partially
// clip the 8th line, where the height matches the spec at
// https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3960-5147&node-type=text&m=dev
// > Maximum size of the compose box is suggested to be 178px. Which
// > has 7 fully visible lines of text
//
// The partial line hints that the content input is scrollable.
maxHeight: verticalPadding + textScaler.scale(
contentLineHeight * 7 + contentLineHeight * 0.727)),
child: ClipRect(
child: InsetShadowBox(
top: verticalPadding, bottom: verticalPadding,
color: designVariables.composeBoxBg,
child: TextField(
controller: controller,
focusNode: focusNode,
style: TextStyle(color: colorScheme.onSurface),
decoration: InputDecoration.collapsed(hintText: hintText),
// Let the content show through the `contentPadding` so that
// our [InsetShadowBox] can fade it smoothly there.
clipBehavior: Clip.none,
style: TextStyle(
fontSize: 17,
height: (contentLineHeight / 17),
color: designVariables.textInput),
// From the spec at
// https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3960-5147&node-type=text&m=dev
// > Compose box has the height to fit 2 lines. This is [done] to
// > have a bigger hit area for the user to start the input. […]
minLines: 2,
maxLines: null,
textCapitalization: TextCapitalization.sentences,
);
}),
));
decoration: InputDecoration(
// This padding ensures that the user can always scroll long
// content entirely out of the top or bottom shadow if desired.
// With this and the `minLines: 2` above, an empty content input
// gets 60px vertical distance between the top of the top shadow
// and the bottom of the bottom shadow (with no text-size
// scaling). That's a bit more than the 54px given in the Figma,
// and we can revisit if needed, but it's tricky to get that
// 54px distance while also making the scrolling work like this
// and offering two lines of touchable area.
contentPadding: const EdgeInsets.symmetric(vertical: verticalPadding),
hintText: hintText,
hintStyle: TextStyle(
color: designVariables.textInput.withValues(alpha: 0.5))))))));
}
}

Expand Down

0 comments on commit 68aa675

Please sign in to comment.