Skip to content

Commit 7acb249

Browse files
committed
Add envelop indicator
1 parent be00d4e commit 7acb249

File tree

9 files changed

+222
-10
lines changed

9 files changed

+222
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
- Use the `flutter_lints` package for analysis.
1616
- Deprecate `leadingGlowVisible` and `trailingGlowVisible` in favor of `leadingScrollIndicatorVisible` and `trailingScrollIndicatorVisible` arguments.
1717
- Added `reversed` argument that allows you to trigger a refresh indicator from the end of the list.
18+
- Added `envelope` example.
19+
- Added `pull to fetch more` example.
1820

1921
## 1.0.0
2022

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,16 @@ CustomRefreshIndicator(
5656
Almost all of these examples are available in the example application.
5757

5858
| Plane indicator [[SOURCE](example/lib/indicators/plane_indicator.dart)][[DEMO](https://custom-refresh-indicator.klyta.it/#/plane)] | Ice cream [[SOURCE](example/lib/indicators/ice_cream_indicator.dart)][[DEMO](https://custom-refresh-indicator.klyta.it/#/ice-cream)] | Warp [[SOURCE](example/lib/indicators/warp_indicator.dart)][[DEMO](https://custom-refresh-indicator.klyta.it/#/warp)] |
59-
| :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: |
60-
| ![plane_indicator](readme/plane_indicator.gif) | ![ice_cream_indicator](readme/ice_cream_indicator.gif) | ![warp_indicator](readme/warp_indicator.gif) |
59+
| :--------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------: |
60+
| ![plane_indicator](readme/plane_indicator.gif) | ![ice_cream_indicator](readme/ice_cream_indicator.gif) | ![warp_indicator](readme/warp_indicator.gif) |
6161

62-
| With complete state [[SOURCE](example/lib/indicators/check_mark_indicator.dart)][[DEMO](https://custom-refresh-indicator.klyta.it/#/check-mark)] | Pull to fetch more [[SOURCE](example/lib/indicators/swipe_action.dart)][[DEMO](https://custom-refresh-indicator.klyta.it/#/fetch-more)] | Envelope |
63-
| :---------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------: |
64-
| ![indicator_with_complete_state](readme/indicator_with_complete_state.gif) | ![fetch_more](readme/fetch_more.gif) | ![letter_indicator](readme/letter_indicator.gif) |
62+
| With complete state [[SOURCE](example/lib/indicators/check_mark_indicator.dart)][[DEMO](https://custom-refresh-indicator.klyta.it/#/check-mark)] | Pull to fetch more [[SOURCE](example/lib/indicators/swipe_action.dart)][[DEMO](https://custom-refresh-indicator.klyta.it/#/fetch-more)] | Envelope [[SOURCE](example/lib/indicators/envelop_indicator.dart)][[DEMO](https://custom-refresh-indicator.klyta.it/#/envelop-indicator)] |
63+
| :----------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: |
64+
| ![indicator_with_complete_state](readme/indicator_with_complete_state.gif) | ![fetch_more](readme/fetch_more.gif) | ![Envelop indicator](readme/envelop_indicator.gif) |
6565

6666
| Programmatically controlled [[SOURCE](example/lib/screens/programmatically_controlled_indicator_screen.dart)][[DEMO](https://custom-refresh-indicator.klyta.it/#/programmatically-controlled)] | Your indicator | Your indicator |
67-
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------: |
68-
| ![programmatically_controlled](readme/programmatically_controlled.gif) | Have you created a fancy refresh indicator? This place is for you. [Open PR](https://github.com/gonuit/flutter-custom-refresh-indicator/pulls). | Have you created a fancy refresh indicator? This place is for you. [Open PR](https://github.com/gonuit/flutter-custom-refresh-indicator/pulls). |
67+
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------: |
68+
| ![programmatically_controlled](readme/programmatically_controlled.gif) | Have you created a fancy refresh indicator? This place is for you. [Open PR](https://github.com/gonuit/flutter-custom-refresh-indicator/pulls). | Have you created a fancy refresh indicator? This place is for you. [Open PR](https://github.com/gonuit/flutter-custom-refresh-indicator/pulls). |
6969

7070
# Documentation
7171

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import 'package:custom_refresh_indicator/custom_refresh_indicator.dart';
2+
import 'package:flutter/material.dart';
3+
4+
class EnvelopRefreshIndicator extends StatelessWidget {
5+
final Widget child;
6+
final bool leadingScrollIndicatorVisible;
7+
final bool trailingScrollIndicatorVisible;
8+
final RefreshCallback onRefresh;
9+
final Color? accent;
10+
11+
static const _circleSize = 70.0;
12+
13+
static const _defaultShadow = [
14+
BoxShadow(blurRadius: 10, color: Colors.black26)
15+
];
16+
17+
const EnvelopRefreshIndicator({
18+
Key? key,
19+
required this.child,
20+
required this.onRefresh,
21+
this.leadingScrollIndicatorVisible = false,
22+
this.trailingScrollIndicatorVisible = true,
23+
this.accent,
24+
}) : super(key: key);
25+
26+
@override
27+
Widget build(BuildContext context) {
28+
return CustomRefreshIndicator(
29+
leadingScrollIndicatorVisible: leadingScrollIndicatorVisible,
30+
trailingScrollIndicatorVisible: trailingScrollIndicatorVisible,
31+
builder: (context, child, controller) =>
32+
LayoutBuilder(builder: (context, constraints) {
33+
return AnimatedBuilder(
34+
animation: controller,
35+
builder: (context, _) {
36+
final widgetWidth = constraints.maxWidth;
37+
final widgetHeight = constraints.maxHeight;
38+
final letterTopWidth = (widgetWidth / 2) + 50;
39+
40+
final leftValue =
41+
(widgetWidth - (letterTopWidth * controller.value / 1))
42+
.clamp(letterTopWidth - 100, double.infinity);
43+
44+
final rightValue =
45+
(widgetWidth - (widgetWidth * controller.value / 1))
46+
.clamp(0.0, double.infinity);
47+
48+
final opacity = (controller.value - 1).clamp(0, 0.5) / 0.5;
49+
return Stack(
50+
children: <Widget>[
51+
Transform.scale(
52+
scale: 1 - 0.1 * controller.value.clamp(0.0, 1.0),
53+
child: child,
54+
),
55+
Positioned(
56+
right: rightValue,
57+
child: Container(
58+
height: widgetHeight,
59+
width: widgetWidth,
60+
decoration: const BoxDecoration(
61+
color: Colors.white,
62+
boxShadow: _defaultShadow,
63+
),
64+
),
65+
),
66+
Positioned(
67+
left: leftValue,
68+
child: CustomPaint(
69+
painter: TrianglePainter(
70+
strokeColor: Colors.white,
71+
paintingStyle: PaintingStyle.fill,
72+
),
73+
child: SizedBox(
74+
height: widgetHeight,
75+
width: letterTopWidth,
76+
),
77+
),
78+
),
79+
if (controller.value >= 1)
80+
Container(
81+
padding: const EdgeInsets.only(right: 100),
82+
child: Transform.scale(
83+
scale: controller.value,
84+
child: Opacity(
85+
opacity: controller.isLoading ? 1 : opacity,
86+
child: Align(
87+
alignment: Alignment.center,
88+
child: Container(
89+
width: _circleSize,
90+
height: _circleSize,
91+
decoration: BoxDecoration(
92+
boxShadow: _defaultShadow,
93+
color: accent ??
94+
Theme.of(context).colorScheme.primary,
95+
shape: BoxShape.circle,
96+
),
97+
child: Stack(
98+
alignment: Alignment.center,
99+
children: <Widget>[
100+
SizedBox(
101+
height: double.infinity,
102+
width: double.infinity,
103+
child: CircularProgressIndicator(
104+
valueColor: const AlwaysStoppedAnimation(
105+
Colors.black),
106+
value: controller.isLoading ? null : 0,
107+
),
108+
),
109+
const Icon(
110+
Icons.mail_outline,
111+
color: Colors.white,
112+
size: 35,
113+
),
114+
],
115+
),
116+
),
117+
),
118+
),
119+
),
120+
)
121+
],
122+
);
123+
});
124+
}),
125+
child: child,
126+
onRefresh: () => Future<void>.delayed(const Duration(seconds: 2)),
127+
);
128+
}
129+
}
130+
131+
class TrianglePainter extends CustomPainter {
132+
final Color strokeColor;
133+
final PaintingStyle paintingStyle;
134+
final double strokeWidth;
135+
136+
static double convertRadiusToSigma(double radius) {
137+
return radius * 0.57735 + 0.5;
138+
}
139+
140+
TrianglePainter(
141+
{this.strokeColor = Colors.black,
142+
this.strokeWidth = 3,
143+
this.paintingStyle = PaintingStyle.stroke});
144+
145+
@override
146+
void paint(Canvas canvas, Size size) {
147+
Paint paint = Paint()
148+
..color = strokeColor
149+
..strokeWidth = strokeWidth
150+
..style = paintingStyle;
151+
final path = getTrianglePath(size.width, size.height);
152+
final shadowPaint = Paint()
153+
..color = Colors.black.withAlpha(50)
154+
..maskFilter =
155+
MaskFilter.blur(BlurStyle.normal, convertRadiusToSigma(10));
156+
canvas.drawPath(path, shadowPaint);
157+
158+
canvas.drawPath(path, paint);
159+
}
160+
161+
Path getTrianglePath(double x, double y) {
162+
return Path()
163+
..moveTo(0, y / 2)
164+
..lineTo(x, 0)
165+
..lineTo(x, y)
166+
..lineTo(0, y / 2);
167+
}
168+
169+
@override
170+
bool shouldRepaint(TrianglePainter oldDelegate) {
171+
return oldDelegate.strokeColor != strokeColor ||
172+
oldDelegate.paintingStyle != paintingStyle ||
173+
oldDelegate.strokeWidth != strokeWidth;
174+
}
175+
}

example/lib/main.dart

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:example/screens/envelop_indicator_screen.dart';
12
import 'package:example/screens/presentation_screen.dart';
23
import 'package:example/screens/programmatically_controlled_indicator_screen.dart';
34
import 'package:example/widgets/web_frame.dart';
@@ -34,6 +35,7 @@ class MyApp extends StatelessWidget {
3435
'/presentation': (context) => const PresentationScreen(),
3536
'/check-mark': (context) => const CheckMarkIndicatorScreen(),
3637
'/warp': (context) => const WarpIndicatorScreen(),
38+
'/envelop': (context) => const EnvelopIndicatorScreen(),
3739
'/fetch-more': (context) => const FetchMoreScreen(),
3840
'/programmatically-controlled': (context) =>
3941
const ProgrammaticallyControlled(),
@@ -138,7 +140,18 @@ class MainScreen extends StatelessWidget {
138140
onPressed: () => Navigator.pushNamed(
139141
context,
140142
'/warp',
141-
arguments: simpleIndicator,
143+
),
144+
),
145+
const SizedBox(height: 15),
146+
ElevatedButton(
147+
child: Container(
148+
height: 50,
149+
alignment: Alignment.center,
150+
child: const Text("Envelop indicator"),
151+
),
152+
onPressed: () => Navigator.pushNamed(
153+
context,
154+
'/envelop',
142155
),
143156
),
144157
const SizedBox(height: 15),
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import 'package:example/indicators/envelop_indicator.dart';
2+
import 'package:example/widgets/example_app_bar.dart';
3+
import 'package:example/widgets/example_list.dart';
4+
import 'package:flutter/material.dart';
5+
6+
class EnvelopIndicatorScreen extends StatelessWidget {
7+
const EnvelopIndicatorScreen({Key? key}) : super(key: key);
8+
9+
@override
10+
Widget build(BuildContext context) {
11+
return Scaffold(
12+
appBar: const ExampleAppBar(
13+
title: "Envelop indicator",
14+
),
15+
body: EnvelopRefreshIndicator(
16+
child: const ExampleList(),
17+
accent: appContentColor,
18+
onRefresh: () => Future<void>.delayed(const Duration(seconds: 2)),
19+
),
20+
);
21+
}
22+
}

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ packages:
4949
path: ".."
5050
relative: true
5151
source: path
52-
version: "1.1.0-dev.1"
52+
version: "1.1.0-dev.2"
5353
fake_async:
5454
dependency: transitive
5555
description:

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: custom_refresh_indicator
22
description: Flutter Widget that make it easy to implement custom refresh indicator.
3-
version: 1.1.0-dev.1
3+
version: 1.1.0-dev.2
44
repository: https://github.com/gonuit/flutter-custom-refresh-indicator
55
issue_tracker: https://github.com/gonuit/flutter-custom-refresh-indicator/issues
66
homepage: https://github.com/gonuit/flutter-custom-refresh-indicator

readme/envelop_indicator.gif

2.56 MB
Loading

readme/letter_indicator.gif

-770 KB
Binary file not shown.

0 commit comments

Comments
 (0)