Skip to content

Extending border and using it in animated scenarios throws exception #180297

Closed

Description

@obiwanzenobi

Steps to reproduce

To reproduce:

  1. extend BoxBorder class with your custom
  2. put created border in AnimatedContainer with BoxDecoration
  3. change param of your border (e.g. color)
BoxBorder.lerp can only interpolate Border and BorderDirectional classes.
BoxBorder.lerp() was called with two objects of type GradientBoxBorder and GradientBoxBorder:
2
  GradientBoxBorder()
However, only Border and BorderDirectional classes are supported by this method.
For a more general interpolation method, consider using ShapeBorder.lerp instead.

Expected results

I should be able to provide way to interpolate my border for the framework.
Extending a class that is not final should not break anything internally the framework.
I should not be forced to write custom container or decoration if I only want to change one param.
As a package provider I want to share some custom decorations implementations.

Actual results

During the animation view shows red error wall.

Code sample

I have reproduction sample on this branch:
https://github.com/obiwanzenobi/gradient-borders/tree/fix/support-animated-container
run it and hit the button in the appbar

Screenshots or Video

Logs

════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building AnimatedContainer(duration: 500ms, bg: BoxDecoration(border: GradientBoxBorder(), borderRadius: BorderRadius.circular(16.0)), BoxConstraints(w=100.0, h=100.0), clipBehavior: Clip.none, dirty, state: _AnimatedContainerState#c9f36(ticker active, bg: DecorationTween(BoxDecoration(border: GradientBoxBorder(), borderRadius: BorderRadius.circular(16.0)) → BoxDecoration(border: GradientBoxBorder(), borderRadius: BorderRadius.circular(16.0))), BoxConstraintsTween(BoxConstraints(w=100.0, h=100.0) → BoxConstraints(w=100.0, h=100.0)))):
BoxBorder.lerp can only interpolate Border and BorderDirectional classes.
BoxBorder.lerp() was called with two objects of type GradientBoxBorder and GradientBoxBorder:
2
  GradientBoxBorder()
However, only Border and BorderDirectional classes are supported by this method.
For a more general interpolation method, consider using ShapeBorder.lerp instead.

The relevant error-causing widget was:
    AnimatedContainer AnimatedContainer:file:///Users/obiwanzenobi/Projects/gradient-borders/example/lib/main.dart:54:13

When the exception was thrown, this was the stack:
#0      BoxBorder.lerp (package:flutter/src/painting/box_border.dart:206:5)
box_border.dart:206
#1      BoxDecoration.lerp (package:flutter/src/painting/box_decoration.dart:309:25)
box_decoration.dart:309
#2      BoxDecoration.lerpFrom (package:flutter/src/painting/box_decoration.dart:256:38)
box_decoration.dart:256
#3      Decoration.lerp (package:flutter/src/painting/decoration.dart:154:14)
decoration.dart:154
#4      DecorationTween.lerp (package:flutter/src/widgets/implicit_animations.dart:84:43)
implicit_animations.dart:84
#5      Tween.transform (package:flutter/src/animation/tween.dart:369:12)
tween.dart:369
#6      Animatable.evaluate (package:flutter/src/animation/tween.dart:71:46)
tween.dart:71
#7      _AnimatedContainerState.build (package:flutter/src/widgets/implicit_animations.dart:830:32)
implicit_animations.dart:830

Flutter Doctor output

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.32.8, on macOS 26.1 25B78 darwin-arm64, locale pl-PL)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.0.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2025.1)
[✓] VS Code (version 1.107.1)
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!

