Skip to content

Conversation

@goderbauer
Copy link
Member

Fixes #6574.

}

static TargetPlatform _platform(BuildContext context) => Theme.of(context).platform;

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extraneous blank line


/// Provides platform-specific feedback for a tap.
static Future<Null> forTap(BuildContext context) async {
switch(_platform(context)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: space after switch


/// Provides platform-specific feedback for a long press.
static Future<Null> forLongPress(BuildContext context) {
switch(_platform(context)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

FeedbackWrapper._();

/// Provides platform-specific feedback for a tap.
static GestureTapCallback forTap(GestureTapCallback callback, BuildContext context) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is really quite clever.

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 😄

return new Future<Null>.value();
}
}

Copy link
Contributor

@Hixie Hixie Jun 23, 2017

Choose a reason for hiding this comment

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

Can we merge these classes and have Feedback.forTap(context) and Feedback.wrapForTap(callback, context) ?

Having two classes with two static methods seems like it will lead to confusion.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea. Done.

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

/// Provides platform-specific acoustic and/or haptic feedback for certain
Copy link
Contributor

Choose a reason for hiding this comment

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

These docs need to be more elaborate.

Things to add at each place there are docs in this file (it's perfectly ok to have duplication in the docs):

  • What does this do on Android?
  • What does this do on iOS?
  • How do you handle things like onPressed: enabled ? callback : null?
  • Sample code? (with the ## Sample code pattern, see other files)

this.splashColor,
}) : super(key: key);
bool enableFeedback,
}) : enableFeedback = enableFeedback ?? true, super(key: key);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just make true the default value for the argument?

Copy link
Contributor

Choose a reason for hiding this comment

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

(and then assert that it's not null, and mention in the docs for the constructor that it must be non-null)

Copy link
Member Author

Choose a reason for hiding this comment

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

The idea was that this way I don't have to specify the default value in two places (here and below for InkWell). Changed it nonetheless as I didn't have strong feelings.

if (widget.onLongPress != null)
if (widget.enableFeedback)
Feedback.forLongPress(context);
widget.onLongPress();
Copy link
Contributor

Choose a reason for hiding this comment

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

you missed the braces here

Copy link
Contributor

Choose a reason for hiding this comment

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

please add a test that would have failed here

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh! Good catch!

I still wish ifs would always come with braces... goto fail; 😉

As to writing a test: there is actually another onLongPress != null check that guards the entire execution of this method (see line 359). So, it's actually impossible to exploit the missing braces in a test here.

Copy link
Contributor

Choose a reason for hiding this comment

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

In that case maybe we should just assert(widget.onLongPress != null) and remove the test?

Color highlightColor,
Color splashColor,
BorderRadius borderRadius,
bool enableFeedback,
Copy link
Contributor

Choose a reason for hiding this comment

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

default value

Copy link
Contributor

Choose a reason for hiding this comment

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

constructor docs update

_timer = null;
_controller.forward();
return; // Already visible.
return false; // Already visible.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extraneous space before comment

}

/// Signature for the callback that reports when the user changes the selection.
typedef void SelectionChangedCallback(TextSelection selection, bool longPress);
Copy link
Contributor

Choose a reason for hiding this comment

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

put typedefs near the top of the file, before first use if possible

selectionControls: materialTextSelectionControls,
onChanged: widget.onChanged,
onSubmitted: widget.onSubmitted,
onSelectionChanged: (TextSelection _, bool longPress) => _onSelectionChanged(context, longPress),
Copy link
Contributor

Choose a reason for hiding this comment

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

interesting, i would have expected this to belong to the selection controls delegate. This works too.

@Hixie
Copy link
Contributor

Hixie commented Jun 23, 2017

This is fantastic.

/// tapped, call [forTap]. For the Android-specific vibration when long pressing
/// an element, call [forLongPress]. Alternatively, you can also wrap your
/// [onTap] or [onLongPress] callback in [wrapForTap] or [wrapForLongPress] to
/// achive the same (see example code below).
Copy link
Contributor

Choose a reason for hiding this comment

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

achieve

/// typically don't provide haptic or acoustic feedback.
///
/// All methods in this class are usually called from within a [build] method
/// as you have to provide a [BuildContext].
Copy link
Contributor

Choose a reason for hiding this comment

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

or from a State's methods

/// To trigger platform-specific feedback before executing the actual callback:
///
/// ```dart
/// @override
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally each example would be something you can copy and paste into code and have work right away, so here we would usually include the StatefulWidget and State boilerplate, as in:
https://master-docs-flutter-io.firebaseapp.com/flutter/painting/TextSpan/recognizer.html

this.splashColor,
}) : super(key: key);
this.enableFeedback: true,
}) : assert(enableFeedback != null), super(key: key);
Copy link
Contributor

@Hixie Hixie Jun 23, 2017

Choose a reason for hiding this comment

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

the doc for the constructor should mention that containedInkWell, borderRadius, and enableFeedback must not be null, and the asserts should include the other two as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

Asserts are throwing left and right when I assert on containedInkWell and borderRadius. If the assert is really true we should do that (and the associated clean-up) in a follow-up PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Odd.

/// Whether detected gestures should provide acoustic and/or haptic feedback.
///
/// For example, on Android a tap will produce a clicking sound and a
/// long-press will produce a short vibration, when feedback is enabled.
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe point to Feedback?


void ensureTooltipVisible() {
/// Returns `false` when the tooltip was already visible.
bool ensureTooltipVisible() {
Copy link
Contributor

Choose a reason for hiding this comment

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

this doesn't say the most important part, that it shows a tooltip! :-)

/// Called when the user indicates that they are done editing the text in the field.
final ValueChanged<String> onSubmitted;

/// Called when the selection of text changed.
Copy link
Contributor

Choose a reason for hiding this comment

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

or when the cursor has moved? or not?


import 'package:flutter/services.dart';

/// Tracks how often feedback has been requested since its instantiation.
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably mention that only one of these can be used at a time.


/// Number of times the click sound was requested to play.
int get clickSoundCount => _clickSoundCount;
int _clickSoundCount = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should add a dispose() method than unhooks the mock handler?

Copy link
Contributor

Choose a reason for hiding this comment

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

or don't bother, it's only used in tests, so...

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 added it for completeness.

@Hixie
Copy link
Contributor

Hixie commented Jun 23, 2017

LGTM

@goderbauer goderbauer merged commit fe40eed into flutter:master Jun 23, 2017
@goderbauer goderbauer deleted the androidFeedback2 branch June 23, 2017 22:02
gspencergoog pushed a commit to gspencergoog/flutter that referenced this pull request Jul 1, 2017
…tter#10920)

* Provide haptic/acoustic feedback for tap & long-press on Android

* review comments

* fixed example code

* review comments

* comment fix
@goderbauer goderbauer added the a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) label Oct 17, 2017
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Touch events missing sound and haptic feedback on Android

3 participants