flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
162.28k stars 26.66k forks source link

Drawing a path results in an unexpected behavior with Impeller #146648

Closed ueman closed 3 weeks ago

ueman commented 1 month ago

Steps to reproduce

Run the following code on iOS (simulator or phyiscal device, it doesn't matter) (complete code can be found in the collapsed block later)

final legLength = size.width * 0.3;
final paint = Paint();
paint.color = const Color(0xFFFF69b4);
paint.style = PaintingStyle.stroke;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = lineWidth;

final path = Path()
  // top left
  ..moveTo(0, legLength)
  ..lineTo(0, borderRadius)
  ..arcToPoint(
    Offset(borderRadius, 0),
    radius: Radius.circular(borderRadius),
  )
  ..lineTo(legLength, 0)
  // top right
  ..moveTo(size.width - legLength, 0)
  ..lineTo(size.width - borderRadius, 0)
  ..arcToPoint(
    Offset(size.width, borderRadius),
    radius: Radius.circular(borderRadius),
  )
  ..lineTo(size.width, legLength)
  // bottom right
  ..moveTo(size.width, size.height - legLength)
  ..lineTo(size.width, size.height - borderRadius)
  ..arcToPoint(
    Offset(size.width - borderRadius, size.height),
    radius: Radius.circular(borderRadius),
  )
  ..lineTo(size.width - legLength, size.height)
  // bottom right
  ..moveTo(legLength, size.height)
  ..lineTo(borderRadius, size.height)
  ..arcToPoint(
    Offset(0, size.height - borderRadius),
    radius: Radius.circular(borderRadius),
  )
  ..lineTo(0, size.height - legLength)
  ..moveTo(0, legLength)
  ..close();

canvas.drawPath(path, paint);

Expected results

With Impeller disabled (the following code added to Info.plist)

<key>FLTEnableImpeller</key>
<false/>

Simulator Screenshot - iPhone 15 Pro Max - 2024-04-11 at 19 46 27

Actual results

With Impeller Simulator Screenshot - iPhone 15 Pro Max - 2024-04-11 at 19 43 51

Code sample

Code sample ```dart import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: _FrameSquare( height: 200, width: 200, borderRadius: 4, lineWidth: 4, )), ); } } class _FrameSquare extends StatelessWidget { const _FrameSquare({ required this.height, required this.width, required this.borderRadius, required this.lineWidth, }); final double height; final double width; final double borderRadius; final double lineWidth; @override Widget build(BuildContext context) { return Center( child: CustomPaint( size: Size(width, height), painter: _FrameBorderPainter( borderRadius: borderRadius, lineWidth: lineWidth, ), ), ); } } class _FrameBorderPainter extends CustomPainter { _FrameBorderPainter({required this.borderRadius, required this.lineWidth}); final double borderRadius; final double lineWidth; @override void paint(Canvas canvas, Size size) { final legLength = size.width * 0.3; final paint = Paint(); paint.color = const Color(0xFFFF69b4); paint.style = PaintingStyle.stroke; paint.strokeCap = StrokeCap.round; paint.strokeWidth = lineWidth; final path = Path() // top left ..moveTo(0, legLength) ..lineTo(0, borderRadius) ..arcToPoint( Offset(borderRadius, 0), radius: Radius.circular(borderRadius), ) ..lineTo(legLength, 0) // top right ..moveTo(size.width - legLength, 0) ..lineTo(size.width - borderRadius, 0) ..arcToPoint( Offset(size.width, borderRadius), radius: Radius.circular(borderRadius), ) ..lineTo(size.width, legLength) // bottom right ..moveTo(size.width, size.height - legLength) ..lineTo(size.width, size.height - borderRadius) ..arcToPoint( Offset(size.width - borderRadius, size.height), radius: Radius.circular(borderRadius), ) ..lineTo(size.width - legLength, size.height) // bottom right ..moveTo(legLength, size.height) ..lineTo(borderRadius, size.height) ..arcToPoint( Offset(0, size.height - borderRadius), radius: Radius.circular(borderRadius), ) ..lineTo(0, size.height - legLength) ..moveTo(0, legLength) ..close(); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } ```

Screenshots or Video

Screenshots / Video demonstration [Upload media here]

Logs

Logs There are no logs

Flutter Doctor output

Doctor output ```console flutter doctor -v [✓] Flutter (Channel stable, 3.19.3, on macOS 14.3.1 23D60 darwin-arm64, locale en-DE) • Flutter version 3.19.3 on channel stable at /Users/ueman/Developer/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision ba39319843 (5 weeks ago), 2024-03-07 15:22:21 -0600 • Engine revision 2e4ba9c6fb • Dart version 3.3.1 • DevTools version 2.31.1 [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /Users/ueman/Library/Android/sdk • Platform android-34, build-tools 34.0.0 • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 15.3) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 15E204a • CocoaPods version 1.15.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2023.1) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) [✓] VS Code (version 1.88.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.86.0 [✓] Connected device (4 available) • iPhone-GWTGT1UTPLJQ (mobile) • 00008030-000E4D310CD3402E • ios • iOS 17.4.1 21E236 • iPhone 15 Pro Max (mobile) • 4E4B3D83-EAA8-490B-B699-90A09831AC06 • ios • com.apple.CoreSimulator.SimRuntime.iOS-17-4 (simulator) • macOS (desktop) • macos • darwin-arm64 • macOS 14.3.1 23D60 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 123.0.6312.107 [✓] Network resources • All expected network resources are available. • No issues found! ```
ueman commented 1 month ago

I could narrow it down a bit more:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
          child: _FrameSquare(
        height: 200,
        width: 200,
        borderRadius: 4,
        lineWidth: 4,
      )),
    );
  }
}