Activity

  1. tirth-patel-nc commented on Jan 2, 2026

    @tirth-patel-nc
    Member

    Thanks for the report. Seeing the same behaviour with latest SDK versions.

    stable : 3.38.5
    master : 3.40.0-1.0.pre-369
    
  2. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    p: material_uimaterial_ui package in flutter/packages
    a: error messageError messages from the Flutter framework
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    and removed
    in triagePresently being triaged by the triage team
    on Jan 2, 2026
  3. added
    P2Important issues not at the top of the work list
    on Jan 14, 2026
  4. justinmc commented on Jan 14, 2026

    @justinmc
    Contributor

    @obiwanzenobi Can you paste in a runnable repro of this?

  5. obiwanzenobi commented on Jan 14, 2026

    @obiwanzenobi
    Author

    @obiwanzenobi Can you paste in a runnable repro of this?

    Yes, I will put single file repro tomorrow - in the meantime you can check repository attached in the issue - which has runnable reproduction scenario

  6. marejonc commented on Mar 9, 2026

    @marejonc

    @justinmc Here is a condensed repro

    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(
          debugShowCheckedModeBanner: false,
          title: 'Flutter Demo',
          theme: ThemeData(primarySwatch: Colors.blue),
          home: const MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key});
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      bool _shouldShowSecondState = false;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text("Gradient borders"),
            actions: [
              IconButton(
                icon: const Icon(Icons.refresh),
                onPressed: () {
                  setState(() {
                    _shouldShowSecondState = !_shouldShowSecondState;
                  });
                },
              ),
            ],
          ),
          body: Center(
            child: AnimatedContainer(
              duration: const Duration(milliseconds: 500),
              width: 100,
              height: 100,
              decoration: BoxDecoration(
                border: GradientBoxBorder(
                  gradient: _shouldShowSecondState
                      ? LinearGradient(colors: [Colors.green, Colors.pink])
                      : LinearGradient(colors: [Colors.blue, Colors.red]),
                  width: 4,
                ),
                borderRadius: BorderRadius.circular(16),
              ),
            ),
          ),
        );
      }
    }
    
    class GradientBoxBorder extends BoxBorder {
      const GradientBoxBorder({required this.gradient, this.width = 1.0});
    
      final Gradient gradient;
      final double width;
    
      @override
      BorderSide get bottom => BorderSide.none;
    
      @override
      BorderSide get top => BorderSide.none;
    
      @override
      EdgeInsetsGeometry get dimensions => EdgeInsets.all(width);
    
      @override
      bool get isUniform => true;
    
      @override
      void paint(
        Canvas canvas,
        Rect rect, {
        TextDirection? textDirection,
        BoxShape shape = BoxShape.rectangle,
        BorderRadius? borderRadius,
      }) {
        switch (shape) {
          case BoxShape.circle:
            assert(
              borderRadius == null,
              'A borderRadius can only be given for rectangular boxes.',
            );
            _paintCircle(canvas, rect);
            break;
          case BoxShape.rectangle:
            if (borderRadius != null) {
              _paintRRect(canvas, rect, borderRadius);
              return;
            }
            _paintRect(canvas, rect);
            break;
        }
      }
    
      void _paintRect(Canvas canvas, Rect rect) {
        canvas.drawRect(rect.deflate(width / 2), _getPaint(rect));
      }
    
      void _paintRRect(Canvas canvas, Rect rect, BorderRadius borderRadius) {
        final rrect = borderRadius.toRRect(rect).deflate(width / 2);
        canvas.drawRRect(rrect, _getPaint(rect));
      }
    
      void _paintCircle(Canvas canvas, Rect rect) {
        final paint = _getPaint(rect);
        final radius = (rect.shortestSide - width) / 2.0;
        canvas.drawCircle(rect.center, radius, paint);
      }
    
      @override
      ShapeBorder scale(double t) {
        return this;
      }
    
      Paint _getPaint(Rect rect) {
        return Paint()
          ..strokeWidth = width
          ..shader = gradient.createShader(rect)
          ..style = PaintingStyle.stroke;
      }
    }
    
  7. github-actions commented on Aug 5, 2026

    @github-actions

    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.

  8. locked as resolved and limited conversation to collaborators on Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: animationAnimation APIsa: error messageError messages from the Flutter frameworkfound in release: 3.38Found to occur in 3.38found in release: 3.40Found to occur in 3.40frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: material_uimaterial_ui package in flutter/packagesteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions