From 4488724194fd6c4ac0168844a85013b11fe7acea Mon Sep 17 00:00:00 2001 From: Ahmed Hnewa <73608287+freshtechtips@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:57:32 +0300 Subject: [PATCH] More improvemennts and one little feature --- CHANGELOG.md | 2 + example/lib/pages/home_page.dart | 4 +- example/lib/universal_ui/universal_ui.dart | 8 +- example/lib/widgets/responsive_widget.dart | 8 +- flutter_quill_extensions/CHANGELOG.md | 2 + .../lib/embeds/builders.dart | 331 +++++++++++------- .../lib/embeds/toolbar/media_button.dart | 13 +- .../lib/embeds/widgets/image.dart | 6 +- .../lib/embeds/widgets/image_resizer.dart | 11 +- lib/src/models/documents/attribute.dart | 6 + lib/src/models/rules/rule.dart | 14 +- lib/src/utils/platform.dart | 6 +- lib/src/widgets/editor.dart | 2 +- lib/src/widgets/raw_editor.dart | 2 +- lib/src/widgets/text_block.dart | 2 +- .../widgets/toolbar/link_style_button2.dart | 5 +- 16 files changed, 274 insertions(+), 148 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cd28c90..2299e415 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # [7.4.12] - Update the minimum version of device_info_plus to 9.1.0. +- Custom style attrbuites for platforms other than mobile (alignment, margin, width, height) +- Improve performance by reduce numbers of widget rebuilt by listen to media query for only the needed things - # [7.4.11] - Add sw locale. diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart index 1f764cbc..2367719d 100644 --- a/example/lib/pages/home_page.dart +++ b/example/lib/pages/home_page.dart @@ -104,7 +104,7 @@ class _HomePageState extends State { ), drawer: Container( constraints: - BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.7), + BoxConstraints(maxWidth: MediaQuery.sizeOf(context).width * 0.7), color: Colors.grey.shade800, child: _buildMenuBar(context), ), @@ -408,7 +408,7 @@ class _HomePageState extends State { ); Widget _buildMenuBar(BuildContext context) { - final size = MediaQuery.of(context).size; + final size = MediaQuery.sizeOf(context); const itemStyle = TextStyle( color: Colors.white, fontSize: 18, diff --git a/example/lib/universal_ui/universal_ui.dart b/example/lib/universal_ui/universal_ui.dart index 91344afb..421725e2 100644 --- a/example/lib/universal_ui/universal_ui.dart +++ b/example/lib/universal_ui/universal_ui.dart @@ -45,7 +45,7 @@ class ImageEmbedBuilderWeb extends EmbedBuilder { // TODO: handle imageUrl of base64 return const SizedBox(); } - final size = MediaQuery.of(context).size; + final size = MediaQuery.sizeOf(context); UniversalUI().platformViewRegistry.registerViewFactory(imageUrl, (viewId) { return html.ImageElement() ..src = imageUrl @@ -61,7 +61,7 @@ class ImageEmbedBuilderWeb extends EmbedBuilder { : size.width * 0.2, ), child: SizedBox( - height: MediaQuery.of(context).size.height * 0.45, + height: MediaQuery.sizeOf(context).height * 0.45, child: HtmlElementView( viewType: imageUrl, ), @@ -94,8 +94,8 @@ class VideoEmbedBuilderWeb extends EmbedBuilder { UniversalUI().platformViewRegistry.registerViewFactory( videoUrl, (id) => html.IFrameElement() - ..width = MediaQuery.of(context).size.width.toString() - ..height = MediaQuery.of(context).size.height.toString() + ..width = MediaQuery.sizeOf(context).width.toString() + ..height = MediaQuery.sizeOf(context).height.toString() ..src = videoUrl ..style.border = 'none'); diff --git a/example/lib/widgets/responsive_widget.dart b/example/lib/widgets/responsive_widget.dart index 3829565c..f9de4027 100644 --- a/example/lib/widgets/responsive_widget.dart +++ b/example/lib/widgets/responsive_widget.dart @@ -13,16 +13,16 @@ class ResponsiveWidget extends StatelessWidget { final Widget? smallScreen; static bool isSmallScreen(BuildContext context) { - return MediaQuery.of(context).size.width < 800; + return MediaQuery.sizeOf(context).width < 800; } static bool isLargeScreen(BuildContext context) { - return MediaQuery.of(context).size.width > 1200; + return MediaQuery.sizeOf(context).width > 1200; } static bool isMediumScreen(BuildContext context) { - return MediaQuery.of(context).size.width >= 800 && - MediaQuery.of(context).size.width <= 1200; + return MediaQuery.sizeOf(context).width >= 800 && + MediaQuery.sizeOf(context).width <= 1200; } @override diff --git a/flutter_quill_extensions/CHANGELOG.md b/flutter_quill_extensions/CHANGELOG.md index 74265cef..54ed746e 100644 --- a/flutter_quill_extensions/CHANGELOG.md +++ b/flutter_quill_extensions/CHANGELOG.md @@ -2,6 +2,8 @@ - Provide a way to use custom image provider for the image widgets - Provide a way to handle different errors in image widgets - Two bug fixes related to pick the image and capture it using the camera +- Add support for image resizing in desktop when force using mobile context menu +- Improve performance by reduce numbers of widget rebuilt by listen to media query for only the needed things ## 0.5.1 - Fix warrning "The platformViewRegistry getter is deprecated and will be removed in a future release. Please import it from dart:ui_web instead." diff --git a/flutter_quill_extensions/lib/embeds/builders.dart b/flutter_quill_extensions/lib/embeds/builders.dart index 6e5ba5f9..f82fd1b4 100644 --- a/flutter_quill_extensions/lib/embeds/builders.dart +++ b/flutter_quill_extensions/lib/embeds/builders.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io' show File; import 'package:flutter/cupertino.dart'; @@ -54,40 +55,68 @@ class ImageEmbedBuilder extends EmbedBuilder { final imageUrl = standardizeImageUrl(node.value.data); OptionalSize? imageSize; final style = node.style.attributes['style']; - if (base.isMobile() && style != null) { - final attrs = base.parseKeyValuePairs(style.value.toString(), { - Attribute.mobileWidth, - Attribute.mobileHeight, - Attribute.mobileMargin, - Attribute.mobileAlignment - }); + + // TODO: Please use the one from [Attribute] + const marginKey = 'margin'; + const alignmentKey = 'alignment'; + if (style != null) { + final attrs = base.isMobile() + ? base.parseKeyValuePairs(style.value.toString(), { + Attribute.mobileWidth, + Attribute.mobileHeight, + Attribute.mobileMargin, + Attribute.mobileAlignment, + }) + : base.parseKeyValuePairs(style.value.toString(), { + Attribute.width.key, + Attribute.height.key, + marginKey, + alignmentKey, + }); if (attrs.isNotEmpty) { + final width = double.tryParse( + (base.isMobile() + ? attrs[Attribute.mobileWidth] + : attrs[Attribute.width.key]) ?? + '', + ); + final height = double.tryParse( + (base.isMobile() + ? attrs[Attribute.mobileHeight] + : attrs[Attribute.height.key]) ?? + '', + ); + final alignment = base.getAlignment(base.isMobile() + ? attrs[Attribute.mobileAlignment] + : attrs[alignmentKey]); + final margin = (base.isMobile() + ? double.tryParse(Attribute.mobileMargin) + : double.tryParse(marginKey)) ?? + 0.0; + assert( - attrs[Attribute.mobileWidth] != null && - attrs[Attribute.mobileHeight] != null, - 'mobileWidth and mobileHeight must be specified'); - final w = double.parse(attrs[Attribute.mobileWidth]!); - final h = double.parse(attrs[Attribute.mobileHeight]!); - imageSize = OptionalSize(w, h); - final m = attrs[Attribute.mobileMargin] == null - ? 0.0 - : double.parse(attrs[Attribute.mobileMargin]!); - final a = base.getAlignment(attrs[Attribute.mobileAlignment]); + width != null && height != null, + base.isMobile() + ? 'mobileWidth and mobileHeight must be specified' + : 'width and height must be specified', + ); + imageSize = OptionalSize(width, height); image = Padding( - padding: EdgeInsets.all(m), - child: imageByUrl( - imageUrl, - width: w, - height: h, - alignment: a, - imageProviderBuilder: imageProviderBuilder, - imageErrorWidgetBuilder: imageErrorWidgetBuilder, - )); + padding: EdgeInsets.all(margin), + child: getQuillImageByUrl( + imageUrl, + width: width, + height: height, + alignment: alignment, + imageProviderBuilder: imageProviderBuilder, + imageErrorWidgetBuilder: imageErrorWidgetBuilder, + ), + ); } } if (imageSize == null) { - image = imageByUrl( + image = getQuillImageByUrl( imageUrl, imageProviderBuilder: imageProviderBuilder, imageErrorWidgetBuilder: imageErrorWidgetBuilder, @@ -108,26 +137,89 @@ class ImageEmbedBuilder extends EmbedBuilder { onPressed: () { Navigator.pop(context); showCupertinoModalPopup( - context: context, - builder: (context) { - final screenSize = MediaQuery.of(context).size; - return ImageResizer( - onImageResize: (w, h) { - final res = getEmbedNode( - controller, controller.selection.start); - final attr = base.replaceStyleString( - getImageStyleString(controller), w, h); - controller - ..skipRequestKeyboard = true - ..formatText( - res.offset, 1, StyleAttribute(attr)); - }, - imageWidth: imageSize?.width, - imageHeight: imageSize?.height, - maxWidth: screenSize.width, - maxHeight: screenSize.height, - ); - }); + context: context, + builder: (context) { + // This now will rebuilt only when there are changes + // to the size only and not other properties + // to reduce the builts and improve peformance + final screenSize = MediaQuery.sizeOf(context); + return ImageResizer( + onImageResize: (w, h) { + print('Width = $w, Height = $h'); + final res = getEmbedNode( + controller, + controller.selection.start, + ); + // For desktop + String _replaceStyleStringWithSize( + String s, + double width, + double height, + ) { + final result = {}; + final pairs = s.split(';'); + for (final pair in pairs) { + final _index = pair.indexOf(':'); + if (_index < 0) { + continue; + } + final _key = pair.substring(0, _index).trim(); + result[_key] = + pair.substring(_index + 1).trim(); + } + + result[Attribute.width.key] = width.toString(); + result[Attribute.height.key] = height.toString(); + final sb = StringBuffer(); + for (final pair in result.entries) { + sb + ..write(pair.key) + ..write(': ') + ..write(pair.value) + ..write('; '); + } + return sb.toString(); + } + + // TODO: Please consider add bool property in + // replaceStyleString that will use either + // mobileWidth or width based on that property + // but that require changes to the flutter_quill + // and it should be published and then we can + // change it from flutter_quill_extensions + + final attr = base.isMobile() + ? base.replaceStyleString( + getImageStyleString(controller), + w, + h, + ) + : _replaceStyleStringWithSize( + getImageStyleString(controller), + w, + h, + ); + controller + ..skipRequestKeyboard = true + ..formatText( + res.offset, + 1, + StyleAttribute(attr), + ); + + print( + jsonEncode( + controller.document.toDelta().toJson(), + ), + ); + }, + imageWidth: imageSize?.width, + imageHeight: imageSize?.height, + maxWidth: screenSize.width, + maxHeight: screenSize.height, + ); + }, + ); }, ); final copyOption = _SimpleDialogItem( @@ -182,7 +274,7 @@ class ImageEmbedBuilder extends EmbedBuilder { ), ), children: [ - if (base.isMobile()) resizeOption, + resizeOption, copyOption, removeOption, ]), @@ -332,76 +424,81 @@ Widget _menuOptionsForReadonlyImage({ return GestureDetector( onTap: () { showDialog( - context: context, - builder: (context) { - final saveOption = _SimpleDialogItem( - icon: Icons.save, - color: Colors.greenAccent, - text: 'Save'.i18n, - onPressed: () async { - imageUrl = appendFileExtensionToImageUrl(imageUrl); - final messenger = ScaffoldMessenger.of(context); - Navigator.of(context).pop(); - - final saveImageResult = await saveImage(imageUrl); - final imageSavedSuccessfully = saveImageResult.isSuccess; - - messenger.clearSnackBars(); - - if (!imageSavedSuccessfully) { - messenger.showSnackBar(SnackBar( - content: Text( - 'Error while saving image'.i18n, - ))); - return; - } - - var message; - switch (saveImageResult.method) { - case SaveImageResultMethod.network: - message = 'Saved using the network'.i18n; - break; - case SaveImageResultMethod.localStorage: - message = 'Saved using the local storage'.i18n; - break; - } - - messenger.showSnackBar( - SnackBar( - content: Text(message), - ), - ); - }, - ); - final zoomOption = _SimpleDialogItem( - icon: Icons.zoom_in, - color: Colors.cyanAccent, - text: 'Zoom'.i18n, - onPressed: () { - Navigator.pushReplacement( - context, - // TODO: Consider add support for other theme system - // like Cupertino or at least add the option to by - // by using PageRoute as option so dev can ovveride this - // this change should be done in all places if you want to - MaterialPageRoute( - builder: (context) => ImageTapWrapper( - imageUrl: imageUrl, - imageProviderBuilder: imageProviderBuilder, - imageErrorWidgetBuilder: imageErrorWidgetBuilder, - ), + context: context, + builder: (context) { + final saveOption = _SimpleDialogItem( + icon: Icons.save, + color: Colors.greenAccent, + text: 'Save'.i18n, + onPressed: () async { + imageUrl = appendFileExtensionToImageUrl(imageUrl); + final messenger = ScaffoldMessenger.of(context); + Navigator.of(context).pop(); + + final saveImageResult = await saveImage(imageUrl); + final imageSavedSuccessfully = saveImageResult.isSuccess; + + messenger.clearSnackBars(); + + if (!imageSavedSuccessfully) { + messenger.showSnackBar(SnackBar( + content: Text( + 'Error while saving image'.i18n, + ))); + return; + } + + var message; + switch (saveImageResult.method) { + case SaveImageResultMethod.network: + message = 'Saved using the network'.i18n; + break; + case SaveImageResultMethod.localStorage: + message = 'Saved using the local storage'.i18n; + break; + } + + messenger.showSnackBar( + SnackBar( + content: Text(message), + ), + ); + }, + ); + final zoomOption = _SimpleDialogItem( + icon: Icons.zoom_in, + color: Colors.cyanAccent, + text: 'Zoom'.i18n, + onPressed: () { + Navigator.pushReplacement( + context, + // TODO: Consider add support for other theme system + // like Cupertino or at least add the option to by + // by using PageRoute as option so dev can ovveride this + // this change should be done in all places if you want to + MaterialPageRoute( + builder: (context) => ImageTapWrapper( + imageUrl: imageUrl, + imageProviderBuilder: imageProviderBuilder, + imageErrorWidgetBuilder: imageErrorWidgetBuilder, ), - ); - }, - ); - return Padding( - padding: const EdgeInsets.fromLTRB(50, 0, 50, 0), - child: SimpleDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - children: [saveOption, zoomOption]), - ); - }); + ), + ); + }, + ); + return Padding( + padding: const EdgeInsets.fromLTRB(50, 0, 50, 0), + child: SimpleDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + ), + children: [saveOption, zoomOption], + ), + ); + }, + ); }, child: image); } diff --git a/flutter_quill_extensions/lib/embeds/toolbar/media_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/media_button.dart index 9df32acc..1bb1673f 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/media_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/media_button.dart @@ -224,9 +224,8 @@ class _MediaLinkDialogState extends State { Widget build(BuildContext context) { final constraints = widget.dialogTheme?.linkDialogConstraints ?? () { - final mediaQuery = MediaQuery.of(context); - final maxWidth = - kIsWeb ? mediaQuery.size.width / 4 : mediaQuery.size.width - 80; + final size = MediaQuery.sizeOf(context); + final maxWidth = kIsWeb ? size.width / 4 : size.width - 80; return BoxConstraints(maxWidth: maxWidth, maxHeight: 80); }(); @@ -338,13 +337,13 @@ class MediaSourceSelectorDialog extends StatelessWidget { Widget build(BuildContext context) { final constraints = dialogTheme?.mediaSelectorDialogConstraints ?? () { - final mediaQuery = MediaQuery.of(context); + final size = MediaQuery.sizeOf(context); double maxWidth, maxHeight; if (kIsWeb) { - maxWidth = mediaQuery.size.width / 7; - maxHeight = mediaQuery.size.height / 7; + maxWidth = size.width / 7; + maxHeight = size.height / 7; } else { - maxWidth = mediaQuery.size.width - 80; + maxWidth = size.width - 80; maxHeight = maxWidth / 2; } return BoxConstraints(maxWidth: maxWidth, maxHeight: maxHeight); diff --git a/flutter_quill_extensions/lib/embeds/widgets/image.dart b/flutter_quill_extensions/lib/embeds/widgets/image.dart index bfff7919..fe698a43 100644 --- a/flutter_quill_extensions/lib/embeds/widgets/image.dart +++ b/flutter_quill_extensions/lib/embeds/widgets/image.dart @@ -28,7 +28,7 @@ String getImageStyleString(QuillController controller) { return s ?? ''; } -Image imageByUrl( +Image getQuillImageByUrl( String imageUrl, { required ImageEmbedBuilderProviderBuilder? imageProviderBuilder, required ImageErrorWidgetBuilder? imageErrorWidgetBuilder, @@ -124,7 +124,7 @@ class ImageTapWrapper extends StatelessWidget { return Scaffold( body: Container( constraints: BoxConstraints.expand( - height: MediaQuery.of(context).size.height, + height: MediaQuery.sizeOf(context).height, ), child: Stack( children: [ @@ -145,7 +145,7 @@ class ImageTapWrapper extends StatelessWidget { ), Positioned( right: 10, - top: MediaQuery.of(context).padding.top + 10.0, + top: MediaQuery.paddingOf(context).top + 10.0, child: InkWell( onTap: () { Navigator.pop(context); diff --git a/flutter_quill_extensions/lib/embeds/widgets/image_resizer.dart b/flutter_quill_extensions/lib/embeds/widgets/image_resizer.dart index 19552296..9f84ad17 100644 --- a/flutter_quill_extensions/lib/embeds/widgets/image_resizer.dart +++ b/flutter_quill_extensions/lib/embeds/widgets/image_resizer.dart @@ -42,6 +42,11 @@ class _ImageResizerState extends State { return _showCupertinoMenu(); case TargetPlatform.android: return _showMaterialMenu(); + case TargetPlatform.macOS: + case TargetPlatform.windows: + case TargetPlatform.linux: + case TargetPlatform.fuchsia: + return _showMaterialMenu(); default: throw 'Not supposed to be invoked for $defaultTargetPlatform'; } @@ -68,7 +73,11 @@ class _ImageResizerState extends State { } Widget _slider( - double value, double max, String label, ValueChanged onChanged) { + double value, + double max, + String label, + ValueChanged onChanged, + ) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Card( diff --git a/lib/src/models/documents/attribute.dart b/lib/src/models/documents/attribute.dart index c8d40360..bc70b8d8 100644 --- a/lib/src/models/documents/attribute.dart +++ b/lib/src/models/documents/attribute.dart @@ -110,6 +110,12 @@ class Attribute { static const String mobileAlignment = 'mobileAlignment'; + /// For other platforms, for mobile use [mobileAlignment] + static const String alignment = 'alignment'; + + /// For other platforms, for mobile use [mobileMargin] + static const String margin = 'margin'; + static const ImageAttribute image = ImageAttribute(null); static const VideoAttribute video = VideoAttribute(null); diff --git a/lib/src/models/rules/rule.dart b/lib/src/models/rules/rule.dart index de9db513..96a6d413 100644 --- a/lib/src/models/rules/rule.dart +++ b/lib/src/models/rules/rule.dart @@ -59,8 +59,14 @@ class Rules { _customRules = customRules; } - Delta apply(RuleType ruleType, Document document, int index, - {int? len, Object? data, Attribute? attribute}) { + Delta apply( + RuleType ruleType, + Document document, + int index, { + int? len, + Object? data, + Attribute? attribute, + }) { final delta = document.toDelta(); for (final rule in _customRules + _rules) { if (rule.type != ruleType) { @@ -76,6 +82,8 @@ class Rules { rethrow; } } - throw 'Apply rules failed'; + throw FormatException( + 'Apply delta rules failed. No matching rule found for type: $ruleType', + ); } } diff --git a/lib/src/utils/platform.dart b/lib/src/utils/platform.dart index 96cb866b..75965bdd 100644 --- a/lib/src/utils/platform.dart +++ b/lib/src/utils/platform.dart @@ -1,12 +1,15 @@ import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter/foundation.dart'; +import 'package:flutter/foundation.dart' + show kIsWeb, TargetPlatform, defaultTargetPlatform; bool isMobile([TargetPlatform? targetPlatform]) { + if (kIsWeb) return false; targetPlatform ??= defaultTargetPlatform; return {TargetPlatform.iOS, TargetPlatform.android}.contains(targetPlatform); } bool isDesktop([TargetPlatform? targetPlatform]) { + if (kIsWeb) return false; targetPlatform ??= defaultTargetPlatform; return {TargetPlatform.macOS, TargetPlatform.linux, TargetPlatform.windows} .contains(targetPlatform); @@ -18,6 +21,7 @@ bool isKeyboardOS([TargetPlatform? targetPlatform]) { } bool isAppleOS([TargetPlatform? targetPlatform]) { + if (kIsWeb) return false; targetPlatform ??= defaultTargetPlatform; return { TargetPlatform.macOS, diff --git a/lib/src/widgets/editor.dart b/lib/src/widgets/editor.dart index 896b6ae4..11fd530d 100644 --- a/lib/src/widgets/editor.dart +++ b/lib/src/widgets/editor.dart @@ -497,7 +497,7 @@ class QuillEditorState extends State cupertinoTheme.primaryColor.withOpacity(0.40); cursorRadius ??= const Radius.circular(2); cursorOffset = Offset( - iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0); + iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context), 0); } else { textSelectionControls = materialTextSelectionControls; paintCursorAboveText = false; diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index 87a938bc..b4d814fe 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -974,7 +974,7 @@ class RawEditorState extends EditorState widget.selectionColor, widget.enableInteractiveSelection, _hasFocus, - MediaQuery.of(context).devicePixelRatio, + MediaQuery.devicePixelRatioOf(context), _cursorCont); return editableTextLine; } diff --git a/lib/src/widgets/text_block.dart b/lib/src/widgets/text_block.dart index 16bebeec..64dacb4f 100644 --- a/lib/src/widgets/text_block.dart +++ b/lib/src/widgets/text_block.dart @@ -159,7 +159,7 @@ class EditableTextBlock extends StatelessWidget { color, enableInteractiveSelection, hasFocus, - MediaQuery.of(context).devicePixelRatio, + MediaQuery.devicePixelRatioOf(context), cursorCont); final nodeTextDirection = getDirectionOfNode(line); children.add(Directionality( diff --git a/lib/src/widgets/toolbar/link_style_button2.dart b/lib/src/widgets/toolbar/link_style_button2.dart index 42aaff11..98140312 100644 --- a/lib/src/widgets/toolbar/link_style_button2.dart +++ b/lib/src/widgets/toolbar/link_style_button2.dart @@ -245,9 +245,8 @@ class _LinkStyleDialogState extends State { final constraints = widget.constraints ?? widget.dialogTheme?.linkDialogConstraints ?? () { - final mediaQuery = MediaQuery.of(context); - final maxWidth = - kIsWeb ? mediaQuery.size.width / 4 : mediaQuery.size.width - 80; + final size = MediaQuery.sizeOf(context); + final maxWidth = kIsWeb ? size.width / 4 : size.width - 80; return BoxConstraints(maxWidth: maxWidth, maxHeight: 80); }();