Skip to content

Gradient subclasses' static lerp methods drop the GradientTransform of both a and b #149534

@Zabadam

Description

@Zabadam

Steps to reproduce

  1. Construct a LinearGradient a with some arbitrary GradientTransform.
  2. Construct a LinearGradient b with some other arbitrary GradientTransform or none.
  3. Construct a third LinearGradient using the static LinearGradient.lerp() method, using any value for t.
  4. Observe that the gradient created by the lerp method never has a GradientTransform of any kind. It neither has the transform of a when t == 0.0 nor has the transform of b when t == 1.0.

Expected results

While it's not too surprising to have no true unmatrix or decomposition that can accurately interpolate all Matrix4s, it at least stands to reason that the lerped result should at minimum have either of a's or b's transforms.

TileMode is handled by returning a.tileMode if t < 0.5 and b.tileMode otherwise. This should also be the case for transforms, in lieu of true interpolation.

Actual results

The interpolated gradient has lost the original transform's of either (or both) a and b. This is also the case with RadialGradients and SweepGradients.

It can be plainly seen, in the LinearGradient.lerp static method for example, that the returned gradient object simply gets no transform argument:

return LinearGradient(
  begin: AlignmentGeometry.lerp(a.begin, b.begin, t)!,
  end: AlignmentGeometry.lerp(a.end, b.end, t)!,
  colors: interpolated.colors,
  stops: interpolated.stops,
  tileMode: t < 0.5 ? a.tileMode : b.tileMode, // TODO(ianh): interpolate tile mode
);

Code sample

Code sample
import 'dart:async';

import 'package:flutter/material.dart';

const colors = [Colors.red, Colors.blue, Colors.yellow],
    a = LinearGradient(colors: colors, transform: GradientRotation(1.57)),
    b = LinearGradient(colors: colors, transform: GradientRotation(2.36)),
    t = 0.0,
    kTickSec = 5;

/// The `lerp` gradient should be more-or-less identical to `a` when
/// `t == 0.0`; look like `b` when `t == 1.0`; and should at least have
/// the GradientTransform of either when `t` is any other value, such as
/// for TileMode:
///     transform: t < 0.5 ? a.transform : b.transform
final lerp = LinearGradient.lerp(a, b, t)!;

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  var index = 0;
  late Timer timer;

  void incrementGradient() =>
      mounted ? setState(() => index = (index == 2) ? 0 : index + 1) : null;

  @override
  void initState() {
    super.initState();
    timer = Timer.periodic(
        const Duration(seconds: kTickSec), (timer) => incrementGradient());
  }

  @override
  void dispose() {
    timer.cancel();
    super.dispose();
  }

  Gradient get gradient => switch (index) { 0 => a, 1 => b, _ => lerp };

  @override
  Widget build(BuildContext context) {
    final gradient = this.gradient;
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: DecoratedBox(
            decoration: BoxDecoration(gradient: gradient),
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  Center(
                      child: Text(
                          '$index\n${switch (index) {
                            0 => 'LinearGradient a',
                            1 => 'LinearGradient b',
                            _ =>
                              'LinearGradient.lerp(a, b, ${t.toStringAsFixed(1)})'
                          }}',
                          style: const TextStyle(fontSize: 40),
                          textAlign: TextAlign.center)),
                  Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 25),
                      child: Center(
                          child: Text(
                        '$gradient',
                        style: const TextStyle(fontSize: 20),
                      ))),
                  Center(
                      child: Text(
                    '${gradient.transform}',
                    style: const TextStyle(fontSize: 26),
                  )),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration
Gradient.lerp.Loses.GradientTransform.mp4

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.22.1, on Microsoft Windows [Version 10.0.19045.4412], locale en-US)
    • Flutter version 3.22.1 on channel stable at C:\src\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision a14f74ff3a (10 days ago), 2024-05-22 11:08:21 -0500
    • Engine revision 55eae6864b
    • Dart version 3.4.1
    • DevTools version 2.34.3

[✓] Windows Version (Installed version of Windows is version 10 or higher)

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at C:\src\android
    • Platform android-34, build-tools 34.0.0
    • ANDROID_SDK_ROOT = C:\src\android
    • Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java
    • Java version OpenJDK Runtime Environment (build 17.0.7+0-b2043.56-10550314)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • Chrome at C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

[✓] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.8.5)
    • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
    • Visual Studio Community 2022 version 17.8.34511.84
    • Windows 10 SDK version 10.0.20348.0

[✓] Android Studio (version 2023.1)
    • Android Studio at C:\Program Files\Android\Android Studio
    • 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-b2043.56-10550314)

[✓] VS Code, 64-bit edition (version 1.89.1)
    • VS Code at C:\Program Files\Microsoft VS Code
    • Flutter extension version 3.89.20240501

[✓] Connected device (3 available)
    • Windows (desktop) • windows • windows-x64    • Microsoft Windows [Version 10.0.19045.4412]
    • Chrome (web)      • chrome  • web-javascript • Google Chrome 124.0.6367.119
    • Edge (web)        • edge    • web-javascript • Microsoft Edge 124.0.2478.105

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

• No issues found!

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listfound in release: 3.22Found to occur in 3.22found in release: 3.23Found to occur in 3.23frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onr: fixedIssue is closed as already fixed in a newer versionteam-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