• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

icapps / flutter-icapps-architecture / 9346650674

03 Jun 2024 08:15AM UTC coverage: 93.711%. First build
9346650674

Pull #126

github

web-flow
Merge 431f410ce into 69b13802d
Pull Request #126: Feature/hover color

38 of 53 new or added lines in 2 files covered. (71.7%)

894 of 954 relevant lines covered (93.71%)

1.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.22
/lib/src/widget/touch_feedback/touch_manager.dart
1
import 'dart:async';
2

3
import 'package:flutter/gestures.dart';
4
import 'package:flutter/material.dart';
5
import 'package:icapps_architecture/icapps_architecture.dart';
6
import 'package:icapps_architecture/src/widget/touch_feedback/color_touch_effect.dart';
7

8
typedef TouchEffectBuilder = Widget Function(
9
    BuildContext context, TouchEffectInfo touchInfo);
10

11
class TouchManager extends StatefulWidget {
12
  final Color color;
13
  final Color tapColor;
14
  final Color? hoverColor;
15
  final Widget child;
16
  final bool animateAwait;
17
  final bool isMobile;
18
  final FutureOr<void> Function()? onTap;
19
  final BorderRadius? borderRadius;
20
  final HitTestBehavior? behavior;
21
  final MouseCursor cursor;
22
  final ValueChanged<PointerEnterEvent>? onEnter;
23
  final ValueChanged<PointerExitEvent>? onExit;
24
  final ValueChanged<PointerHoverEvent>? onHover;
25
  final List<TouchEffectBuilder> touchEffectBuilders;
26

27
  const TouchManager({
1✔
28
    required this.tapColor,
29
    required this.child,
30
    required this.animateAwait,
31
    required this.touchEffectBuilders,
32
    this.hoverColor,
33
    this.isMobile = true,
34
    this.onTap,
35
    this.borderRadius,
36
    this.behavior,
37
    this.cursor = MouseCursor.defer,
38
    this.color = Colors.transparent,
39
    this.onEnter,
40
    this.onExit,
41
    this.onHover,
42
    super.key,
43
  });
44

45
  @override
1✔
46
  State<TouchManager> createState() => _TouchManagerState();
1✔
47
}
48

49
class _TouchManagerState extends State<TouchManager>
50
    with SingleTickerProviderStateMixin {
51
  var _isTouched = false;
52
  var _isHovering = false;
53
  var _touchPosition = Offset.zero;
54
  static const durationSeconds = 10;
55

56
  /// Set by a child to ignore touch events when the child
57
  /// handles the touch event itself
58
  var ignoreTouch = false;
59

60
  AnimationController? _animationController;
61

62
  @override
1✔
63
  void initState() {
64
    super.initState();
1✔
65
    _animationController = AnimationController(
2✔
66
      vsync: this,
67
      duration: const Duration(seconds: durationSeconds),
68
    );
69
  }
70

71
  @override
1✔
72
  void dispose() {
73
    _animationController?.dispose();
2✔
74
    _animationController = null;
1✔
75
    super.dispose();
1✔
76
  }
77

78
  void _onTapDown(TapDownDetails details, BuildContext context) {
1✔
79
    if (ignoreTouch) {
1✔
80
      ignoreTouch = false;
×
81
      return;
82
    }
83
    _touchPosition = details.localPosition;
2✔
84
    _isTouched = true;
1✔
85
    _animationController!
1✔
86
      ..reset()
1✔
87
      ..forward();
1✔
88
    setState(() {});
2✔
89

90
    var ancestor = context.findAncestorStateOfType<_TouchManagerState>();
1✔
91
    do {
92
      ancestor?.ignoreTouch = true;
×
93
      if (!mounted) return;
1✔
94
      ancestor =
95
          ancestor?.context.findAncestorStateOfType<_TouchManagerState>();
×
96
    } while (ancestor != null);
97
  }
98

99
  FutureOr<void> _onTapUp(TapUpDetails details) async {
1✔
100
    if (_isTouched)
1✔
101
      widget.animateAwait ? await widget.onTap?.call() : widget.onTap?.call();
5✔
102
    if (!mounted) return;
1✔
103
    _isTouched = false;
1✔
104
    setState(() {});
2✔
105
  }
106

107
  void _onTapCancel() {
1✔
108
    if (!mounted) return;
1✔
109
    _isTouched = false;
1✔
110
    setState(() {});
2✔
111
  }
112

113
  @override
1✔
114
  Widget build(BuildContext context) {
115
    if (widget.onTap == null) return widget.child;
4✔
116
    final Widget detector = GestureDetector(
1✔
117
      behavior: widget.behavior,
2✔
118
      onTapDown: (details) => _onTapDown(details, context),
2✔
119
      onTapUp: _onTapUp,
1✔
120
      onTapCancel: _onTapCancel,
1✔
121
      child: AnimatedBuilder(
1✔
122
        animation: _animationController!,
1✔
123
        builder: (context, child) => Stack(
2✔
124
          children: [
1✔
125
            child!,
126
            ...widget.touchEffectBuilders.map(
3✔
127
              (builder) => Positioned.fill(
2✔
128
                child: IgnorePointer(
1✔
129
                  child: builder(
1✔
130
                    context,
131
                    TouchEffectInfo(
1✔
132
                      touchPosition: _touchPosition,
1✔
133
                      isTouched: _isTouched,
1✔
134
                      animationController: _animationController,
1✔
135
                      durationInSeconds: durationSeconds,
136
                      borderRadius: widget.borderRadius,
2✔
137
                      tapColor: widget.tapColor,
2✔
138
                    ),
139
                  ),
140
                ),
141
              ),
142
            ),
143
          ],
144
        ),
145
        child: ColorTouchEffect(
1✔
146
          isTouched: _isTouched,
1✔
147
          color: widget.tapColor,
2✔
148
          child: ColorTouchEffect(
1✔
149
            isTouched: _isHovering && !widget.isMobile,
1✔
150
            color: widget.hoverColor ?? widget.tapColor,
2✔
151
            child: Container(
1✔
152
              color: widget.color,
2✔
153
              child: widget.child,
2✔
154
            ),
155
          ),
156
        ),
157
      ),
158
    );
159
    if (widget.isMobile) return detector;
2✔
NEW
160
    return MouseRegion(
×
NEW
161
      cursor: widget.cursor,
×
NEW
162
      onEnter: _onEnter,
×
NEW
163
      onExit: _onExit,
×
NEW
164
      onHover: widget.onHover,
×
165
      child: detector,
166
    );
167
  }
168

NEW
169
  void _onExit(event) {
×
NEW
170
    widget.onExit?.call(event);
×
NEW
171
    if (!mounted) return;
×
NEW
172
    setState(() {
×
NEW
173
      _isHovering = false;
×
174
    });
175
  }
176

NEW
177
  void _onEnter(event) {
×
NEW
178
    widget.onEnter?.call(event);
×
NEW
179
    if (!mounted) return;
×
NEW
180
    setState(() {
×
NEW
181
      _isHovering = true;
×
182
    });
183
  }
184
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc