[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SplashFactory in the widgets library #149963

Closed
wants to merge 4 commits into from

Conversation

nate-thegrate
Copy link
Member
@nate-thegrate nate-thegrate commented Jun 8, 2024

πŸ“œ Link to Design Doc


This pull request transforms ink effects to be completely design system-agnostic.

Original names "Splash" names
InkFeature β€”
InteractiveInkFeature Splash
InteractiveInkFeatureFactory SplashFactory
β€” SplashBox
MaterialInkController SplashController
debugCheckHasMaterial debugCheckSplash

Merging this PR will resolve #147392 and make progress toward #48017.

Handling breakages:

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. labels Jun 8, 2024
@nate-thegrate nate-thegrate marked this pull request as ready for review June 8, 2024 21:53
Piinks

This comment was marked as resolved.

@nate-thegrate

This comment was marked as resolved.

@nate-thegrate

This comment was marked as resolved.

@nate-thegrate nate-thegrate marked this pull request as draft June 17, 2024 05:30
@github-actions github-actions bot added a: text input Entering text in a text field or keyboard related problems f: cupertino flutter/packages/flutter/cupertino repository d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos f: gestures flutter/packages/flutter/gestures repository. labels Jun 18, 2024
@nate-thegrate nate-thegrate marked this pull request as ready for review June 18, 2024 05:21
@nate-thegrate nate-thegrate changed the title Add BlankMaterial widget Ink effects in widgets library Jun 18, 2024
@nate-thegrate

This comment was marked as resolved.

@Piinks

This comment was marked as resolved.

@nate-thegrate

This comment was marked as resolved.

Copy link
Contributor
@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nate-thegrate Have you seen the breaking changes policy? We'll have to follow it if we include these changes that break the Google tests and/or the customer tests. Not saying we shouldn't make these changes though, it could totally be worth it. https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#handling-breaking-changes

The test failure is caused by Material being changed from a stateful to a stateless widget.

It looks like it would be very easy to migrate the packages repo to fix these breakages, something like this?

-       final StatefulElement srcMaterialElement = tester.firstElement(
+       final StatelessElement srcMaterialElement = tester.firstElement(
        find.ancestor(
          of: find.text('Closed'),
          matching: find.byType(Material),
        ),
      );

https://github.com/flutter/packages/blob/937038cd033d2c468f871a338ec893343a09ca04/packages/animations/test/open_container_test.dart#L40-L45

The other breaking change I see is the removal of debugCheckHasMaterial, which I commented on.

Also @nate-thegrate have you considered using deprecation to mitigate these breaking changes? For example, if we do decide to remove/replace debugCheckHasMaterial, the typical way would be to deprecate it and remove it later.

We could also do something similar for the Stateful=>Stateless breaking change, but it might not be worth it for that one.

Edit: By the way I forgot to say, thank you for the PR! I appreciate you taking a look at the hard problems like this, this is the kind of introspection that Flutter needs to stay great.

packages/flutter/lib/src/material/debug.dart Show resolved Hide resolved
@yakagami
Copy link
Contributor

Is the discussion about using WidgetState in the Widgets library relevant here?

From #142151:

As far as I'm aware, there is no plan to use WidgetState in a major way across the base Widgets. If there is a concern about increasing complexity by using WidgetState in them, we can either just be careful about where we use them, or we can try and find a way to make them not required to use.

There is currently no plan to use WidgetState anywhere in the base widgets, especially ones that already exist. Ideally, we'd come up with a better way to design the API for WidgetState before that happens.

These are in response to this point:

The MaterialStateProperty API is rather awkward to use from a developer's perspective. I think it's fine for Material (and probably Cupertino) but I wouldn't want us to start using this at the widgets level (e.g. in TextField), because that would force other UI libraries built on Flutter to use it whenever they wanted to interact with the widgets layer, and different UI libraries have different ideas of what that should look like (e.g. have different groups of state properties, different mechanisms for animating them, etc). This would complicate any package that's trying to provide an alternative API to the material API

@nate-thegrate
Copy link
Member Author
nate-thegrate commented Jun 19, 2024

@yakagami thanks for bringing this up!

The InkGestureDetector class has a couple of spots for WidgetStateProperty objects, but they're optional:

  • The mouseCursor can be set using either a MouseCursor or a WidgetStateMouseCursor object.
  • The ink colors can be set either with a WidgetStateProperty<Color> overlayColor, or just by passing Colors to focusColor, hoverColor, and splashColor.

I agree with the concern you mentionedβ€”as it is currently, the WidgetStateProperty API feels a bit too clunky. It has a learning curve, and even when you're used to it, it's not super pleasant to look at:

InkGestureDetector(
  overlayColor: WidgetStateProperty.resolveWith((states) {
    if (states.contains(WidgetState.focused) || states.contains(WidgetState.hovered)) {
      return Colors.blue;
    }
    if (states.contains(WidgetState.pressed)) {
      return Colors.black;
    }
    return null;
  }),
)

I have an open pull request that introduces an easier syntax: Edit: it's merged!

InkGestureDetector(
  overlayColor: WidgetStateProperty<Color?>.fromMap({
    WidgetState.focused | WidgetState.hovered: Colors.blue,
    WidgetState.pressed: Colors.black,
  }),
)

@nate-thegrate

This comment was marked as outdated.

@MitchellGoodwin

This comment was marked as resolved.

@nate-thegrate
Copy link
Member Author
nate-thegrate commented Aug 8, 2024

Quick status update…

  • I pushed 1 more commit here (more details in a comment above).
  • The design doc now links to a demo PR that shows a straightforward way to improve the efficiency of Scaffold and other widgets.
  • I made another draft PR that shows how we might create a design-agnostic InkResponse.
  • I recently noticed another breakage and opened yet another PR to fix it.
    (idk why an aforementioned test failure seems to have disappeared, but I'm not complaining!)

Let me know what you think! :)

auto-submit bot pushed a commit to flutter/packages that referenced this pull request Aug 13, 2024
@flutter-dashboard

This comment was marked as outdated.

@nate-thegrate
Copy link
Member Author

Earlier this week, I had hopes of splitting this big PR into a couple of smaller PRs, by holding off on migrating to the widgets library.

Based on master...nate-thegrate:flutter:SplashFactory-no-migration, unfortunately it'd be splitting 1 big PR into 2 big PRs :(
(still over 900 lines added)

'This feature was deprecated after v3.24.0-0.2.pre.',
)
/// An interface for creating ink effects on a [Material].
typedef MaterialInkController = SplashController;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than deprecating and typedef-ing this, can it implement the interface and add the addInkFeature? That way it would not break anyone, and we would not need to add that method as a deprecation in the new class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally am not worried about breaking anyone here, since data-driven fixes are easy and since interacting with a MaterialInkController directly is highly unusual.

(As far as implementing the interface, see comment below)

packages/flutter/lib/src/widgets/splash.dart Outdated Show resolved Hide resolved
Comment on lines 386 to 391
@Deprecated(
'Use addSplash instead. '
'"Splash effects" no longer rely on a MaterialInkController. '
'This feature was deprecated after v3.24.0-0.2.pre.',
)
void addInkFeature(Splash feature);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a new class, why add something just for it to be deprecated? Let's see if we can make MaterialInkController implement this class instead of a typedef, so this method does not need to be here. It will be less breaking too I think.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, MaterialInkController was the interface, not the implementation.
Re-implementing the interface within the Material library would involve a lot of duplicated logic, but I do agree that a newly-added deprecated method is very off-putting.


The style guide mentions using extension methods for cases like this one:

Avoid using extension.

Prefer adding methods directly to relevant classes. If that is not possible, create a method that clearly identifies what object(s) it works with and is part of.

(A rare exception can be made for extensions that provide temporary workarounds when deprecating features. In those cases, however, the extensions and all their members must be deprecated in the PR that adds them, and they must be removed in accordance with our deprecation policy.)

The downside of "extension methods aren't easy to discover" isn't much of a downside when the method is deprecated and we'd rather not have people discovering it anyway :)

