Skip to content

[feat] RefreshIndicator.noSpinner implementation #133507

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

Closed
wants to merge 21 commits into from

Conversation

opxdelwin
Copy link
Contributor

@opxdelwin opxdelwin commented Aug 28, 2023

New RefreshIndicator.noSpinner constructor to override and disable default spinner for custom handling.

New onModeChange function to keep a track of events performed by RefreshIndicator such as drag, armed, refresh, cancelled for custom handling by developers.
onModeChange is by default set to null for RefreshIndicator & RefreshIndicator.adaptive as UI gets updated as per.

Contributions to more (and better) tests would be equally appreciated!

Fixes #132775

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I signed the CLA.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.
Minimal Reproducible code
import 'package:flutter/material.dart';

/// Flutter code sample for [RefreshIndicator].

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: RefreshIndicatorExample(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('RefreshIndicator Sample'),
      ),
      body: RefreshIndicator.noSpinner(
        onModeChange: (mode) => print(mode ?? ''),
        onRefresh: () async {
          print("calling on refresh");
          return Future<void>.delayed(const Duration(seconds: 3));
        },
        notificationPredicate: (ScrollNotification notification) {
          return notification.depth == 1;
        },
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[
              Container(
                height: 100,
                alignment: Alignment.center,
                color: Colors.pink[100],
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text(
                      'Pull down here',
                      style: Theme.of(context).textTheme.headlineMedium,
                    ),
                    const Text("RefreshIndicator won't trigger"),
                  ],
                ),
              ),
              Container(
                color: Colors.green[100],
                child: ListView.builder(
                  shrinkWrap: true,
                  itemCount: 25,
                  itemBuilder: (BuildContext context, int index) {
                    return const ListTile(
                      title: Text('Pull down here'),
                      subtitle: Text('RefreshIndicator will trigger'),
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. labels Aug 28, 2023
@opxdelwin
Copy link
Contributor Author

Anyone with iOS devices may test this implementation as well.

@flutter-dashboard
Copy link

This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold.

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.

@opxdelwin
Copy link
Contributor Author

@danagbemava-nc @HansMuller please look into this pull.

@opxdelwin opxdelwin changed the title RefreshIndicator.noSpinner implementation [feat] RefreshIndicator.noSpinner implementation Oct 1, 2023
@bernaferrari
Copy link
Contributor

I personally would name it none instead of noSpinner, but let's see what the reviewer says

@HansMuller
Copy link
Contributor

Making the refresh indicator more flexible, so that you can change the animated "indicator" to something else seems reasonable. I'd expected to see a widget valued callback, something like buildRefreshIndicator(BuildContext, Animation), which would enable one to replace the default animation. The notification approach you've taken seems to work nicely for a situation where the indicator isn't stacked on top of the scrollable the way the default one is, but is just some unrelated part of the UI. Can you find a simple way to satisfy both use cases?

@opxdelwin
Copy link
Contributor Author

All checks are green! 🟢

@bernaferrari pointed out that none might be a better option, so I'll be tweaking that toward the end.

Regarding @HansMuller's suggestion, yup, we can offer an alternative to the spinner. Thinking of taking in a custom animation (maybe an icon?). I'm considering adding a separate constructor, maybe RefreshIndicator.custom().

Let me know your thoughts on how to move forward. Implementation might take a bit as I'm juggling multiple projects. Thanks for understanding!

Copy link
Contributor

@HansMuller HansMuller left a comment

Choose a reason for hiding this comment

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

Sorry about the long review delay; I plead the holidays :-)

This is looking good. With a few small tweaks and an API doc (DartPad) example I think we could land this soon.

@github-actions github-actions bot added d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos labels Feb 29, 2024
@opxdelwin
Copy link
Contributor Author

Hey, sorry for the long disappearance, Had a lot of assignments and tests.

I've added an example flutter app, still unsure on how to update https://api.flutter.dev/flutter/material/RefreshIndicator-class.html. Please do guide me.

Copy link
Contributor

@HansMuller HansMuller left a comment

Choose a reason for hiding this comment

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

Just some comments about the example (which is headed in the right direction).

body: RefreshIndicator.noSpinner(
// Callback function used by the app to listen to the
// status of the RefreshIndicator pull-down action.
onStatusChange: (RefreshIndicatorStatus? status) {
Copy link
Contributor

Choose a reason for hiding this comment

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

For the onStatusChange and onRefresh cases it would be best to do something that affects the UI rather than printing messages to the console. For the onRefresh case you could temporarily change the UI for 3 seconds.

Container(
color: Colors.green[100],
child: ListView.builder(
shrinkWrap: true,
Copy link
Contributor

@HansMuller HansMuller Feb 29, 2024

Choose a reason for hiding this comment

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

We don't want to encourage developers to use shrinkWrap. This can be rewritten with CustomScrollView:

CustomScrollView(
  slivers: <Widget>[
    SliverToBoxAdapter(
      child: Container(
        height: 100,
        alignment: Alignment.center,
        color: Colors.pink[100],
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Pull down here',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const Text("RefreshIndicator won't trigger"),
          ],
        ),
      ),
    ),
    DecoratedSliver(
      decoration: BoxDecoration(color: Colors.green[100]),
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
          (BuildContext context, int index) {
            return const ListTile(
              title: Text('Pull down here'),
              subtitle: Text('RefreshIndicator will trigger'),
            );
          },
          childCount: 25,
        ),
      ),
    ),
  ],
)

return Future<void>.delayed(const Duration(seconds: 3));
},

// This check is used to customize listening to scroll notifications
Copy link
Contributor

Choose a reason for hiding this comment

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

This check?

Better to explain this a little more directly: To ensure that we're notified only when the nested scroll view is scrolled we check the notification's depth. [Now explain what the default is and why we don't want that, etc.]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I copied part of the sample from another refresh indicator example, maybe I'll just remove this part?

/// Creates [RefreshIndicator] with no spinner and calls `onRefresh` when
/// successfully armed by drag event. Events can be optionally listened
/// by using `onStatusChange` callback
const RefreshIndicator.noSpinner({
Copy link
Contributor

Choose a reason for hiding this comment

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

You'll need a comment here that links to the example. Something like this (from TextButton):

/// {@tool dartpad}
/// This sample shows various ways to configure TextButtons, from the
/// simplest default appearance to versions that don't resemble
/// Material Design at all.
///
/// ** See code in examples/api/lib/material/text_button/text_button.0.dart **
/// {@end-tool}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this for the doc generation part?

@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';

/// Flutter code sample for [RefreshIndicator.noSpinner].
Copy link
Contributor

Choose a reason for hiding this comment

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

You'll also need a test for the example. Put it in examples/api/test/material/refresh_indicator/refresh_indicator.2_test.dart

The test just needs to exercise the example enough to know that the basics are working.

@Piinks
Copy link
Contributor

Piinks commented May 1, 2024

Hi @opxdelwin would you like to continue working on this PR?

@Piinks
Copy link
Contributor

Piinks commented May 29, 2024

Thank you for your contribution. I'm going to close this PR for now since there are outstanding comments, just to get this off our PR review queue. Please don't hesitate to re-open or submit a new PR if you have the time to address the review comments. Thanks!

@Piinks Piinks closed this May 29, 2024
auto-submit bot pushed a commit that referenced this pull request Aug 23, 2024
This PR adds a new constructor to the RefreshIndicator's class, which is `noSpinner`.
The purpose of this new constructor is to create a RefreshIndicator that doesn't show a spinner when the user arms it by pulling.

The work is based on a partial that is here: #133507

I addressed the following issues reported in the PR above:
- in the example for `noSpinner`, arming the RefreshIndicator now shows a CircularProgressIndicator, instead of just printing text to the console;
- added a test for the new example;
- added a doc comment on the new constructor;

Fixes #132775
Buchimi pushed a commit to Buchimi/flutter that referenced this pull request Sep 2, 2024
This PR adds a new constructor to the RefreshIndicator's class, which is `noSpinner`.
The purpose of this new constructor is to create a RefreshIndicator that doesn't show a spinner when the user arms it by pulling.

The work is based on a partial that is here: flutter#133507

I addressed the following issues reported in the PR above:
- in the example for `noSpinner`, arming the RefreshIndicator now shows a CircularProgressIndicator, instead of just printing text to the console;
- added a test for the new example;
- added a doc comment on the new constructor;

Fixes flutter#132775
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos 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.

Allow disabling the overlay spinner for RefreshIndicator
5 participants