Skip to content

Issue with Grid Line Alignment in Flutter-Quill Editor #2553

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

Open
1 task done
shanshen6 opened this issue Apr 29, 2025 · 3 comments
Open
1 task done

Issue with Grid Line Alignment in Flutter-Quill Editor #2553

shanshen6 opened this issue Apr 29, 2025 · 3 comments
Labels
bug Something isn't working

Comments

@shanshen6
Copy link

Have you checked for an existing issue?

Flutter Quill Version

Flutter-Quill Version: 11.2.0 (or “flutter_quill: ^11.0.0-dev.4”)

Steps to Reproduce

Dear Flutter-Quill Developers,

I hope this message finds you well. I am reaching out to report an issue I encountered while attempting to draw grid lines in the Flutter-Quill editor.

I noticed that the alignment of each line within the editor is not accurate, which prevents me from correctly drawing the grid lines. As a result, the grid lines are misaligned, leading to an unsatisfactory layout. I have set up custom styles for the editor, but despite my efforts, the grid lines do not align as expected.

Here are the details of my environment:
• Flutter-Quill Version: 11.2.0 (or “flutter_quill: ^11.0.0-dev.4”)
• Flutter Version:
Flutter 3.27.1 • channel stable
Framework • revision 17025dd882 (4 months ago)
Engine • revision cb4b5fff73
Tools • Dart 3.6.0 • DevTools 2.40.2

Custom Styles: I am using a custom style setup for the editor, but the issue persists.

Could you please advise on how to resolve this issue or provide any guidance on how to achieve accurate line alignment when drawing grid lines in the editor?

I would greatly appreciate any insights or suggestions.

Thank you for your time and support.

this is my code:

@OverRide
Widget build(BuildContext context) {
final style =
YwDefaultStyles.getYwInstance(context, widget.controller.ywEditorConfig.editorType);
widget.controller.ywEditorConfig.defaultStyles = style;
return QuillEditor(
focusNode: _editorFocusNode,
scrollController: _editorScrollController,
controller: _controller,
config: QuillEditorConfig(
editorKey: _controller.editorKey,
scrollable: widget.controller.ywEditorConfig.editorType != EditorType.chapter,
expands: true,
showCursor: !widget.controller.ywEditorConfig.readOnly,
placeholder: widget.controller.ywEditorConfig.placeholder,
contextMenuBuilder:
YwQuillRawEditorConfig.getContextMenuBuilder(widget.controller.ywEditorConfig),
customStyleBuilder: (Attribute attribute) {
return CustomAttrRender.render(attribute, context);
},
enableSelectionToolbar: !widget.controller.ywEditorConfig.readOnly,
customStyles: style,
embedBuilders: widget.controller.ywEditorConfig.showImage
? QuillImageCustomConfig.embedBuilders(widget.controller.ywEditorConfig)
: [],
onLaunchUrl: (url) {
print('lgy click url = ${url}');
},
onTapUp: onTapUp,
),
);
}

style:
static DefaultStyles getChapterInstance(BuildContext context) {
final themeData = Theme.of(context);
final defaultTextStyle = DefaultTextStyle.of(context);
double fontSize;
try {
fontSize = double.parse(KVConfig.get(editorFontSize, '16'));
} catch (e) {
fontSize = 16.0;
}
double lineHeight = 1.8;
try {
lineHeight = double.parse(KVConfig.get(editorLineHeight, '1.8'));
} catch (e) {
lineHeight = 1.8;
}

final String textColorStr = KVConfig.get(EDITOR_FONTCOLOR, '');
Color? textColor =
    textColorStr.isNotEmpty ? Color(int.parse(textColorStr)) : themeData.auColors.gray_5_5;
final baseStyle = defaultTextStyle.style.copyWith(
  fontSize: fontSize,
  color: textColor,
  height: lineHeight,
  decoration: TextDecoration.none,
  fontWeight: FontWeight.w400,
  fontFamily: 'PingFang SC',
  backgroundColor: Colors.blue,
);

return DefaultStyles(
  paragraph: DefaultTextBlockStyle(
    baseStyle,
    HorizontalSpacing.zero,
    VerticalSpacing.zero,
    VerticalSpacing.zero,
    null,
  ),
  lineHeightNormal: DefaultTextBlockStyle(
    baseStyle,
    HorizontalSpacing.zero,
    VerticalSpacing.zero,
    VerticalSpacing.zero,
    null,
  ),
  lineHeightTight: DefaultTextBlockStyle(
    baseStyle,
    HorizontalSpacing.zero,
    VerticalSpacing.zero,
    VerticalSpacing.zero,
    null,
  ),
  lineHeightOneAndHalf: DefaultTextBlockStyle(
    baseStyle,
    HorizontalSpacing.zero,
    VerticalSpacing.zero,
    VerticalSpacing.zero,
    null,
  ),
  lineHeightDouble: DefaultTextBlockStyle(
    baseStyle,
    HorizontalSpacing.zero,
    VerticalSpacing.zero,
    VerticalSpacing.zero,
    null,
  ),
  placeHolder: DefaultTextBlockStyle(
    defaultTextStyle.style.copyWith(
      fontSize: 16,
      height: 1.8,
      color: Colors.grey.withValues(alpha: 0.6),
      fontWeight: FontWeight.w400,
      fontFamily: 'PingFang SC',
    ),
    HorizontalSpacing.zero,
    VerticalSpacing.zero,
    VerticalSpacing.zero,
    null,
  ),
  leading: DefaultTextBlockStyle(
    baseStyle,
    HorizontalSpacing.zero,
    VerticalSpacing.zero,
    VerticalSpacing.zero,
    null,
  ),
);

}