Comment on lines +288 to +292
ErrorHint(
'A SplashController can be provided by an ancestor SplashBox; alternatively, '
'there are several viable options from the Material libary, including '
'Material, Card, Dialog, Drawer, and Scaffold.',
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone is not using the material library, I am not sure if this hint is very helpful.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, though my guess would be that the majority of the time, the person reading this message is someone using the material library.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then why are we moving this to the widgets library? :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once this migration takes place, I imagine there will be Cupertino widgets that build a SplashBox; once that happens, I think it'd be good to add them to this message.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we further alter the error message to account for every widget and library possible? The point I was trying to make was that error messages should be as specific as they can be. Listing widgets that may or may not have to do with the error is not helpful to the user.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on this being a little too specific. You can say something like, "Many existing widgets in Flutter already contain a SplashBox. Alternatively, you can add one yourself."

We maybe should have never had a list of widgets in the original error hint for the material debug check. If something like that changes in the future, I don't trust myself to remember to check the error hints to be correct.

required Offset position,
required Color color,
required TextDirection textDirection,
bool containedInkWell = false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also looks like leakage from Material

Copy link
Member Author
@nate-thegrate nate-thegrate Oct 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

…well shoot, it looks like deprecating & replacing an abstract method's parameter isn't possible. πŸ˜“


I believe the solution is to use a union type here. An abstract parent type would probably be ideal for facilitating the migrationβ€”it's not quite as fun as a sealed class, but not being restricted to a single library means we won't have to bring a bunch of material stuff into the widgets layer!

This would also let us get rid of the useless constructor, so we'll have a clean class with 1 method :)

abstract interface class SplashFactory {
  @factory
  Splash create( ... );
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching thisβ€”it very well could have gone unnoticed.

Comment on lines +497 to +459
child: _InkFeatures(
key: _inkFeatureRenderer,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should rename these, ink is very specifically material.

Copy link
Member Author
@nate-thegrate nate-thegrate Oct 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unfortunate that you weren't able to make it to the dash forum where we talked about this process, but I've unresolved an above thread that has the current plan:

  1. Get a LGTM
  2. Rename _RenderInkFeatures and migrate a whole bunch of tests
  3. Merge

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a private class being added anew here in the widget layer. We should be able to call it what we intend to call it here. I found no references to _InkFeatures in any tests, which tests are affected by that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies, it was _RenderInkFeatures that was causing the issues. Will edit previous comment.

Comment on lines +536 to +500
class _RenderInkFeatures extends RenderProxyBox implements SplashController {
_RenderInkFeatures({this.color, required this.vsync}) : super(null);

/// Enables [InkFeature] animations.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here re: ink references

'Splash effects are no longer exclusive to Material design. '
'This feature was deprecated after v3.24.0-0.2.pre.',
)
List<Splash> get debugInkFeatures => splashes;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should also be addressed once tests are updated.

packages/flutter/lib/src/widgets/splash.dart Outdated Show resolved Hide resolved
packages/flutter/lib/src/widgets/splash.dart Outdated Show resolved Hide resolved
@nate-thegrate nate-thegrate force-pushed the blank-material branch 2 times, most recently from 1a9e3d5 to dde079e Compare October 3, 2024 02:27
@nate-thegrate
Copy link
Member Author

@Piinks and @MitchellGoodwin thank you both very much for the feedback; it feels great to be making progress on this PR after it had been on hold for a while.

I'm getting ready to embark on a weekend camping trip; once I'm back I will make updates based on advice given above!

@nate-thegrate nate-thegrate marked this pull request as draft October 4, 2024 18:11
@flutter-dashboard
Copy link

This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@nate-thegrate
Copy link
Member Author

Based on the advice given recently, I think maybe the best option is to close this PR and try a different approach:


Rather than planning to dump a bunch of test changes here, we could migrate the tests first in their own PR, and then use this pull request as a template for a much cleaner transition :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: text input Entering text in a text field or keyboard related problems c: tech-debt Technical debt, code quality, testing, etc. d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos design doc Tracks a design discussion document f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Proposal: a simple MaterialInkController widget
6 participants