Skip to content

Commit

Permalink
Fixed popup overflow by suggestions
Browse files Browse the repository at this point in the history
Wrapped the popup content in a SingleChildScrollView;
Constrained the height of the popup;

Changed the `MistakeBuilderCallback` typedef to match the signature of the `LanguageToolMistakePopup` unnamed constructor for simpler interoperability.
  • Loading branch information
mitryp committed May 25, 2023
1 parent d922d5a commit 53d1df3
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 51 deletions.
11 changes: 6 additions & 5 deletions lib/domain/typedefs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import 'package:languagetool_textfield/domain/mistake.dart';
import 'package:languagetool_textfield/utils/popup_overlay_renderer.dart';

/// Callback used to build popup body
typedef MistakeBuilderCallback = Widget Function(
PopupOverlayRenderer popupRenderer,
Mistake mistake,
ColoredTextEditingController controller,
);
typedef MistakeBuilderCallback = Widget Function({
required PopupOverlayRenderer popupRenderer,
required Mistake mistake,
required ColoredTextEditingController controller,
required Offset mistakePosition,
});

/// Function called after mistake was clicked
typedef ShowPopupCallback = void Function(
Expand Down
132 changes: 86 additions & 46 deletions lib/utils/mistake_popup.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:languagetool_textfield/domain/typedefs.dart';
import 'package:languagetool_textfield/languagetool_textfield.dart';
Expand All @@ -21,16 +23,18 @@ class MistakePopup {
Offset popupPosition,
ColoredTextEditingController controller,
) {
final MistakeBuilderCallback builder =
mistakeBuilder ?? LanguageToolMistakePopup.new;

popupRenderer.render(
context,
position: popupPosition,
popupBuilder: (context) =>
mistakeBuilder?.call(popupRenderer, mistake, controller) ??
LanguageToolMistakePopup(
popupRenderer: popupRenderer,
mistake: mistake,
controller: controller,
),
popupBuilder: (context) => builder.call(
popupRenderer: popupRenderer,
mistake: mistake,
controller: controller,
mistakePosition: popupPosition,
),
);
}
}
Expand All @@ -43,6 +47,8 @@ class LanguageToolMistakePopup extends StatelessWidget {
required this.popupRenderer,
required this.mistake,
required this.controller,
required this.mistakePosition,
this.maxHeight = double.infinity,
});

/// Renderer used to display this window.
Expand All @@ -54,60 +60,94 @@ class LanguageToolMistakePopup extends StatelessWidget {
/// Controller of the text where mistake was found
final ColoredTextEditingController controller;

/// An on-screen position of the mistake
final Offset mistakePosition;

/// A maximum height of the popup.
/// If infinity, the popup will use all the available height between the
/// [mistakePosition] and the furthest border of the layout constraints.
final double maxHeight;

@override
Widget build(BuildContext context) {
const _borderRadius = 10.0;
const _mistakeNameFontSize = 13.0;
const _mistakeMessageFontSize = 15.0;
const _replacementButtonsSpacing = 10.0;

const padding = 10.0;
const paddingCount = 4;
const paddingSum = padding * paddingCount;

final availableSpace = _calculateAvailableSpace(
context,
paddings: paddingSum,
);

return Container(
padding: const EdgeInsets.all(10),
padding: const EdgeInsets.all(padding),
decoration: BoxDecoration(
boxShadow: const [BoxShadow(color: Colors.grey, blurRadius: 20)],
color: Colors.white,
borderRadius: BorderRadius.circular(_borderRadius),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// mistake type
Text(
mistake.type.name,
style: TextStyle(
color: Colors.grey.shade700,
fontSize: _mistakeNameFontSize,
fontWeight: FontWeight.w500,
constraints: BoxConstraints(maxHeight: availableSpace),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// mistake type
Text(
mistake.type.name,
style: TextStyle(
color: Colors.grey.shade700,
fontSize: _mistakeNameFontSize,
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 10),

// mistake message
Text(
mistake.message,
style: const TextStyle(fontSize: _mistakeMessageFontSize),
),
const SizedBox(height: 10),

// replacements
Wrap(
spacing: _replacementButtonsSpacing,
direction: Axis.horizontal,
children: mistake.replacements
.map(
(replacement) => ElevatedButton(
onPressed: () {
controller.replaceMistake(mistake, replacement);
popupRenderer.dismiss();
},
child: Text(replacement),
),
)
.toList(growable: false),
),
],
const SizedBox(height: padding),

// mistake message
Text(
mistake.message,
style: const TextStyle(fontSize: _mistakeMessageFontSize),
),
const SizedBox(height: padding),

// replacements
Wrap(
spacing: _replacementButtonsSpacing,
runSpacing: _replacementButtonsSpacing,
direction: Axis.horizontal,
children: mistake.replacements
.map(
(replacement) => ElevatedButton(
onPressed: () {
controller.replaceMistake(mistake, replacement);
popupRenderer.dismiss();
},
child: Text(replacement),
),
)
.toList(growable: false),
),
],
),
),
);
}

double _calculateAvailableSpace(
BuildContext context, {
required double paddings,
}) {
final mediaQuery = MediaQuery.of(context);

final availableSpaceBottom =
mediaQuery.size.height - mistakePosition.dy - paddings;
final availableSpaceTop = mistakePosition.dy - paddings;

return min(max(availableSpaceBottom, availableSpaceTop), maxHeight);
}
}

0 comments on commit 53d1df3

Please sign in to comment.