gridline:
import 'package:au_base_utils/au_base_utils.dart';
import 'package:au_grid_lines/view_model/grid_lines_view_model.dart';
import 'package:au_grid_lines/view_model/model/grid_lines_model.dart';
import 'package:au_provider/au_provider.dart';
import 'package:flutter/material.dart';

class GridLinesView extends StatefulWidget {
const GridLinesView({
super.key,
});

@OverRide
GridLinesViewState createState() => GridLinesViewState();
}

class GridLinesViewState extends State with WidgetsBindingObserver {
GridLineViewModel? viewModel;

@OverRide
Widget build(BuildContext context) {
return _createUI(context);
}

Widget _createUI(BuildContext context) {
return BaseDisposeRiverpodWidget<GridLineViewModel, BaseDataState>(
provider: gridLineProvider,
onReady: (viewModel, ref, context) {
this.viewModel = viewModel;
},
builder: (context, ref, viewModel, state, child) {
if (state.status == RiverpodDataState.loading) {
return const SizedBox.shrink();
}

    final model = state.data!;

    // 构建 TextStyle
    final textStyle = TextStyle(
      fontSize: model.fontSize,
      height: model.lineHeight,
      decoration: TextDecoration.none,
      fontWeight: FontWeight.w400,
      fontFamily: 'PingFang SC',
      color: context.color.gray6,
    );

    // 使用 TextPainter 计算实际行高
    final textPainter = TextPainter(
      text: const TextSpan(text: '示例Ab?*.!'), // 使用任意非空文本计算行高
      textDirection: TextDirection.ltr,
      textScaler: MediaQuery.of(context).textScaler,
      strutStyle: StrutStyle.fromTextStyle(textStyle, forceStrutHeight: true),
    )..layout();

    final actualLineHeight = textPainter.height;

    return RepaintBoundary(
      child: CustomPaint(
        painter: GridLinesPainter(
          lineType: model.lineType,
          viewHeight: actualLineHeight,
          color: context.color.gray6,
        ),
        size: Size.infinite,
      ),
    );
  },
);

}
}

class GridLinesPainter extends CustomPainter {
int lineType = 0;
double viewHeight = 29.0;
Color color = Colors.transparent;

GridLinesPainter({
required this.lineType,
required this.viewHeight,
required this.color,
});

@OverRide
void paint(Canvas canvas, Size size) {
// 如果网格线类型为 0,则不绘制网格线
if (lineType < 1) return;

final paint = Paint()
  ..color = color
  ..strokeWidth = 0.5;

// 使用计算出的行高
final double totalLineHeight = viewHeight;
double y = 0;

// 绘制水平网格线
while (y < size.height) {
  if (lineType == 1) {
    // 实线
    canvas.drawLine(
      Offset(0, y + totalLineHeight),
      Offset(size.width, y + totalLineHeight),
      paint,
    );
  } else if (lineType == 2) {
    // 虚线
    const dashWidth = 5.0;
    const dashSpace = 3.0;
    double startX = 0;

    while (startX < size.width) {
      canvas.drawLine(
        Offset(startX, y + totalLineHeight),
        Offset(startX + dashWidth, y + totalLineHeight),
        paint,
      );
      startX += dashWidth + dashSpace;
    }
  } else if (lineType == 3) {
    // 点线
    const dotSpace = 3.0;
    double startX = 0;

    while (startX < size.width) {
      canvas.drawCircle(
        Offset(startX, y + totalLineHeight),
        0.5,
        paint,
      );
      startX += dotSpace;
    }
  }
  y += totalLineHeight;
}

}

@OverRide
bool shouldRepaint(covariant GridLinesPainter oldDelegate) {
return oldDelegate.lineType != lineType ||
oldDelegate.viewHeight != viewHeight ||
oldDelegate.color != color;
}
}

Expected results

Image
Image

Actual results

Image

Additional Context

Screenshots / Video demonstration

[Attach media here]

Logs
[Paste logs here]
@shanshen6 shanshen6 added the bug Something isn't working label Apr 29, 2025
@shanshen6
Copy link
Author

Image Why isn’t there an exposed API for more granular control over text baseline alignment for different languages or character sets?

@shanshen6
Copy link
Author

Image

Image

@wangjinshan
Copy link

Our desired goal is to provide the best service to our customers.

Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants