Skip to content

Path StrokeJoin behaves differently on Android and web #186942

Description

@jakob-kristiansen

Steps to reproduce

  1. Create a blank application project flutter create --template app {app_name}
  2. Replace main.dart with the provided code sample
  3. Run the app on web device (Chrome in my case) and observe stroke joins
  4. Run the app on an Android device (Android 16 in my case) and observe different stroke joins

Expected results

When the path is composed of multiple subpaths by use of Path().addPath() I did not expect stroke join to take effect.

Actual results

On web (Chrome) I get the expected result. On Android, the bevel stroke join seems to be applied.

Code sample

Code sample
import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: JoinStylesDemo()));

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Path StrokeJoin Comparison")),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              _buildColumn("PathJoinPainter - subpaths", false),
              _buildColumn("PathJoinPainter - combined path", true),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildColumn(String title, bool usePainter2) {
    return Column(
      children: [
        Text(title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
        const SizedBox(height: 20),
        _buildCanvas(StrokeJoin.miter, "Miter", usePainter2),
        const SizedBox(height: 20),
        _buildCanvas(StrokeJoin.round, "Round", usePainter2),
        const SizedBox(height: 20),
        _buildCanvas(StrokeJoin.bevel, "Bevel", usePainter2),
      ],
    );
  }

  Widget _buildCanvas(StrokeJoin join, String label, bool usePainter2) {
    return Column(
      children: [
        CustomPaint(
          size: const Size(120, 80),
          painter: usePainter2 ? PathJoinPainter2(join) : PathJoinPainter(join),
        ),
        const SizedBox(height: 8),
        Text(label),
      ],
    );
  }
}

class PathJoinPainter extends CustomPainter {
  final StrokeJoin joinStyle;
  PathJoinPainter(this.joinStyle);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 20
      ..style = PaintingStyle.stroke
      ..strokeJoin = joinStyle;

    final path = Path();
    path.moveTo(0, size.height);
    path.lineTo(size.width / 2, 0);
    // PathJoinPainter specifically adds a path segment
    path.addPath(
      Path()
        ..moveTo(size.width / 2, 0)
        ..lineTo(size.width, size.height),
      Offset.zero,
    );

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class PathJoinPainter2 extends CustomPainter {
  final StrokeJoin joinStyle;
  PathJoinPainter2(this.joinStyle);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.redAccent
      ..strokeWidth = 20
      ..style = PaintingStyle.stroke
      ..strokeJoin = joinStyle;

    final path = Path();
    // PathJoinPainter2 uses a single continuous path
    path.moveTo(0, size.height);
    path.lineTo(size.width / 2, 0);
    path.lineTo(size.width, size.height);

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

Screenshots or Video

Screenshots / Video demonstration Web output Image Android output Image

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.44.0, on Ubuntu 24.04.3 LTS 6.8.0-117-generic, locale en_US.UTF-8) [31ms]
    • Flutter version 3.44.0 on channel stable at /home/jakob/development/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 559ffa3f75 (7 days ago), 2026-05-15 14:13:13 -0700
    • Engine revision 4c525dac5e
    • Dart version 3.12.0
    • DevTools version 2.57.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-swift-package-manager, omit-legacy-version-file,
      enable-lldb-debugging, enable-uiscene-migration

[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [1,068ms]
    • Android SDK at /home/jakob/Android/Sdk
    • Emulator version 36.2.12.0 (build_id 14214601) (CL:N/A)
    • Platform android-36, build-tools 36.1.0
    • ANDROID_HOME = /home/jakob/Android/Sdk
    • Java binary at: /home/jakob/Downloads/android-studio-2025.2.1.8-linux/android-studio/jbr/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.8+-14196175-b1038.72)
    • All Android licenses accepted.

[✓] Chrome - develop for the web [7ms]
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop [334ms]
    • Ubuntu clang version 18.1.3 (1ubuntu1)
    • cmake version 3.28.3
    • ninja version 1.11.1
    • pkg-config version 1.8.1
    • OpenGL core renderer: AMD Radeon 780M (radeonsi, phoenix, LLVM 20.1.2, DRM 3.57, 6.8.0-117-generic)
    • OpenGL core version: 4.6 (Core Profile) Mesa 25.0.7-0ubuntu0.24.04.2
    • OpenGL core shading language version: 4.60
    • OpenGL ES renderer: AMD Radeon 780M (radeonsi, phoenix, LLVM 20.1.2, DRM 3.57, 6.8.0-117-generic)
    • OpenGL ES version: OpenGL ES 3.2 Mesa 25.0.7-0ubuntu0.24.04.2
    • OpenGL ES shading language version: OpenGL ES GLSL ES 3.20
    • GL_EXT_framebuffer_blit: yes
    • GL_EXT_texture_format_BGRA8888: yes

[✓] Connected device (3 available) [183ms]
    • SM X306B (mobile) • R52Y406GNHA • android-arm64  • Android 16 (API 36)
    • Linux (desktop)   • linux       • linux-x64      • Ubuntu 24.04.3 LTS 6.8.0-117-generic
    • Chrome (web)      • chrome      • web-javascript • Google Chrome 147.0.7727.137

[✓] Network resources [267ms]
    • All expected network resources are available.

• No issues found!

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work listc: renderingUI glitches reported at the engine/skia or impeller rendering levele: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-androidAndroid applications specificallyteam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions