|
1 | 1 | import 'dart:async';
|
| 2 | + |
2 | 3 | import 'package:flutter/material.dart';
|
3 | 4 | import 'package:flutter/widgets.dart';
|
4 |
| -import 'package:getwidget/getwidget.dart'; |
5 |
| - |
6 |
| -class GFToast extends StatefulWidget { |
7 |
| - ///Creates [GFToast] that can be used to display quick warning or error messages. |
8 |
| - /// Toast has to be wrap inside the body like [GFFloatingWidget]. See [GFFloatingWidget] |
9 |
| - const GFToast({ |
10 |
| - Key? key, |
11 |
| - this.child, |
12 |
| - this.button, |
13 |
| - this.backgroundColor, |
14 |
| - this.text, |
15 |
| - this.width, |
16 |
| - this.type = GFToastType.basic, |
17 |
| - this.autoDismiss = true, |
18 |
| - this.alignment, |
19 |
| - this.animationDuration = const Duration(milliseconds: 300), |
20 |
| - this.duration = const Duration(milliseconds: 300), |
21 |
| - this.textStyle = const TextStyle(color: Colors.white70), |
22 |
| - this.autoDismissDuration = const Duration(milliseconds: 3000), |
23 |
| - }) : super(key: key); |
24 |
| - |
25 |
| - /// child of type [Widget]is alternative to text key. text will get priority over child |
26 |
| - final Widget? child; |
27 |
| - |
28 |
| - /// button of type [Widget],or you can use [GFButton] for easy implementation with [GFToast] |
29 |
| - final Widget? button; |
30 |
| - |
31 |
| - ///pass color of type [Color] or [GFColors] for background of [GFToast] |
32 |
| - final Color? backgroundColor; |
33 |
| - |
34 |
| - /// text of type [String] is alternative to child. text will get priority over child |
35 |
| - final String? text; |
36 |
| - |
37 |
| - /// textStyle of type [textStyle] will be applicable to text only and not for the child |
38 |
| - final TextStyle textStyle; |
39 |
| - |
40 |
| - /// width of type [double] used to control the width of the [GFToast] |
41 |
| - final double? width; |
42 |
| - |
43 |
| - ///type of [GFToastType] which takes the type ie, basic, rounded and fullWidth for the [GFToast] |
44 |
| - final GFToastType type; |
45 |
| - |
46 |
| - ///type of [bool] which takes bool values ie, true or false to automatically hide the [GFToast] message |
47 |
| - final bool autoDismiss; |
48 |
| - |
49 |
| - ///type of [Duration] which takes the duration of the fade in animation |
50 |
| - final Duration animationDuration; |
51 |
| - |
52 |
| - ///type of [Duration] which takes the duration of the animation |
53 |
| - final Duration duration; |
| 5 | +import 'package:getwidget/position/gf_toast_position.dart'; |
| 6 | + |
| 7 | +class GFToast { |
| 8 | + /// text of type [String] display on toast |
| 9 | + String? text; |
| 10 | + |
| 11 | + /// defines the duration of time toast display over screen |
| 12 | + int? toastDuration; |
| 13 | + |
| 14 | + /// defines the position of toast over the screen |
| 15 | + GFToastPosition? toastPosition; |
| 16 | + |
| 17 | + /// defines the background color of the toast |
| 18 | + Color? backgroundColor; |
| 19 | + |
| 20 | + /// defines the test style of the toast text |
| 21 | + TextStyle? textStyle; |
| 22 | + |
| 23 | + /// defines the border radius of the toast |
| 24 | + double? toastBorderRadius; |
| 25 | + |
| 26 | + /// defines the border of the toast |
| 27 | + Border? border; |
| 28 | + |
| 29 | + /// defines the trailing widget of the toast |
| 30 | + late Widget trailing; |
| 31 | + |
| 32 | + // ignore: type_annotate_public_apis, always_declare_return_types |
| 33 | + static showToast( |
| 34 | + text, |
| 35 | + BuildContext context, { |
| 36 | + toastDuration, |
| 37 | + toastPosition, |
| 38 | + backgroundColor = const Color(0xAA000000), |
| 39 | + textStyle = const TextStyle(fontSize: 15, color: Colors.white), |
| 40 | + toastBorderRadius = 20.0, |
| 41 | + border, |
| 42 | + trailing, |
| 43 | + }) { |
| 44 | + assert(text != null); |
| 45 | + ToastView.dismiss(); |
| 46 | + ToastView.createView(text, context, toastDuration, toastPosition, |
| 47 | + backgroundColor, textStyle, toastBorderRadius, border, trailing); |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +class ToastView { |
| 52 | + static final ToastView _instance = ToastView._internal(); |
| 53 | + // ignore: sort_constructors_first |
| 54 | + factory ToastView() => _instance; |
| 55 | + // ignore: sort_constructors_first |
| 56 | + ToastView._internal(); |
| 57 | + |
| 58 | + static OverlayState? overlayState; |
| 59 | + static OverlayEntry? _overlayEntry; |
| 60 | + static bool _isVisible = false; |
| 61 | + |
| 62 | + // ignore: avoid_void_async |
| 63 | + static void createView( |
| 64 | + String text, |
| 65 | + BuildContext context, |
| 66 | + int? toastDuration, |
| 67 | + GFToastPosition? toastPosition, |
| 68 | + Color backgroundColor, |
| 69 | + TextStyle textStyle, |
| 70 | + double toastBorderRadius, |
| 71 | + Border? border, |
| 72 | + // ignore: type_annotate_public_apis |
| 73 | + trailing) async { |
| 74 | + overlayState = Overlay.of(context, rootOverlay: false); |
| 75 | + |
| 76 | + final Widget toastChild = ToastCard( |
| 77 | + Container( |
| 78 | + decoration: BoxDecoration( |
| 79 | + color: backgroundColor, |
| 80 | + borderRadius: BorderRadius.circular(toastBorderRadius), |
| 81 | + border: border, |
| 82 | + ), |
| 83 | + margin: const EdgeInsets.symmetric(horizontal: 16), |
| 84 | + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), |
| 85 | + child: trailing == null |
| 86 | + ? Text(text, softWrap: true, style: textStyle) |
| 87 | + : Row( |
| 88 | + mainAxisAlignment: MainAxisAlignment.spaceBetween, |
| 89 | + children: [ |
| 90 | + Expanded( |
| 91 | + child: Text(text, style: textStyle), |
| 92 | + ), |
| 93 | + const SizedBox( |
| 94 | + width: 6, |
| 95 | + ), |
| 96 | + trailing |
| 97 | + ], |
| 98 | + ), |
| 99 | + ), |
| 100 | + Duration(seconds: toastDuration ?? 2), |
| 101 | + fadeDuration: 500); |
| 102 | + |
| 103 | + _overlayEntry = OverlayEntry( |
| 104 | + builder: (BuildContext context) => |
| 105 | + _showWidgetBasedOnPosition(toastChild, toastPosition)); |
| 106 | + |
| 107 | + _isVisible = true; |
| 108 | + overlayState!.insert(_overlayEntry!); |
| 109 | + await Future.delayed(Duration(seconds: toastDuration ?? 2)); |
| 110 | + await dismiss(); |
| 111 | + } |
54 | 112 |
|
55 |
| - ///type of [Duration] which takes the duration of the autoDismiss |
56 |
| - final Duration autoDismissDuration; |
| 113 | + static Positioned _showWidgetBasedOnPosition( |
| 114 | + Widget child, GFToastPosition? toastPosition) { |
| 115 | + switch (toastPosition) { |
| 116 | + case GFToastPosition.BOTTOM: |
| 117 | + return Positioned(bottom: 60, left: 18, right: 18, child: child); |
| 118 | + case GFToastPosition.BOTTOM_LEFT: |
| 119 | + return Positioned(bottom: 60, left: 18, child: child); |
| 120 | + case GFToastPosition.BOTTOM_RIGHT: |
| 121 | + return Positioned(bottom: 60, right: 18, child: child); |
| 122 | + case GFToastPosition.CENTER: |
| 123 | + return Positioned( |
| 124 | + top: 60, bottom: 60, left: 18, right: 18, child: child); |
| 125 | + case GFToastPosition.CENTER_LEFT: |
| 126 | + return Positioned(top: 60, bottom: 60, left: 18, child: child); |
| 127 | + case GFToastPosition.CENTER_RIGHT: |
| 128 | + return Positioned(top: 60, bottom: 60, right: 18, child: child); |
| 129 | + case GFToastPosition.TOP_LEFT: |
| 130 | + return Positioned(top: 110, left: 18, child: child); |
| 131 | + case GFToastPosition.TOP_RIGHT: |
| 132 | + return Positioned(top: 110, right: 18, child: child); |
| 133 | + default: |
| 134 | + return Positioned(top: 110, left: 18, right: 18, child: child); |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + static Future<void> dismiss() async { |
| 139 | + if (!_isVisible) { |
| 140 | + return; |
| 141 | + } |
| 142 | + _isVisible = false; |
| 143 | + _overlayEntry?.remove(); |
| 144 | + } |
| 145 | +} |
57 | 146 |
|
58 |
| - /// type of [Alignment] used to align the text inside the toast |
59 |
| - final Alignment? alignment; |
| 147 | +class ToastCard extends StatefulWidget { |
| 148 | + const ToastCard(this.child, this.duration, |
| 149 | + {Key? key, this.fadeDuration = 500}) |
| 150 | + : super(key: key); |
| 151 | + |
| 152 | + final Widget child; |
| 153 | + final Duration duration; |
| 154 | + final int fadeDuration; |
60 | 155 |
|
61 | 156 | @override
|
62 |
| - _GFToastState createState() => _GFToastState(); |
| 157 | + ToastStateFulState createState() => ToastStateFulState(); |
63 | 158 | }
|
64 | 159 |
|
65 |
| -class _GFToastState extends State<GFToast> with TickerProviderStateMixin { |
66 |
| - late AnimationController animationController, fadeAnimationController; |
67 |
| - late Animation<double> animation, fadeAnimation; |
68 |
| - Timer? timer; |
69 |
| - bool hideToast = false; |
| 160 | +class ToastStateFulState extends State<ToastCard> |
| 161 | + with SingleTickerProviderStateMixin { |
| 162 | + void showAnimation() { |
| 163 | + _animationController!.forward(); |
| 164 | + } |
| 165 | + |
| 166 | + void hideAnimation() { |
| 167 | + _animationController!.reverse(); |
| 168 | + _timer?.cancel(); |
| 169 | + } |
| 170 | + |
| 171 | + AnimationController? _animationController; |
| 172 | + late Animation _fadeAnimation; |
| 173 | + |
| 174 | + Timer? _timer; |
70 | 175 |
|
71 | 176 | @override
|
72 | 177 | void initState() {
|
73 |
| - animationController = |
74 |
| - AnimationController(duration: widget.duration, vsync: this); |
75 |
| - animation = CurvedAnimation( |
76 |
| - parent: animationController, |
77 |
| - curve: Curves.easeIn, |
| 178 | + _animationController = AnimationController( |
| 179 | + vsync: this, |
| 180 | + duration: Duration(milliseconds: widget.fadeDuration), |
78 | 181 | );
|
| 182 | + _fadeAnimation = |
| 183 | + CurvedAnimation(parent: _animationController!, curve: Curves.easeIn); |
| 184 | + super.initState(); |
79 | 185 |
|
80 |
| - if (mounted) { |
81 |
| - animationController.forward(); |
82 |
| - fadeAnimationController = |
83 |
| - AnimationController(duration: widget.animationDuration, vsync: this) |
84 |
| - ..addListener(() => setState(() {})); |
85 |
| - fadeAnimation = Tween<double>( |
86 |
| - begin: 0, |
87 |
| - end: 1, |
88 |
| - ).animate(fadeAnimationController); |
89 |
| - timer = Timer(widget.duration, () { |
90 |
| - if (mounted) { |
91 |
| - fadeAnimationController.forward(); |
92 |
| - } |
93 |
| - }); |
94 |
| - fadeAnimation = Tween<double>( |
95 |
| - begin: 1, |
96 |
| - end: 0, |
97 |
| - ).animate(fadeAnimationController); |
98 |
| - fadeAnimation.addStatusListener((AnimationStatus state) { |
99 |
| - if (fadeAnimation.isCompleted && widget.autoDismiss) { |
100 |
| - setState(() { |
101 |
| - hideToast = true; |
102 |
| - }); |
103 |
| - } |
104 |
| - }); |
105 |
| - } |
| 186 | + showAnimation(); |
| 187 | + _timer = Timer(widget.duration, hideAnimation); |
| 188 | + } |
106 | 189 |
|
107 |
| - super.initState(); |
| 190 | + @override |
| 191 | + void deactivate() { |
| 192 | + _timer?.cancel(); |
| 193 | + _animationController!.stop(); |
| 194 | + super.deactivate(); |
108 | 195 | }
|
109 | 196 |
|
110 | 197 | @override
|
111 | 198 | void dispose() {
|
112 |
| - animationController.dispose(); |
113 |
| - fadeAnimationController.dispose(); |
114 |
| - timer?.cancel(); |
| 199 | + _timer?.cancel(); |
| 200 | + _animationController?.dispose(); |
115 | 201 | super.dispose();
|
116 | 202 | }
|
117 | 203 |
|
118 | 204 | @override
|
119 |
| - Widget build(BuildContext context) => hideToast |
120 |
| - ? Container() |
121 |
| - : FadeTransition( |
122 |
| - opacity: widget.autoDismiss ? fadeAnimation : animation, |
123 |
| - child: Column( |
124 |
| - children: <Widget>[ |
125 |
| - Container( |
126 |
| - width: widget.type == GFToastType.fullWidth |
127 |
| - ? MediaQuery.of(context).size.width |
128 |
| - : widget.width ?? MediaQuery.of(context).size.width * 0.885, |
129 |
| - constraints: const BoxConstraints(minHeight: 50), |
130 |
| - margin: widget.type == GFToastType.fullWidth |
131 |
| - ? const EdgeInsets.only(left: 0, right: 0) |
132 |
| - : const EdgeInsets.only(left: 10, right: 10), |
133 |
| - padding: const EdgeInsets.all(10), |
134 |
| - decoration: BoxDecoration( |
135 |
| - borderRadius: widget.type == GFToastType.basic |
136 |
| - ? BorderRadius.circular(0) |
137 |
| - : widget.type == GFToastType.rounded |
138 |
| - ? BorderRadius.circular(10) |
139 |
| - : BorderRadius.zero, |
140 |
| - color: widget.backgroundColor ?? const Color(0xff323232), |
141 |
| - boxShadow: [ |
142 |
| - BoxShadow( |
143 |
| - color: Colors.black.withOpacity(0.40), |
144 |
| - blurRadius: 6, |
145 |
| - ) |
146 |
| - ]), |
147 |
| - child: Row( |
148 |
| - children: <Widget>[ |
149 |
| - Flexible( |
150 |
| - flex: 7, |
151 |
| - fit: FlexFit.tight, |
152 |
| - child: Align( |
153 |
| - alignment: widget.alignment ?? Alignment.topLeft, |
154 |
| - child: widget.text != null |
155 |
| - ? Text(widget.text!, style: widget.textStyle) |
156 |
| - : (widget.child ?? Container()), |
157 |
| - ), |
158 |
| - ), |
159 |
| - const SizedBox( |
160 |
| - width: 10, |
161 |
| - ), |
162 |
| - widget.button != null |
163 |
| - ? Flexible( |
164 |
| - flex: 4, |
165 |
| - fit: FlexFit.tight, |
166 |
| - child: Align( |
167 |
| - alignment: Alignment.topRight, |
168 |
| - child: widget.button, |
169 |
| - )) |
170 |
| - : Container() |
171 |
| - ], |
172 |
| - ), |
173 |
| - ), |
174 |
| - ], |
| 205 | + Widget build(BuildContext context) => FadeTransition( |
| 206 | + opacity: _fadeAnimation as Animation<double>, |
| 207 | + child: Center( |
| 208 | + child: Material( |
| 209 | + color: Colors.transparent, |
| 210 | + child: widget.child, |
175 | 211 | ),
|
176 |
| - ); |
| 212 | + ), |
| 213 | + ); |
177 | 214 | }
|
0 commit comments