Flutter Tooltip on One Tap / Hold Down - flutter

My app has several textfields and I want to have a tooltip so that users know can see the definition of each field.
I came across this answer, but it wasn't helpful: Flutter Tooltip on One Tap. Therefore I decided to try and fix it myself.

Here is how to do it:
First add GestureDetector as child for Tooltip,
TooltipTriggerMode.manual for triggerMode.
add onTapDown, onTapUp, and onTapCancel as follows
Widget build(BuildContext context) {
final tooltipkey = GlobalKey<TooltipState>();
return Tooltip(
key: tooltipkey,
message: message,
triggerMode: TooltipTriggerMode.manual, // make it manual
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (_) => _onTapDown(tooltipkey), // add this
onTapUp: (_) => _onTapUpAndCancel(tooltipkey), // add this
onTapCancel: () => _onTapUpAndCancel(tooltipkey), // add this
child: Icon(EvaIcons.questionMarkCircleOutline),
),
);
}
and the helper functions shown inside the code above:
void _onTapDown(GlobalKey<TooltipState> tooltipkey) {
tooltipkey.currentState?.ensureTooltipVisible();
}
void _onTapUpAndCancel(GlobalKey<TooltipState> tooltipkey) {
tooltipkey.currentState?.deactivate();
}
Hooray, it works. Now you can hold down the icon to display the tooltip immediately instead of holding it down for a while (the default configuration of tooltip).

Related

Flutter: Programatically perform a button click

How can you programatically perform a button click in Flutter? I basically want to bind a number of buttons to keys on the keyboard, the most important part is that after a key is pressed the corresponding button the visual state of the button is triggered to inform the user which button was pressed.
I am aware you can invoke the onTap function, but that by itself doesn't update the visual state of button. Is there a way to simulate this kind of behavior, I tried to look into MaterialStateController but doesn't feel like it's the right approach.
I was going to say that you could use the MaterialStateController to simulate that the button has been pressed.
This can be done by updating the state controller in some good enough fashion of your choice. Something like this perhaps:
Non-optimized code for this (you will bind to keys on the keyboard instead):
ElevatedButton(
onPressed: () async {
button2StatesController.update(MaterialState.pressed, true);
await Future.delayed(const Duration(milliseconds: 200));
button2StatesController.update(MaterialState.pressed, false);
},
child: const Text('Button 1'),
),
ElevatedButton(
onPressed: () {},
statesController: button2StatesController,
child: const Text('Button 2'),
)
You cannot simulate the InkWell effect (as far as I know) without more intervention. Like creating your own button with your own inkwell animations etc...
I discovered that _InkWellResponseState (internal-) classes have a function called simulateTap. The function is bound against ActivateIntent and by default this intend is fired whenever the user presses Enter or Space
class SimActivateIntent extends ActivateIntent {
const SimActivateIntent (this.model);
final FocusNode model;
}
class SimActivateAction extends Action<SimActivateIntent> {
SimActivateAction ();
#override
void invoke(covariant SimActivateIntentintent) {
Actions.maybeInvoke(intent.model.context!, const ActivateIntent());
}
}
With the intends listed above one can invoke the ActivateIntent on a specific object and simulate a button press. They can bound into the application using Shortcuts & Actions classes.
Map<ShortcutActivator, Intent> get defaultShortcuts {
return <ShortcutActivator, Intent>{
const SingleActivator(LogicalKeyboardKey.backspace):
SimActivateIntent(digitBackspace),
}
}
#override
Widget build(BuildContext context)
{
return Shortcuts(
shortcuts: defaultShortcuts,
child: Actions(
actions: <Type, Action<Intent>>{
SimActivateIntent: SimActivateAction(),
},
child: ...
));
}

Flutter pop best practice