class _FrameSquare extends StatelessWidget {
  const _FrameSquare({
    required this.height,
    required this.width,
    required this.borderRadius,
    required this.lineWidth,
  });
  final double height;
  final double width;
  final double borderRadius;
  final double lineWidth;
  @override
  Widget build(BuildContext context) {
    return Center(
      child: CustomPaint(
        size: Size(width, height),
        painter: _FrameBorderPainter(
          borderRadius: borderRadius,
          lineWidth: lineWidth,
        ),
      ),
    );
  }
}

class _FrameBorderPainter extends CustomPainter {
  _FrameBorderPainter({required this.borderRadius, required this.lineWidth});
  final double borderRadius;
  final double lineWidth;
  @override
  void paint(Canvas canvas, Size size) {
    final legLength = size.width * 0.3;
    final paint = Paint();
    paint.color = const Color(0xFFFF69b4);
    paint.style = PaintingStyle.stroke;
    paint.strokeCap = StrokeCap.round;
    paint.strokeWidth = lineWidth;

    final path = Path()
      ..moveTo(0, legLength)
      ..lineTo(0, borderRadius)
      ..arcToPoint(
        Offset(borderRadius, 0),
        radius: Radius.circular(borderRadius),
      )
      ..lineTo(legLength, 0)
      ..moveTo(0, legLength)
      ..close();

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

That code results in the following images:

Impeller disabled Impeller enabled
image image
darshankawar commented 1 month ago

Thanks for the bug report. I was able to replicate this on latest stable and master versions.

stable, master flutter doctor -v ``` [!] Flutter (Channel stable, 3.19.5, on macOS 12.2.1 21D62 darwin-x64, locale en-GB) • Flutter version 3.19.5 on channel stable at /Users/dhs/documents/fluttersdk/flutter ! Warning: `flutter` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 300451adae (4 days ago), 2024-03-27 21:54:07 -0500 • Engine revision e76c956498 • Dart version 3.3.3 • DevTools version 2.31.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [!] Xcode - develop for iOS and macOS (Xcode 12.3) • Xcode at /Applications/Xcode.app/Contents/Developer ! Flutter recommends a minimum Xcode version of 13. Download the latest version or update via the Mac App Store. • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.21.0 [✓] Connected device (5 available) • SM G975F (mobile) • RZ8M802WY0X • android-arm64 • Android 11 (API 30) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 14.4.1 18D61 • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729 • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator) • macOS (desktop) • macos • darwin-x64 • Mac OS X 10.15.4 19E2269 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 98.0.4758.80 [✓] HTTP Host Availability • All required HTTP hosts are available ! Doctor found issues in 1 category. [!] Flutter (Channel master, 3.22.0-7.0.pre.14, on macOS 12.2.1 21D62 darwin-x64, locale en-GB) • Flutter version 3.22.0-7.0.pre.14 on channel master at /Users/dhs/documents/fluttersdk/flutter ! Warning: `flutter` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 74b65d4e4c (4 hours ago), 2024-04-09 21:26:14 -0400 • Engine revision eaf73cd39c • Dart version 3.5.0 (build 3.5.0-36.0.dev) • DevTools version 2.34.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [!] Android toolchain - develop for Android devices (Android SDK version 30.0.3) • Android SDK at /Users/dhs/Library/Android/sdk ✗ cmdline-tools component is missing Run `path/to/sdkmanager --install "cmdline-tools;latest"` See https://developer.android.com/studio/command-line for more details. ✗ Android license status unknown. Run `flutter doctor --android-licenses` to accept the SDK licenses. See https://flutter.dev/docs/get-started/install/macos#android-setup for more details. [✓] Xcode - develop for iOS and macOS (Xcode 13.2.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 13C100 • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] IntelliJ IDEA Ultimate Edition (version 2021.3.2) • IntelliJ at /Applications/IntelliJ IDEA.app • Flutter plugin version 65.1.4 • Dart plugin version 213.7228 [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.29.0 [✓] Connected device (3 available) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 15.3.1 19D52 • macOS (desktop) • macos • darwin-x64 • macOS 12.2.1 21D62 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 109.0.5414.119 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. [!] Xcode - develop for iOS and macOS (Xcode 12.3) • Xcode at /Applications/Xcode.app/Contents/Developer ! Flutter recommends a minimum Xcode version of 13. Download the latest version or update via the Mac App Store. • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.21.0 [✓] Connected device (5 available) • SM G975F (mobile) • RZ8M802WY0X • android-arm64 • Android 11 (API 30) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 14.4.1 18D61 • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729 • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator) • macOS (desktop) • macos • darwin-x64 • Mac OS X 10.15.4 19E2269 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 98.0.4758.80 [✓] HTTP Host Availability • All required HTTP hosts are available ! Doctor found issues in 1 category. ```
jonahwilliams commented 1 month ago

The reason this is happening is a bug with Path.close(). We should be ignoring the final close call which is supposed to draw a line to the path start - since this is stroke and not a fill.

jonahwilliams commented 1 month ago

So we should fix this, but in this case you don't need/shouldn't use the Path.close at the end there. Because the fix is going to be ignoring it anyway

ueman commented 1 month ago

I can confirm that the workaround works like a charm. Thanks for that!

github-actions[bot] commented 1 week ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.