Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Web]: App crashes after selecting a text that contains emojis in it. Throws "Bad UTF-8 encoding found while decoding string:" #79105

@delay

Description

@delay

My app is crashing when I print a string with an emoji in it. This problem is only able to be duplicated with flutter web. The error message on crash is: Bad UTF-8 encoding found while decoding string: Here is a project which reproduces the issue.

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(
          text:
              '😄 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
          style: TextStyle(color: Colors.black45, fontSize: 18)),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.text, this.style}) : super(key: key);

  final String text;
  final TextStyle style;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _textKey = GlobalKey();
  final List<Rect> _textRects = [];
  final List<Rect> _selectionRects = [];
  String selectedText = '';
  Rect _caretRect = Rect.zero;
  MouseCursor _cursor = SystemMouseCursors.basic;
  int _selectionBaseOffset;
  TextSelection _textSelection = TextSelection.collapsed(offset: -1);
  @override
  initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      _updateAllTextRects();
    });
  }

  void _onMouseMove(event) {
    if (event is PointerHoverEvent) {
      if (_renderParagraph == null) {
        return;
      }
      final allTextRects = _computeRectsForSelection(
        TextSelection(baseOffset: 0, extentOffset: widget.text.length),
      );
      bool isOverText = false;
      for (final rect in allTextRects) {
        if (rect.contains(event.localPosition)) {
          isOverText = true;
        }
      }
      final newCursor =
          isOverText ? SystemMouseCursors.text : SystemMouseCursors.basic;
      if (newCursor != _cursor) {
        setState(() {
          _cursor = newCursor;
        });
      }
    }
  }

  void _updateAllTextRects() {
    setState(() {
      _textRects
        ..clear()
        ..addAll(_computeRectsForSelection(
            TextSelection(baseOffset: 0, extentOffset: widget.text.length)));
    });
  }

  List<Rect> _computeRectsForSelection(TextSelection textSelection) {
    if (_renderParagraph == null) {
      return [];
    }
    final textBoxes = _renderParagraph.getBoxesForSelection(textSelection);
    return textBoxes.map((box) => box.toRect()).toList();
  }

  RenderParagraph get _renderParagraph =>
      _textKey.currentContext.findRenderObject() as RenderParagraph;

  void _onPanStart(DragStartDetails details) {
    if (_renderParagraph == null) {
      return;
    }
    _selectionBaseOffset =
        _renderParagraph.getPositionForOffset(details.localPosition).offset;
    _textSelection = TextSelection.collapsed(offset: _selectionBaseOffset);
    _updateSelectionDisplay();
  }

  void _updateSelectionDisplay() {
    //Compute selection rectangles
    final selectionRects = _computeRectsForSelection(_textSelection);
    //Update caret display
    final caretOffset =
        _renderParagraph.getOffsetForCaret(_textSelection.extent, Rect.zero);
    final caretHeight =
        _renderParagraph.getFullHeightForCaret(_textSelection.extent);
    setState(() {
      _selectionRects
        ..clear()
        ..addAll(selectionRects);
      _caretRect =
          Rect.fromLTWH(caretOffset.dx - 1, caretOffset.dy, 2, caretHeight);
      // widget.onSelectionChange?.call(_textSelection);
      selectedText = _textSelection.textInside(widget.text);
      print('selectedText: ' + selectedText.toString());
    });
  }

  void _onPanUpdate(DragUpdateDetails details) {
    final selectionExtentOffset =
        _renderParagraph.getPositionForOffset(details.localPosition).offset;
    _textSelection = TextSelection(
        baseOffset: _selectionBaseOffset, extentOffset: selectionExtentOffset);
    _updateSelectionDisplay();
  }

  void _onPanEnd(DragEndDetails details) {}
  void _onPanCancel() {}

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Container(
            color: Colors.white,
            child: Center(
              child: Listener(
                onPointerHover: _onMouseMove,
                child: MouseRegion(
                  cursor: _cursor,
                  child: GestureDetector(
                    onPanStart: _onPanStart,
                    onPanUpdate: _onPanUpdate,
                    onPanEnd: _onPanEnd,
                    onPanCancel: _onPanCancel,
                    child: Stack(
                      children: [
                        CustomPaint(
                          painter: _SelectionPainter(
                              color: Colors.yellow,
                              rects: _selectionRects,
                              fill: true),
                        ),
                        CustomPaint(
                          painter: _SelectionPainter(
                              color: Colors.grey,
                              rects: _textRects,
                              fill: false),
                        ),
                        Text(widget.text, key: _textKey, style: widget.style),
                        CustomPaint(
                          painter: _SelectionPainter(
                              color: Colors.blue,
                              rects: [_caretRect],
                              fill: true),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
          ),
          Text(selectedText, style: widget.style),
        ],
      ),
    );
  }
}

class _SelectionPainter extends CustomPainter {
  _SelectionPainter({
    @required Color color,
    @required List<Rect> rects,
    bool fill = true,
  })  : _color = color,
        _rects = rects,
        _fill = fill,
        _paint = Paint()..color = color;

  final Color _color;
  final List<Rect> _rects;
  final bool _fill;
  Paint _paint;

  @override
  void paint(Canvas canvas, Size size) {
    //a rectangle
    if (_fill == false) {
      //make an outline of rectangle
      _paint = Paint()
        ..color = _color
        ..style = PaintingStyle.stroke;
    }
    for (Rect _rect in _rects) {
      canvas.drawRect(_rect, _paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }
}

Steps to Reproduce

  1. in the above project flutter run -d chrome for web. It works fine in iOS.
  2. Select some text in the app with your mouse cursor. Once you include the emoji in your selection it will crash.
  3. If you comment out the print('selectedText: ' + selectedText.toString()); and then re-run the app, it will then select the emoji without any problems in the app.
  4. This problem is only able to be duplicated with flutter web. The error message on crash is: Bad UTF-8 encoding found while decoding string:
  5. I am using VSCode, I don't think this would make a difference, but since it is console output that is crashing, I thought I would mention it.

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work lista: typographyText rendering, possibly libtxtassigned for triageissue is assigned to a domain expert for further triagec: fatal crashCrashes that terminate the processfound in release: 2.1Found to occur in 2.1has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-webWeb applications specificallyr: fixedIssue is closed as already fixed in a newer versionteam-webOwned by Web platform team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions