Skip to content

[Impeller] drawArc with large stroke width does not match Skia behavior. #158567

@seal-app-07

Description

@seal-app-07

Steps to reproduce

(flutter version is 3.24.3)

  1. flutter create
  2. Run the following code to see the animation displayed in the center
import 'dart:math';

import 'package:flutter/material.dart';

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

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

  // This widget is the root of your application.
  @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> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Container(
        color: Colors.red,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              FilledCircularIndicator(
                mainColor: const Color(0xFFEDEDED),
                subColor: const Color(0xFFEDEDED).withOpacity(0.4),
                borderColor: const Color(0xFFEDEDED),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class FilledCircularIndicator extends StatefulWidget {
  const FilledCircularIndicator({
    super.key,
    this.height = 20.0,
    this.width = 20.0,
    this.borderWidth = 1.0,
    this.animationDuration = const Duration(seconds: 5),
    required this.mainColor,
    required this.subColor,
    required this.borderColor,
  });

  final double height;

  final double width;

  final double borderWidth;

  final Duration animationDuration;

  final Color mainColor;

  final Color subColor;

  final Color borderColor;

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

class _FilledCircularIndicatorState extends State<FilledCircularIndicator>
    with SingleTickerProviderStateMixin {
  late AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: widget.animationDuration * 2,
      upperBound: 2,
      vsync: this,
    )..repeat();
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(5),
      width: widget.width,
      height: widget.height,
      decoration: BoxDecoration(
        border: Border.all(
          color: widget.borderColor,
          width: widget.borderWidth,
        ),
        borderRadius: BorderRadius.circular(20),
      ),
      child: AnimatedBuilder(
        animation: _animationController,
        builder: (context, _) => _animationController.value > 1
            ? CircularProgressIndicator(
                value: _animationController.value - 1,
                color: widget.mainColor,
                backgroundColor: widget.subColor,
                strokeWidth: 10,
              )
            : Transform(
                alignment: Alignment.center,
                transform: Matrix4.rotationY(pi),
                child: CircularProgressIndicator(
                  value: 1 - _animationController.value,
                  color: widget.mainColor,
                  backgroundColor: widget.subColor,
                  strokeWidth: 10,
                ),
              ),
      ),
    );
  }
}

Expected results

Circles form a nice regular circle.

Actual results

The center area is slightly collapsed.

Code sample

Code sample
import 'dart:math';

import 'package:flutter/material.dart';

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

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

  // This widget is the root of your application.
  @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> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Container(
        color: Colors.red,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              FilledCircularIndicator(
                mainColor: const Color(0xFFEDEDED),
                subColor: const Color(0xFFEDEDED).withOpacity(0.4),
                borderColor: const Color(0xFFEDEDED),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class FilledCircularIndicator extends StatefulWidget {
  const FilledCircularIndicator({
    super.key,
    this.height = 20.0,
    this.width = 20.0,
    this.borderWidth = 1.0,
    this.animationDuration = const Duration(seconds: 5),
    required this.mainColor,
    required this.subColor,
    required this.borderColor,
  });

  final double height;

  final double width;

  final double borderWidth;

  final Duration animationDuration;

  final Color mainColor;

  final Color subColor;

  final Color borderColor;

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

class _FilledCircularIndicatorState extends State<FilledCircularIndicator>
    with SingleTickerProviderStateMixin {
  late AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: widget.animationDuration * 2,
      upperBound: 2,
      vsync: this,
    )..repeat();
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(5),
      width: widget.width,
      height: widget.height,
      decoration: BoxDecoration(
        border: Border.all(
          color: widget.borderColor,
          width: widget.borderWidth,
        ),
        borderRadius: BorderRadius.circular(20),
      ),
      child: AnimatedBuilder(
        animation: _animationController,
        builder: (context, _) => _animationController.value > 1
            ? CircularProgressIndicator(
                value: _animationController.value - 1,
                color: widget.mainColor,
                backgroundColor: widget.subColor,
                strokeWidth: 10,
              )
            : Transform(
                alignment: Alignment.center,
                transform: Matrix4.rotationY(pi),
                child: CircularProgressIndicator(
                  value: 1 - _animationController.value,
                  color: widget.mainColor,
                  backgroundColor: widget.subColor,
                  strokeWidth: 10,
                ),
              ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration
Simulator.Screen.Recording.-.iPhone.15.Pro.iOS18.1.-.2024-11-13.at.19.25.21.mp4

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.24.3, on macOS 14.5 23F79 darwin-x64, locale ja-JP)
    • Flutter version 3.24.3 on channel stable at /Users/XXXXX/fvm/versions/3.24.3
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 2663184aa7 (9 weeks ago), 2024-09-11 16:27:48 -0500
    • Engine revision 36335019a8
    • Dart version 3.5.3
    • DevTools version 2.37.3

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
    • Android SDK at /Users/XXXXXX/Library/Android/sdk
    • Platform android-35, build-tools 35.0.0
    • ANDROID_HOME = /Users/XXXXXXX/Library/Android/sdk
    • ANDROID_SDK_ROOT = /Users/XXXXXXLibrary/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.0)
    • Xcode at /Applications/Xcode_16.app/Contents/Developer
    • Build 16A242d
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[!] Android Studio (version unknown)
    • Android Studio at /Applications/Android Studio Preview.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
    ✗ Unable to determine Android Studio version.
    • Java version OpenJDK Runtime Environment (build 21.0.3+-77717978-b509.4)

[✓] Android Studio (version 2024.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.11+0-17.0.11b1207.24-11852314)

[✓] VS Code (version 1.95.2)
    • VS Code at /Users/XXXXXX/tools/Visual Studio Code.app/Contents
    • Flutter extension version 3.100.0

[✓] Connected device (3 available)
    • iPhone 15 Pro(iOS18.1) (mobile) • 2A9EA93E-D232-47A2-9E91-B786B8674029 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-18-1 (simulator)
    • macOS (desktop)                 • macos                                • darwin-x64     • macOS 14.5 23F79 darwin-x64
    • Chrome (web)                    • chrome                               • web-javascript • Google Chrome 130.0.6723.117
    ! Error: Browsing on the local area network for iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
    ! Error: Browsing on the local area network for iPhone13. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)

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

! Doctor found issues in 1 category.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work liste: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.found in release: 3.24Found to occur in 3.24found in release: 3.27Found to occur in 3.27has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-iosiOS applications specificallyr: fixedIssue is closed as already fixed in a newer versionteam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions