Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed popup overflow by suggestions #35

Merged
merged 12 commits into from
May 29, 2023
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
142 changes: 92 additions & 50 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,30 +23,24 @@ 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,
),
);
}
}

/// Default mistake window that looks similar to LanguageTool popup
class LanguageToolMistakePopup extends StatelessWidget {
/// [LanguageToolMistakePopup] constructor
const LanguageToolMistakePopup({
super.key,
required this.popupRenderer,
required this.mistake,
required this.controller,
});

/// Renderer used to display this window.
final PopupOverlayRenderer popupRenderer;

Expand All @@ -54,60 +50,106 @@ 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;

/// [LanguageToolMistakePopup] constructor
const LanguageToolMistakePopup({
super.key,
required this.popupRenderer,
required this.mistake,
required this.controller,
required this.mistakePosition,
this.maxHeight = double.infinity,
});

@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;

final availableSpace = _calculateAvailableSpace(
context,
paddings: defaultPopupVerticalPadding + defaultPopupHorizontalPadding,
mitryp marked this conversation as resolved.
Show resolved Hide resolved
);

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: CustomScrollView(
shrinkWrap: true,
slivers: [
SliverToBoxAdapter(
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: padding),

// mistake message
Text(
mistake.message,
style: const TextStyle(fontSize: _mistakeMessageFontSize),
),
const SizedBox(height: padding),
],
),
),
const SizedBox(height: 10),
SliverList.builder(
itemCount: mistake.replacements.length,
itemBuilder: (context, index) {
final replacement = mistake.replacements[index];

// 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),
return Padding(
padding: const EdgeInsets.all(_replacementButtonsSpacing / 2),
child: ElevatedButton(
onPressed: () {
controller.replaceMistake(mistake, replacement);
popupRenderer.dismiss();
},
child: Text(replacement),
),
);
},
),
],
),
);
}

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);
}
mitryp marked this conversation as resolved.
Show resolved Hide resolved
}
8 changes: 4 additions & 4 deletions lib/utils/popup_overlay_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import 'package:flutter/material.dart';
const defaultPopupWidth = 250.0;

/// defaultHorizontalPadding
const defaultHorizontalPadding = 10.0;
const defaultPopupHorizontalPadding = 10.0;

/// defaultVerticalMargin
const defaultVerticalPadding = 30.0;
const defaultPopupVerticalPadding = 30.0;

/// Renderer used to show popup window overlay
class PopupOverlayRenderer {
Expand Down Expand Up @@ -79,8 +79,8 @@ class PopupOverlayLayoutDelegate extends SingleChildLayoutDelegate {
const PopupOverlayLayoutDelegate(
this.position, {
this.width = defaultPopupWidth,
this.horizontalPadding = defaultHorizontalPadding,
this.verticalPadding = defaultVerticalPadding,
this.horizontalPadding = defaultPopupHorizontalPadding,
this.verticalPadding = defaultPopupVerticalPadding,
});

@override
Expand Down