Skip to content

Color.lerp asserts when mixing Display P3 and sRGB colors in ThemeData/ColorScheme #182777

@spydon

Description

@spydon

Steps to reproduce

Use Color.from() with ColorSpace.displayP3 in a ColorScheme and render any widget that internally animates colors (e.g. TextField).

  MaterialApp(
    theme: ThemeData(
      colorScheme: ColorScheme.light(
        primary: const Color.from(
          alpha: 1,
          red: 0.0272,
          green: 0.2264,
          blue: 0.2928,
          colorSpace: ColorSpace.displayP3,
        ),
      ),
    ),
    home: const Scaffold(body: TextField()),
  )

Tap the TextField to trigger focus animation.

Expected results

Color.lerp should handle mixed color spaces gracefully, either by normalizing to a common space before interpolation or by documenting that ColorScheme/ThemeData only supports sRGB.

Actual results

The assertion fires because InputDecorator creates sRGB border colors internally and BorderSide.lerp calls Color.lerp with one sRGB and one Display P3 operand. The same issue affects AnimatedTheme (via ThemeData.lerp), TextStyleTween, ColorTween, and any other framework code that lerps theme-sourced colors against internally-created sRGB colors.

══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
The following assertion was thrown during paint():
'dart:ui/painting.dart': Failed assertion: line 428 pos 16: '<optimized out>': is not true.

Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=02_bug.yml

The relevant error-causing widget was:
  TextField TextField:file:///home/spydon/repos/h2-core/app/test/unit/display_p3_repro_test.dart:24:17

When the exception was thrown, this was the stack:
#2      Color.lerp (dart:ui/painting.dart:428:16)
#3      BorderSide.lerp (package:flutter/src/painting/borders.dart:271:22)
#4      UnderlineInputBorder.lerpFrom (package:flutter/src/material/input_border.dart:217:32)
#5      ShapeBorder.lerp (package:flutter/src/painting/borders.dart:516:36)
#6      _InputBorderTween.lerp (package:flutter/src/material/input_decorator.dart:110:45)
#7      Tween.transform (package:flutter/src/animation/tween.dart:369:12)
#8      Animatable.evaluate (package:flutter/src/animation/tween.dart:71:46)
#9      _InputBorderPainter.paint (package:flutter/src/material/input_decorator.dart:140:44)
#10     RenderCustomPaint._paintWithPainter (package:flutter/src/rendering/custom_paint.dart:593:13)
#11     RenderCustomPaint.paint (package:flutter/src/rendering/custom_paint.dart:646:7)
#12     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#13     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#14     _RenderDecoration.paint.doPaint (package:flutter/src/material/input_decorator.dart:1598:17)
#15     _RenderDecoration.paint (package:flutter/src/material/input_decorator.dart:1602:5)
#16     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#17     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#18     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:143:13)
#19     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#20     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#21     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:143:13)
#22     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#23     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#24     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:143:13)
#25     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#26     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#27     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:143:13)
#28     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#29     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#30     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:143:13)
#31     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#32     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#33     RenderBoxContainerDefaultsMixin.defaultPaint (package:flutter/src/rendering/box.dart:3368:15)
#34     RenderCustomMultiChildLayoutBox.paint (package:flutter/src/rendering/custom_layout.dart:422:5)
#35     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#36     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#37     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:143:13)
#38     _RenderInkFeatures.paint (package:flutter/src/material/material.dart:634:11)
#39     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#40     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#41     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:143:13)
#42     RenderPhysicalModel.paint.<anonymous closure> (package:flutter/src/rendering/proxy_box.dart:2252:15)
#43     PaintingContext.pushClipRRect (package:flutter/src/rendering/object.dart:626:14)
#44     RenderPhysicalModel.paint (package:flutter/src/rendering/proxy_box.dart:2239:21)
#45     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#46     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#47     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:143:13)
#48     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#49     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:265:13)
#50     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:143:13)
#51     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:3429:7)
#52     PaintingContext._repaintCompositedChild (package:flutter/src/rendering/object.dart:180:11)
#53     PaintingContext.repaintCompositedChild (package:flutter/src/rendering/object.dart:125:5)
#54     PipelineOwner.flushPaint (package:flutter/src/rendering/object.dart:1325:31)
#55     PipelineOwner.flushPaint (package:flutter/src/rendering/object.dart:1335:15)
#56     AutomatedTestWidgetsFlutterBinding.drawFrame (package:flutter_test/src/binding.dart:2137:31)
#57     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:495:5)
#58     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1430:15)
#59     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1345:9)
#60     AutomatedTestWidgetsFlutterBinding.pump.<anonymous closure> (package:flutter_test/src/binding.dart:1960:9)
#63     TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:74:41)
#64     AutomatedTestWidgetsFlutterBinding.pump (package:flutter_test/src/binding.dart:1949:27)
#65     WidgetTester.pumpAndSettle.<anonymous closure> (package:flutter_test/src/widget_tester.dart:719:23)
<asynchronous suspension>
#66     TestAsyncUtils.guard.<anonymous closure> (package:flutter_test/src/test_async_utils.dart:130:27)
<asynchronous suspension>
#67     main.<anonymous closure> (file:///home/spydon/repos/h2-core/app/test/unit/display_p3_repro_test.dart:31:5)
<asynchronous suspension>
#68     testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:192:15)
<asynchronous suspension>
#69     TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:1682:5)
<asynchronous suspension>
<asynchronous suspension>
(elided 5 frames from class _AssertionError, dart:async, and package:stack_trace)

The following RenderObject was being processed when the exception was fired: RenderCustomPaint#5c433:
  creator: CustomPaint ← _BorderContainer ← _Decorator ← InputDecorator ← AnimatedBuilder ← Listener ←
    RawGestureDetector ← TextSelectionGestureDetector ← Semantics ← AnimatedBuilder ← IgnorePointer ←
    TextFieldTapRegion ← ⋯
  parentData: offset=Offset(0.0, 0.0) (can use size)
  constraints: BoxConstraints(w=800.0, h=48.0)
  size: Size(800.0, 48.0)
  painter: null
  foregroundPainter: _InputBorderPainter#b661f
This RenderObject has no descendants.

Code sample

Code sample
import 'dart:ui' show ColorSpace;

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Display P3 color in ColorScheme triggers Color.lerp assert', (
    tester,
  ) async {
    await tester.pumpWidget(
      MaterialApp(
        theme: ThemeData(
          colorScheme: ColorScheme.light(
            primary: const Color.from(
              alpha: 1,
              red: 0.0272,
              green: 0.2264,
              blue: 0.2928,
              colorSpace: ColorSpace.displayP3,
            ),
          ),
        ),
        home: const Scaffold(
          body: TextField(),
        ),
      ),
    );

    // Trigger validation which causes AnimatedDefaultTextStyle to lerp
    await tester.tap(find.byType(TextField));
    await tester.pumpAndSettle();
  });
}

Screenshots or Video

No response

Logs

No response

Flutter Doctor output

Doctor output I tested both on main and stable with exactly the same results.

main:

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel main, 3.42.0-1.0.pre-222, on Ubuntu 24.04.4 LTS 6.8.0-100-generic, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!

stable:

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.41.1, on Ubuntu 24.04.4 LTS 6.8.0-100-generic, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listf: material designflutter/packages/flutter/material repository.frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions