stargazing-dino / just_the_tooltip Goto Github PK
View Code? Open in Web Editor NEWA directional tooltip for flutter projects
Home Page: https://pub.dev/packages/just_the_tooltip
License: MIT License
A directional tooltip for flutter projects
Home Page: https://pub.dev/packages/just_the_tooltip
License: MIT License
I imagine this would be useful to see when a tooltip is on the screen and where. Top level UI might find something to do with this information.
I saw a neat answer from Remi on Notifications. This is a separate from a controller.
https://stackoverflow.com/a/51460832/8213910
We would add notifaction listener and possibly provide:
I reported this previously, but have the problem again.
try a test like:
Listview
children: [
tooltip(
child: Row(children: [Text, Expanded(Text), Button])
)
]
)
The tooltip code doesn't like that expanded row.
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: RenderFlex children have non-zero flex but incoming width constraints are unbounded.
When a row is in a parent that does not provide a finite width constraint, for example if it is in a horizontal scrollable, it will try to shrink-wrap its children along the horizontal axis. Setting a flex on a child (e.g. using Expanded) indicates that the child is to expand to fill the remaining space in the horizontal direction.
These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child cannot simultaneously expand to fit its parent.
Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose fits for the flexible children (using Flexible rather than Expanded). This will allow the flexible children to size themselves to less than the infinite remaining space they would otherwise be forced to take, and then will cause the RenderFlex to shrink-wrap the children rather than expanding to fit the maximum constraints provided by the parent.
I haven't investigated this yet. But I added a tooltip on some listview items and I'm getting this:
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: RenderFlex children have non-zero flex but incoming width constraints are unbounded.
When a row is in a parent that does not provide a finite width constraint, for example if it is in a horizontal scrollable, it will try to shrink-wrap its children along the horizontal axis. Setting a flex on a child (e.g. using Expanded) indicates that the child is to expand to fill the remaining space in the horizontal direction.
These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child cannot simultaneously expand to fit its parent.
Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose fits for the flexible children (using Flexible rather than Expanded). This will allow the flexible children to size themselves to less than the infinite remaining space they would otherwise be forced to take, and then will cause the RenderFlex to shrink-wrap the children rather than expanding to fit the maximum constraints provided by the parent.
If this message did not help you determine the problem, consider using debugDumpRenderTree():
https://flutter.dev/debugging/#rendering-layer
http://api.flutter.dev/flutter/rendering/debugDumpRenderTree.html
The affected RenderFlex is:
RenderFlex#88256 relayoutBoundary=up18(creator: Row ← Padding ← DefaultTextStyle ← Builder ← Semantics ← DefaultTextStyle ← AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#2bfc5 ink renderer] ← NotificationListener ← CustomPaint ← _ShapeBorderPaint ← PhysicalShape ← ⋯, parentData: offset=Offset(10.0, 10.0) (can use size), constraints: BoxConstraints(w=849.8, 0.0<=h<=Infinity), size: Size(849.8, 60.0), direction: horizontal, mainAxisAlignment: start, mainAxisSize: max, crossAxisAlignment: center, textDirection: ltr, verticalDirection: down)
The creator information is set to:
Row ← Padding ← DefaultTextStyle ← Builder ← Semantics ← DefaultTextStyle ← AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#2bfc5 ink renderer] ← NotificationListener ← CustomPaint ← _ShapeBorderPaint ← PhysicalShape ← ⋯
See also: https://flutter.dev/layout/
If none of the above helps enough to fix this problem, please don't hesitate to file a bug:
https://github.com/flutter/flutter/issues/new?template=2_bug.md
#0 RenderBox.debugCannotComputeDryLayout.
#1 RenderBox.debugCannotComputeDryLayout
#2 RenderFlex.computeDryLayout
#3 RenderBox._computeDryLayout
#4 RenderBox.getDryLayout.
#5 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#6 RenderBox.getDryLayout
#7 RenderPadding.computeDryLayout
#8 RenderBox._computeDryLayout
#9 RenderBox.getDryLayout.
#10 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#11 RenderBox.getDryLayout
#12 RenderProxyBoxMixin.computeDryLayout
#13 RenderBox._computeDryLayout
#14 RenderBox.getDryLayout.
#15 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#16 RenderBox.getDryLayout
#17 RenderProxyBoxMixin.computeDryLayout
#18 RenderBox._computeDryLayout
#19 RenderBox.getDryLayout.
#20 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#21 RenderBox.getDryLayout
#22 RenderProxyBoxMixin.computeDryLayout
#23 RenderBox._computeDryLayout
#24 RenderBox.getDryLayout.
#25 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#26 RenderBox.getDryLayout
#27 RenderProxyBoxMixin.computeDryLayout
#28 RenderBox._computeDryLayout
#29 RenderBox.getDryLayout.
#30 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#31 RenderBox.getDryLayout
#32 RenderPadding.computeDryLayout
#33 RenderBox._computeDryLayout
#34 RenderBox.getDryLayout.
#35 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#36 RenderBox.getDryLayout
#37 RenderProxyBoxMixin.computeDryLayout
#38 RenderBox._computeDryLayout
#39 RenderBox.getDryLayout.
#40 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#41 RenderBox.getDryLayout
#42 RenderProxyBoxMixin.computeDryLayout
#43 RenderBox._computeDryLayout
#44 RenderBox.getDryLayout.
#45 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#46 RenderBox.getDryLayout
#47 RenderProxyBoxMixin.computeDryLayout
#48 RenderBox._computeDryLayout
#49 RenderBox.getDryLayout.
#50 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:314:23)
#51 RenderBox.getDryLayout
#52 _JustTheTooltipState._getTargetInformation
#53 _JustTheTooltipState._createEntry
#54 _JustTheTooltipState._createNewEntries
#55 _JustTheTooltipState.ensureTooltipVisible
#56 _JustTheTooltipState._showTooltip.
#57 _JustTheTooltipState._showTooltip.
#58 _rootRun (dart:async/zone.dart:1420:47)
#59 _CustomZone.run (dart:async/zone.dart:1328:19)
#60 _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
#61 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1276:23)
#62 _rootRun (dart:async/zone.dart:1428:13)
#63 _CustomZone.run (dart:async/zone.dart:1328:19)
#64 _CustomZone.bindCallback. (dart:async/zone.dart:1260:23)
#65 Timer._createTimer. (dart:async-patch/timer_patch.dart:18:15)
#66 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395:19)
#67 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426:5)
#68 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:187:12)
I think this occurs when you use the default AppBar()
. This creates a back button that has a maybePop()
call when a back it triggered. The maybePop
likely sees the overlays and sends a false back.
One workaround I've found is to replace the skrim's default gestureDetector
Widget _createSkrim() {
return GestureDetector(
key: skrimKey,
behavior: HitTestBehavior.translucent,
onTap: _hideTooltip,
);
}
with a
Widget _createSkrim() {
return Listener(
key: skrimKey,
behavior: HitTestBehavior.translucent,
onPointerDown: (_) => _hideTooltip(),
);
}
But this creates the weird case where when you're dragging, the tooltip is immediately closed because an onPointerDown event was sent.
GestureDetector
https://user-images.githubusercontent.com/29642168/135305163-17499af7-85cc-4c98-8dac-2e8193eb50bf.mp4
For vertical text boxes like japanese, it'd be cool if you could center the tip along the center vertical axis.
This would involve doing some black magic on cross axis code of vertical|horizontal PositionDependentBox function. Namely, we'd likely pass through an alignment
and do it based off that. The code below favors the left margin I think
// HORIZONTAL DIRECTION
double x;
if (size.width - margin.horizontal < childSize.width) {
x = (size.width - childSize.width) / 2.0;
} else {
final normalizedTargetX =
target.dx.clamp(margin.left, size.width - margin.right);
final edge = margin.left + childSize.width / 2.0;
if (normalizedTargetX < edge) {
x = margin.left;
} else if (normalizedTargetX > size.width - edge) {
x = size.width - margin.left - childSize.width;
} else {
x = normalizedTargetX - childSize.width / 2.0;
}
}
When you move cursor over the popup tooltip after hovering over the child it should keep the popup open. The flutter tooltip class already does this
I tried passing offsets to the function but I get the triangle relative to the canvas, not to the tooltip,
Please help
Although I do think what I'm currently doing is a little bit hacky, considering I have 10+ fields on the widget state, I'd need to create those same fields on the CustomRenderObject and update them all accordingly. This is super verbose.
My current workaround is just to get the RenderBox of child's self at runtime when it's requested to show a tooltip.
TargetInformation _getTargetInformation(BuildContext context) {
final box = context.findRenderObject() as RenderBox?;
if (box == null) {
throw StateError(
'Cannot find the box for the given object with context $context',
);
}
final targetSize = box.getDryLayout(const BoxConstraints.tightForFinite());
final target = box.localToGlobal(box.size.center(Offset.zero));
final offsetToTarget = Offset(
-target.dx + box.size.width / 2,
-target.dy + box.size.height / 2,
);
return TargetInformation(
targetSize,
target,
offsetToTarget,
);
}
Hi, I have error when I try create a new justTheController
Any ideas?
StackTrace: #0 LateError._
throwFieldAlreadyInitialized (dart:_internal-patch\/internal_patch.dart:211)\n#1 _JustTheToolt
ipState._controller= (package:just_the_tooltip\/src\/just_the_tooltip.dart:150)\n#2 _JustTheTo
oltipState.didUpdateWidget (package:just_the_tooltip\/src\/just_the_tooltip.dart:222)\n#3 Stat
efulElement.update (package:flutter\/src\/widgets\/framework.dart:4855)\n#4 Element.updateChil
d (package:flutter\/src\/widgets\/framework.dart:3412)\n#5 ComponentElement.performRebuild (pa
ckage:flutter\/src\/widgets\/framework.dart:4690)\n#6 Element.rebuild (package:flutter\/src\/w
idgets\/framework.dart:4355)\n#7 StatelessElement.update (package:flutter\/src\/widgets\/frame
work.dart:4746)\n#8 Element.updateChild (package:flutter\/src\/widgets\/framework.dart:3412)\n
#9 ComponentElement.performRebuild (package:flutter\/src\/widgets\/framework.dart:4690)\n#10
StatefulElement.performRebuild (package:flutter\/src\/widgets\/framework.dart:4840)\n#11 Ele
ment.rebuild (package:flutter\/src\/widgets\/framework.dart:4355)\n#12 StatefulElement.update (
package:flutter\/src\/widgets\/framework.dart:4872)\n#13 Element.updateChild (package:flutter\/
src\/widgets\/framework.dart:3412)\n#14 Element.inflateWidget (package:flutter\/src\/widgets\/f
ramework.dart:3663)\n#15 Element.updateChild (package:flutter\/src\/widgets\/framework.dart:342
5)\n#16 ComponentElement.performRebuild (package:flutter\/src\/widgets\/framework.dart:4690)\n#
17 StatefulElement.performRebuild (package:flutter\/src\/widgets\/framework.dart:4840)\n#18
Element.rebuild (package:flutter\/src\/widgets\/framework.dart:4355)\n#19 ComponentElement._fi
rstBuild (package:flutter\/src\/widgets\/framework.dart:4643)\n#20 StatefulElement._firstBuild
(package:flutter\/src\/widgets\/framework.dart:4831)\n#21 ComponentElement.mount (package:flutt
er\/src\/widgets\/framework.dart:4638)\n#22 Element.inflateWidget (package:flutter\/src\/widget
s\/framework.dart:3673)\n#23 Element.updateChild (package:flutter\/src\/widgets\/framework.dart
:3425)\n#24 ComponentElement.performRebuild (package:flutter\/src\/widgets\/framework.dart:4690
...
#201 BuildOwner.buildScope (package:flutter\/src\/widgets\/framework.dart:2620)\n#202 Widgets
Binding.drawFrame (package:flutter\/src\/widgets\/binding.dart:882)\n#203 RendererBinding._handl
ePersistentFrameCallback (package:flutter\/src\/rendering\/binding.dart:319)\n#204 SchedulerBind
ing._invokeFrameCallback (package:flutter\/src\/scheduler\/binding.dart:1143)\n#205 SchedulerBin
ding.handleDrawFrame (package:flutter\/src\/scheduler\/binding.dart:1080)\n#206 SchedulerBinding
._handleDrawFrame (package:flutter\/src\/scheduler\/binding.dart:996)\n#207 _rootRun (dart:async
\/zone.dart:1428)\n#208 _CustomZone.run (dart:async\/zone.dart:1328)\n#209 _CustomZone.runGua
rded (dart:async\/zone.dart:1236)\n#210 _invoke (dart:ui\/hooks.dart:166)\n#211 PlatformDispa
tcher._drawFrame (dart:ui\/platform_dispatcher.dart:270)\n#212 _drawFrame (dart:ui\/hooks.dart:1
29)\n \n\n Error message: LateInitializationError: Field '_controller@1501137672' has already been
initialized
The size and layout would be changed after initial build.
Dunno
Let's add an Alignment argument that the tooltip center will use to position itself. For example, we could provide a value of Alignment.topRight
and the tail tip will point itself on the child's box at the topRight corner. We'd likely need to flip the Alignment (multiply by -1) if we end up positioning ourselves in the opposite of preferredAxis
.
I was searching through some packages on pub and saw the name and was cracking up 👍
Maybe something like a controller that gets passed to the children. That'd require a builder pattern tho... HMMM
We should be able to set how long the tooltip will stay shown
I just got really confused by thinking immediately
referred to an action the tooltip took without animation. That is not the case. immediately
in the original Tooltip
s case implies that they're not waiting for hover hide/show duration.
That's cool and all, but for a controller, we'll want to be able to specify immediately
in terms of, skip the whole animation thing. We also don't need to worry about that stuff in terms of modals as they're driven by taps
I'm getting this error while building my app:
ERROR: ../../.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.11+2/lib/src/just_the_tooltip.dart:329:27: Warning: Operand of null-aware operation '!' has type 'RendererBinding' which excludes null.
ERROR: - 'RendererBinding' is from 'package:flutter/src/rendering/binding.dart' ('/opt/flutter/packages/flutter/lib/src/rendering/binding.dart').
ERROR: RendererBinding.instance!.mouseTracker.mouseIsConnected;
ERROR: ^
ERROR: ../../.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.11+2/lib/src/just_the_tooltip.dart:393:21: Warning: Operand of null-aware operation '!' has type 'RendererBinding' which excludes null.
ERROR: - 'RendererBinding' is from 'package:flutter/src/rendering/binding.dart' ('/opt/flutter/packages/flutter/lib/src/rendering/binding.dart').
ERROR: RendererBinding.instance!.mouseTracker
ERROR: ^
ERROR: ../../.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.11+2/lib/src/just_the_tooltip.dart:397:20: Warning: Operand of null-aware operation '!' has type 'GestureBinding' which excludes null.
ERROR: - 'GestureBinding' is from 'package:flutter/src/gestures/binding.dart' ('/opt/flutter/packages/flutter/lib/src/gestures/binding.dart').
ERROR: GestureBinding.instance!.pointerRouter.addGlobalRoute(_handlePointerEvent);
ERROR: ^
ERROR: ../../.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.11+2/lib/src/just_the_tooltip.dart:402:21: Warning: Operand of null-aware operation '?.' has type 'RendererBinding' which excludes null.
ERROR: - 'RendererBinding' is from 'package:flutter/src/rendering/binding.dart' ('/opt/flutter/packages/flutter/lib/src/rendering/binding.dart').
ERROR: RendererBinding.instance?.mouseTracker
ERROR: ^
ERROR: ../../.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.11+2/lib/src/just_the_tooltip.dart:404:20: Warning: Operand of null-aware operation '?.' has type 'GestureBinding' which excludes null.
ERROR: - 'GestureBinding' is from 'package:flutter/src/gestures/binding.dart' ('/opt/flutter/packages/flutter/lib/src/gestures/binding.dart').
ERROR: GestureBinding.instance?.pointerRouter
ERROR: ^
ERROR: ../../.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.11+2/lib/src/just_the_tooltip.dart:414:25: Warning: Operand of null-aware operation '!' has type 'RendererBinding' which excludes null.
ERROR: - 'RendererBinding' is from 'package:flutter/src/rendering/binding.dart' ('/opt/flutter/packages/flutter/lib/src/rendering/binding.dart').
ERROR: RendererBinding.instance!.mouseTracker.mouseIsConnected;
ERROR: ^
ERROR: ../../.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.11+2/lib/src/just_the_tooltip.dart:498:22: Warning: Operand of null-aware operation '?.' has type 'WidgetsBinding' which excludes null.
ERROR: - 'WidgetsBinding' is from 'package:flutter/src/widgets/binding.dart' ('/opt/flutter/packages/flutter/lib/src/widgets/binding.dart').
ERROR: WidgetsBinding.instance?.addPostFrameCallback((_) async {
ERROR: ^
ERROR: ../../.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.11+2/lib/src/just_the_tooltip_entry.dart:210:20: Warning: Operand of null-aware operation '?.' has type 'WidgetsBinding' which excludes null.
ERROR: - 'WidgetsBinding' is from 'package:flutter/src/widgets/binding.dart' ('/opt/flutter/packages/flutter/lib/src/widgets/binding.dart').
ERROR: WidgetsBinding.instance?.addPostFrameCallback((_) {
ERROR: ^
Forcing the child onTap to show the tooltip is not cool. It might be the default behavior but we should be able to decide when we want to show the tooltip in the app. Think of onboarding scenarios.
It's easy to reproduce, just add a listener to the JustTheController instance. And check 'isShowing' within the closure, like what I did in the PR.
As I mentioned in the PR, JustTheController as a ValueNotifier, ideally, it should reflect the current status of tooltip, like open or close. I noticed you have implemented these functions: 'isShowing' and 'isHidden', however, it's broken, the value never changes.
I also tried to fix it directly, but the value will be changed multiple times during each user action, that means, if I listen to JustTheController, the listener will be invoked multiple times for each user action. Potentially, this listener behavior would cause some unnecessary issues. That's why I end up with creating a new status notifier. Maybe you have better implementation. Please let me know if you have any questions.
I don't know exactly what's going on, but setting these durations is near impossible to get right.
Most of the time the tooltip doesn't show up. It must be hiding the tooltip in timers that get fired at the wrong time.
Very confusing to set this up.
Does this plugin support textAlignment, specifically Center?
We should allow for more customizations. A good starting point would be to try to implement some of these:
I might want to consider whether the tooltip should be the space around the tail as well and if the tail should just be gotten from a clipper. That would make things like frosted glass a lot easier to implement. I think it'd complicate gestures though. Just a guess.
I don't know how to get a box that has a preferredSize that is larger in the vertical direction than the horizontal. This was to test that it worked in correctly constraining stuff.
However, I tried a RotatedBox and it looks like it doesn't swap the size? Not sure how but I got a width of like 68 for something that was obviously like 300. I don't know how you'd check if something was rotated either
This functionality probably belongs in dependentBox stuff but if it's not even being passed the actual information of the box it's positioning then I'm at a loss
I currently allow stuff that pass me a scroll controller to go past the immediate viewport into extentBefore and extentAfter. HOWEVER. If it's something that chooses to expand as large as possible such as a Center then we should try constraining the child size first to the viewport and then, if it fails, fallback to giving it all the possible size.
Dear diary, just saw this other package and its whack how much we have alike. Unlike their package, I use a custom render object instead of drawing a frame late. They somehow manage update the tooltip in realtime as the target moves though which is cool. I think I'll see if I can't get some pointers from them.
Right now it appears and disappears instantly and this does not look good
I am using a widget by tapping it i am showing a tooltip. but i also want to change the size, colour of the tooltip but unable to do because i am not getting any callback of show and close in which i can change the widget.It would be great if those call backs are added soon. Thank you.
════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during performLayout():
The _RenderDecoration class does not support dry layout.
Layout requires baseline metrics, which are only available after a full layout.
The relevant error-causing widget was
TooltipOverlay
../…/src/just_the_tooltip.dart:227
When the exception was thrown, this was the stack
#0 RenderBox.debugCannotComputeDryLayout.<anonymous closure>
package:flutter/…/rendering/box.dart:1901
#1 RenderBox.debugCannotComputeDryLayout
package:flutter/…/rendering/box.dart:1911
#2 _RenderDecoration.computeDryLayout
package:flutter/…/material/input_decorator.dart:1300
#3 RenderBox._computeDryLayout
package:flutter/…/rendering/box.dart:1824
#4 RenderBox.getDryLayout.<anonymous closure>
package:flutter/…/rendering/box.dart:1813
#5 _LinkedHashMapMixin.putIfAbsent (dart:collection-patch/compact_hash.dart:311:23)
#6 RenderBox.getDryLayout
package:flutter/…/rendering/box.dart:1813
#7 RenderProxyBoxMixin.computeDryLayout
package:flutter/…/rendering/proxy_box.dart:108
#8 RenderBox._computeDryLayout
package:flutter/…/rendering/box.dart:1824
#9 RenderBox.getDryLayout.<anonymous closure>
package:flutter/…/rendering/box.dart:1813
This is probably because I'm calling getDryLayout on the child and it doesn't have that available for textfields. I think there's a debug version available that knows though isn't there?
This happens with an entry.
Without a scrim to properly close things, it looks like the context is the same as the old one.
I noticed a problem in the use of this library when used with the area mode and the tooltip inside a ListView or a GridView on Flutter 2.10.1.
When the widget that contains the tooltip is activated and move to outside of ListView, its cause this error
FlutterError (setState() or markNeedsBuild() called during build.
This ValueListenableBuilder<Widget?> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
Possible to reproduce on example:
https://github.com/Nolence/just_the_tooltip/blob/main/example/lib/pages/tooltip_area_page.dart
This is semi expected behavior as the tooltip adds a global listener for taps and will close the tooltip if there's any taps. However, I don't think it should happen the second the tooltip is created.
The tap handler should should ignore all taps until hasEntry is true
It does wait until it has an entry. It still doesn't handle it correctly
void _handlePointerEvent(PointerEvent event) {
if (!delegate.hasEntry) return;
if (event is PointerUpEvent || event is PointerCancelEvent) {
_hideTooltip();
} else if (event is PointerDownEvent) {
_hideTooltip(immediately: true);
}
}
This means if we change the axis, no new constraints are processed.
This likely will be fixed with making each property a setter and getter and making the correct thing dirty. Either paint or layout.
app is working in debug mode, but its throwing several errors when i try to build the APK. Bellow i will mention the errors which i got.
`../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/just_the_tooltip.dart:80:9: Error: Type 'TooltipTriggerMode' not found.
final TooltipTriggerMode? triggerMode;
^^^^^^^^^^^^^^^^^^
../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/just_the_tooltip.dart:288:16: Error: Type 'TooltipTriggerMode' not found.
static const TooltipTriggerMode _defaultTriggerMode =
^^^^^^^^^^^^^^^^^^
../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/just_the_tooltip.dart:300:8: Error: Type 'TooltipTriggerMode' not found.
late TooltipTriggerMode triggerMode;
^^^^^^^^^^^^^^^^^^
../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/just_the_tooltip_entry.dart:60:9: Error: Type 'TooltipTriggerMode' not found.
final TooltipTriggerMode? triggerMode;
^^^^^^^^^^^^^^^^^^
../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/models/just_the_interface.dart:125:3: Error: Type 'TooltipTriggerMode' not found.
TooltipTriggerMode? get triggerMode;
^^^^^^^^^^^^^^^^^^
../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/just_the_tooltip.dart:289:7: Error: Getter not found: 'TooltipTriggerMode'.
TooltipTriggerMode.longPress;
^^^^^^^^^^^^^^^^^^
../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/just_the_tooltip.dart:80:9: Error: 'TooltipTriggerMode' isn't a type.
final TooltipTriggerMode? triggerMode;
^^^^^^^^^^^^^^^^^^
../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/just_the_tooltip.dart:288:16: Error: 'TooltipTriggerMode' isn't a type.
static const TooltipTriggerMode _defaultTriggerMode =
^^^^^^^^^^^^^^^^^^
../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/just_the_tooltip.dart:300:8: Error: 'TooltipTriggerMode' isn't a type.
late TooltipTriggerMode triggerMode;
^^^^^^^^^^^^^^^^^^
../../src/flutter_2.2.3/.pub-cache/hosted/pub.dartlang.org/just_the_tooltip-0.0.10/lib/src/just_the_tooltip.dart:559:26: Error: The getter 'TooltipTriggerMode' isn't defined for the class '_JustTheTooltipState'.
FAILURE: Build failed with an exception.
Where:
Script '/home/mindstack/src/flutter_2.2.3/packages/flutter_tools/gradle/flutter.gradle' line: 1035
What went wrong:
Execution failed for task ':app:compileFlutterBuildRelease'.
Process 'command '/home/mindstack/src/flutter_2.2.3/bin/flutter'' finished with non-zero exit value 1
Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
Get more help at https://help.gradle.org
`
I'm currently doing the most naive golden tests. I couldn't find out a way to use to use a screenbuilder with various screens without breaking the tooltip. It wouldn't break but because internally the golden test lib is just resizing things instead of relaying them out as well, the tooltip was being pushed to the edge.
I'd like scenerios like the following example from flutter_glove_box:
final builder = DeviceBuilder()
..overrideDevicesForAllScenarios(devices: [
Device.phone,
Device.iphone11,
Device.tabletPortrait,
Device.tabletLandscape,
])
..addScenario(
widget: FlutterDemoPage(),
name: 'default page',
)
..addScenario(
widget: FlutterDemoPage(),
name: 'tap once',
onCreate: (scenarioWidgetKey) async {
final finder = find.descendant(
of: find.byKey(scenarioWidgetKey),
matching: find.byIcon(Icons.add),
);
expect(finder, findsOneWidget);
await tester.tap(finder);
},
)
..addScenario(
widget: FlutterDemoPage(),
name: 'tap five times',
onCreate: (scenarioWidgetKey) async {
final finder = find.descendant(
of: find.byKey(scenarioWidgetKey),
matching: find.byIcon(Icons.add),
);
expect(finder, findsOneWidget);
await tester.tap(finder);
await tester.tap(finder);
await tester.tap(finder);
await tester.tap(finder);
await tester.tap(finder);
},
);
but where I can choose to do something in between different devices. Like a hook before the screenshot is taken.
A custom user built barrier shouldn't visually obscure the child widget. This doesn't really match with what a user would expect and naturally bars a lot of users from using the tooltip as a sort of coach or app guide such as the following:
A couple of possibilities to get what we want would be to:
or something else
coachmaker does something similar so it might help to get inspiration from them.
Those images are quite large and to force every user to install them with a standard install is wack
I don't think this should overflow as I've passed in a scroll Controller and there is technically space both before and after. If anything, this looks weird in where it decides to stop drawing. Are those the constaints?
class TooltipAreaExamplePage extends StatefulWidget {
const TooltipAreaExamplePage({Key? key}) : super(key: key);
@override
State<TooltipAreaExamplePage> createState() => _TooltipAreaExamplePageState();
}
class _TooltipAreaExamplePageState extends State<TooltipAreaExamplePage> {
final titleController = TextEditingController(
text: 'Lorem ipsum dolor',
);
final descriptionController = TextEditingController(
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do '
'eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim '
'ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut ',
);
final scrollController = ScrollController();
@override
void dispose() {
titleController.dispose();
descriptionController.dispose();
scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('It goes under me')),
body: JustTheTooltipArea(
builder: (context, tooltip, scrim) {
return Stack(
fit: StackFit.passthrough,
children: [
ListView(
controller: scrollController,
children: List.generate(
40,
(index) {
if (index == 15) {
return JustTheTooltipEntry(
scrollController: scrollController,
isModal: true,
tailLength: 20.0,
preferredDirection: AxisDirection.right,
margin: const EdgeInsets.all(16.0),
child: const Material(
color: Colors.blue,
shape: CircleBorder(),
elevation: 4.0,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.touch_app,
color: Colors.white,
),
),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
maxLines: null,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
style: Theme.of(context).textTheme.headline6,
controller: titleController,
),
const SizedBox(height: 12.0),
TextField(
controller: descriptionController,
maxLines: null,
keyboardType: TextInputType.multiline,
textCapitalization: TextCapitalization.sentences,
style: Theme.of(context).textTheme.subtitle1,
),
const SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: const StadiumBorder(),
),
onPressed: () {},
child: const Text('exercises'),
),
),
const SizedBox(width: 16.0),
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: const StadiumBorder(),
),
onPressed: () {},
child: const Text('course'),
),
),
],
)
],
),
);
}
return ListTile(title: Text('Item $index'));
},
),
),
if (scrim != null) scrim,
if (tooltip != null) tooltip,
],
);
},
),
);
}
}
I think I previously was using it to avoid passing through super parameters but with new super
language feature in dart it's relatively easy. It would also just be nice because this is very non-standard the way I do it.
Is this how it was intended, that the tooltip closes when you click on it?
Can this be disabled somehow?
I think such property values (that I have set) should prevent this.
triggerMode: TooltipTriggerMode.manual,
barrierDismissible: false,
But it doesn't work that way.
Did as its shown on an example, but still always getting errors about try layout support..
When I hover the button this tooltip need to show how to do this
...Not allowing the tooltip to show for any new item on the new route (as the previous one wasn't closed 'properly'. It also doesn't allow it to close on the new page as it technically doesn't exist on that page? Lets look at this example:
import 'package:flutter/material.dart';
import 'package:just_the_tooltip/just_the_tooltip.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Error App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Error'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center: (child: GestureDetector(
onDoubleTap: () {
Navigator.push<void>(
context,
MaterialPageRoute(builder: (context) {
FocusScope.of(context).unfocus();
return SecondPage();
}),
);
},
child: ToolTipBug(
description: 'Hello',
),
),
),
);
}
}
class ToolTipBug extends StatelessWidget {
final String description;
const ToolTipBug({
Key? key,
required this.description,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return JustTheTooltip(
isModal: true,
triggerMode: TooltipTriggerMode.tap,
content: Text(description),
child: SizedBox(
height: 100,
child: Center(child: Text('Test')),
),
);
}
}
class SecondPage extends StatelessWidget {
const SecondPage({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page 2'),
),
body: Center: (
child: ToolTipBug(
description: 'hello 2',
),
),
);
}
}
Lets consider this simple example. When you open the tooltip and then double click on the MyHomePage Class, and you go to the SecondPage, you expect the modal of the previous page to be closed and that you will be able to open the second ToolTipBug Widget on SecondPage by cliking on it but this doesn't happen. The previous modal in the MyHomePage Class persists (but is not there) and nothing opens in the new page. This behaviour only happens when isModal is set to true.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.