I have the following flow Screen 1 -> Screen 2 -> Dialog (in a separate widget).
Screen 2 displays a dialog (Close? Yes or No). If someone presses Yes, I would like to return to the Screen 1, if they press No, just close the dialog and return to Screen 2. What I currently do is when Yes is tapped, I do Navigator.pop(context) twice. Is this a good practice? Is there a way to pass the context of Screen 2 to my dialog widget so I can pop that one directly?
Personally, I think it would be better to pass the response from the dialog back to the page, and let the page handle the rest.
You can do this:
//I'm using a raised button just to call the alert as an example...
RaisedButton(
child: Text('Press me'),
//This part here is the important part
onPressed: () async {
//You can return anything when you use Navigator.pop
//In this case I'm returning a bool indicating if the page should close or not.
//You have to await this because it depends on user input.
bool shouldPopResult = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
//The content of your dialog
actions: <Widget>[
// The value you pass here in Navigator.of(context).pop
// is the value that will be stored in shouldPopResult,
// so if "Yes" is pressed, true will return...
// and if "No", false is returned.
FlatButton(
child: Text('Yes'),
onPressed: () => Navigator.of(context).pop(true),
),
FlatButton(
child: Text('No'),
onPressed: () => Navigator.of(context).pop(false),
)
],
),
);
// This is for if the user dismisses the dialog without pressing a button
// In that case shouldPopResult would be null, so I'm setting it to false.
// You can prevent the user from dismissing the dialog
// setting barrierDismissible to false in the showDialog method.
if (shouldPopResult == null) shouldPopResult = false;
// And finally with the dialog already dismissed, you can decide
// to go back or not.
if (shouldPopResult) Navigator.of(context).pop();
});
As usual you can extract the dialog as a Widget, or extract the function that handles the dialog response altogether or anything else.
You can see the example of returning data from a page in the flutter documentation here.

Flutter PointerEvent vs OnTap, one works, the other doesn't

I have a stack widget I am trying to create in which:
1. The user touches the widget button triggering a pointerDown event.
2. Pointer down causes a slider type widget to scale from 0 to 100% from behind the button
3. With finger still down, the user drags to select a value on the scale
4. The value is selected by releasing the finger from the screen i.e. pointerUp.
The widget works fine when I use onTap instead of pointerDown in step 1. But when I try to use a pointer down event, the _open method (that manages the scaleUp of the slider) isn't triggered.
I have followed this example almost exactly: https://fireship.io/lessons/flutter-radial-menu-staggered-animations/, but have tried to change the touch event on the floatingActionbuton like this:
Transform.scale(
scale: widget.scale.value,
child: Listener(
onPointerDown: (PointerDownEvent event) {
print('pointer is down');
setState(() {
_open;
});
},
child: FloatingActionButton(child: Icon(Icons.blur_circular), onPressed: () {})),
)
The print part detects and fires the event, but the _open method does not do anything and the menu part does not appear like in the tutorial link.
I am at a loss.
Since the button below the FloatingActionButton also has a listener, The Listener widget doesn't get's the PointerDown event. So to do that you have to change the behaviour to opaque so that both get the events.
Try Something like this:
Listener(
behavior: HitTestBehavior.opaque,
onPointerDown: (PointerDownEvent details){
},
child: ...,
)

Hide circle progress inside RefreshIndicator

I am trying to use RefreshIndicator in my flutter app so i use this builtin library:
child: SafeArea(
child: RefreshIndicator(
onRefresh: () => _onRefreshData(),
child: Column(
children: <Widget>[
Expanded(...
In my page i have 2 list.when this page appear I shows a dialog and I get data from server and show these data inside of lists:
Future<void> _onRefreshData() async {
getMyChan();
}
void getMyChan() async {
Future.delayed(Duration.zero, () => _showProgressDialog());
_myChannel = await MyToolsProvider().getMe(_testToken);
getTools();
setState(() {
_closeDialog();
});
}
Now i want to use RefreshIndicator to refresh my lists but i have a question:
I just want to use swap of RefreshIndicator and don't need circle progress because as you can see i am using progressDialog in getMyChan() method so i do not need circle progress.
How can i hide circle progress inside RefreshIndicator?
Unfortunately (or not), the RefreshIndicator indicator don't have an option to hide the RefreshProgressIndicator widget present inside.
The only way is to copy the Widget in your project and replace the RefreshProgressIndicator with an empty Container here :
https://github.com/flutter/flutter/blob/f3d95cd734ad23b7f9e15e7d0bc182d40965e05f/packages/flutter/lib/src/material/refresh_indicator.dart#L459

which widget can be used to explain functionality in app

I want to explain something in my app and add a widget which looks like a notification or chat. I want this widget to be visible for some time and then get dismissed. I tried using tooltip but it is visible only when I click it.
Which widget can I use?
The Dart package intro_views_flutter is what you need, but one of its main limitations is that it is displayed on full screen, if that is not an issue to you, then you should take a look at it. Or you can use a showDialog method inside a Future function this way :
Future showNotification() async {
showDialog<String>(
context: context,
child: new AlertDialog(
title: Text('Note!') ,
contentPadding: const EdgeInsets.all(16.0),
content: //any widget you want to display here
),
);
await new Future.delayed(const Duration(seconds: 5), () {
Navigator.of(context).pop(); // this will dismiss the dialog automatically after five seconds
}
}
then when you need it call:
showNotificaion();