Skip to content

Commit cad9384

Browse files
author
Kamil Klyta
committed
Add autoRebuild argument
1 parent 7f3f057 commit cad9384

File tree

10 files changed

+127
-121
lines changed

10 files changed

+127
-121
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
## 2.0.0-dev.4
22
## Breaking changes
3+
- Added `autoRebuild` flag which is by default set to `true`.
4+
From now on, there is no need to wrap widgets in the builder function with the `AnimatedBuilder` widget, as it will be automatically rebuilt. For optimization purposes, you can use the old behavior by setting the `autoRebuild` argument to false.
35
- Renamed `extentPercentageToArmed` argument to `containerExtentPercentageToArmed` which better describes what it exactly does.
46
- Changed the default value of the `defaultContainerExtentPercentageToArmed` from `0.20` to `0.1(6)` to match the behavior of the built-in indicator widget.
57
- Removed deprecated **IndicatorStateHelper** class. Instead use **CustomRefreshIndicator.onStateChanged** method.
68
- Removed deprecated **leadingGlowVisible** and **trailingGlowVisible** arguments. Instead use **leadingScrollIndicatorVisible** and **trailingScrollIndicatorVisible** accoringly.
7-
- The default value of the **trailingScrollIndicatorVisible** for the **EnvelopRefreshIndicator** has been changed from **true** to **false**.
89
- Allow setting the edge of the list that will trigger the pull to refresh action.
910
- Introduced **IndicatorEdge**, **IndicatorTrigger**, **IndicatorSide** and **IndicatorTriggerMode** classes.
1011
- Replaced **reversed** argument of the **CustomRefreshIndicator** class with **trigger**.
@@ -15,6 +16,7 @@
1516
- Now the *onRefresh* function will be triggered immediately when the indicator is released in the armed state. Previously, the *onRefresh* function was triggered when the indicator reached a target value in the loading state of `1.0`.
1617
- Fixed a bug causing the *onRefresh* method not to be triggered on the iOS platform due to bounce physics.
1718
- Implemented equality operator for *IndicatorStateChange* class.
19+
- Improved code coverage with tests
1820
- Multiple minor fixes, improvements and optimizations.
1921
## 1.2.1
2022
- Flutter 3.0.0 migration backward compatibility fix ([#31](https://github.com/gonuit/flutter-custom-refresh-indicator/pull/31)) by [Jordan1122](https://github.com/Jordan1122)

README.md

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ A flutter package that allows you to easily create a custom refresh indicator wi
1414
```dart
1515
CustomRefreshIndicator(
1616
/// Scrollable widget
17-
child: ListView.separated(
18-
itemBuilder: (BuildContext context, int index) => const SizedBox(
17+
child: ListView.builder(
18+
itemBuilder: (BuildContext context, int index) => Container(
19+
child: Text(index.toString()),
1920
height: 100,
2021
),
21-
separatorBuilder: (BuildContext context, int index) =>
22-
const SizedBox(height: 20),
2322
),
24-
/// Custom indicator builder function
23+
/// The function that builds the indicator
2524
builder: (
2625
BuildContext context,
2726
Widget child,
@@ -30,13 +29,9 @@ CustomRefreshIndicator(
3029
/// TODO: Implement your own refresh indicator
3130
return Stack(
3231
children: <Widget>[
33-
AnimatedBuilder(
34-
animation: controller,
35-
builder: (BuildContext context, _) {
36-
/// This part will be rebuild on every controller change
37-
return MyIndicator();
38-
},
39-
),
32+
/// Your indicator implementation
33+
return MyIndicator(value: controller.value, loading: controller.state.isLoading);
34+
4035
/// Scrollable widget that was provided as [child] argument
4136
///
4237
/// TIP:
@@ -45,8 +40,8 @@ CustomRefreshIndicator(
4540
],
4641
);
4742
}
48-
/// A function that's called when the user has dragged the refresh indicator
49-
/// far enough to demonstrate that they want the app to refresh.
43+
/// A function that is called when the user drags the refresh indicator
44+
/// far enough to show that they want to refresh the list.
5045
/// Should return [Future].
5146
onRefresh: myAsyncRefreshMethod,
5247
)

example/lib/indicators/envelope_indicator.dart

Lines changed: 79 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -30,97 +30,92 @@ class EnvelopRefreshIndicator extends StatelessWidget {
3030
trailingScrollIndicatorVisible: trailingScrollIndicatorVisible,
3131
builder: (context, child, controller) =>
3232
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;
33+
final widgetWidth = constraints.maxWidth;
34+
final widgetHeight = constraints.maxHeight;
35+
final letterTopWidth = (widgetWidth / 2) + 50;
3936

40-
final leftValue =
41-
(widgetWidth - (letterTopWidth * controller.value / 1))
42-
.clamp(letterTopWidth - 100, double.infinity);
37+
final leftValue =
38+
(widgetWidth - (letterTopWidth * controller.value / 1))
39+
.clamp(letterTopWidth - 100, double.infinity);
4340

44-
final rightValue =
45-
(widgetWidth - (widgetWidth * controller.value / 1))
46-
.clamp(0.0, double.infinity);
41+
final rightValue = (widgetWidth - (widgetWidth * controller.value / 1))
42+
.clamp(0.0, double.infinity);
4743

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-
],
44+
final opacity = (controller.value - 1).clamp(0, 0.5) / 0.5;
45+
return Stack(
46+
children: <Widget>[
47+
Transform.scale(
48+
scale: 1 - 0.1 * controller.value.clamp(0.0, 1.0),
49+
child: child,
50+
),
51+
Positioned(
52+
right: rightValue,
53+
child: Container(
54+
height: widgetHeight,
55+
width: widgetWidth,
56+
decoration: const BoxDecoration(
57+
color: Colors.white,
58+
boxShadow: _defaultShadow,
59+
),
60+
),
61+
),
62+
Positioned(
63+
left: leftValue,
64+
child: CustomPaint(
65+
painter: TrianglePainter(
66+
strokeColor: Colors.white,
67+
paintingStyle: PaintingStyle.fill,
68+
),
69+
child: SizedBox(
70+
height: widgetHeight,
71+
width: letterTopWidth,
72+
),
73+
),
74+
),
75+
if (controller.value >= 1)
76+
Container(
77+
padding: const EdgeInsets.only(right: 100),
78+
child: Transform.scale(
79+
scale: controller.value,
80+
child: Opacity(
81+
opacity: controller.isLoading ? 1 : opacity,
82+
child: Align(
83+
alignment: Alignment.center,
84+
child: Container(
85+
width: _circleSize,
86+
height: _circleSize,
87+
decoration: BoxDecoration(
88+
boxShadow: _defaultShadow,
89+
color:
90+
accent ?? Theme.of(context).colorScheme.primary,
91+
shape: BoxShape.circle,
92+
),
93+
child: Stack(
94+
alignment: Alignment.center,
95+
children: <Widget>[
96+
SizedBox(
97+
height: double.infinity,
98+
width: double.infinity,
99+
child: CircularProgressIndicator(
100+
valueColor:
101+
const AlwaysStoppedAnimation(Colors.black),
102+
value: controller.isLoading ? null : 0,
115103
),
116104
),
117-
),
105+
const Icon(
106+
Icons.mail_outline,
107+
color: Colors.white,
108+
size: 35,
109+
),
110+
],
118111
),
119112
),
120-
)
121-
],
122-
);
123-
});
113+
),
114+
),
115+
),
116+
)
117+
],
118+
);
124119
}),
125120
child: child,
126121
onRefresh: () => Future<void>.delayed(const Duration(seconds: 2)),

example/lib/indicators/ice_cream_indicator.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class _IceCreamIndicatorState extends State<IceCreamIndicator>
9393
return CustomRefreshIndicator(
9494
offsetToArmed: _indicatorSize,
9595
onRefresh: () => Future.delayed(const Duration(seconds: 4)),
96+
autoRebuild: false,
9697
child: widget.child,
9798
builder: (
9899
BuildContext context,

example/lib/indicators/plane_indicator.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ class _PlaneIndicatorState extends State<PlaneIndicator>
172172
return CustomRefreshIndicator(
173173
offsetToArmed: _offsetToArmed,
174174
child: widget.child,
175+
autoRebuild: false,
175176
onRefresh: () => Future.delayed(const Duration(seconds: 3)),
176177
builder: (BuildContext context, Widget child,
177178
IndicatorController controller) {

example/lib/indicators/simple_indicator.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,9 @@ final simpleIndicatorWithOpacity = CustomIndicatorConfig(
7272
builder: (context, child, controller) =>
7373
LayoutBuilder(builder: (context, constraints) {
7474
return Stack(children: <Widget>[
75-
AnimatedBuilder(
75+
Opacity(
76+
opacity: 1.0 - controller.value.clamp(0.0, 1.0),
7677
child: child,
77-
animation: controller,
78-
builder: (context, child) => Opacity(
79-
opacity: 1.0 - controller.value.clamp(0.0, 1.0),
80-
child: child,
81-
),
8278
),
8379
PositionedIndicatorContainer(
8480
constraints: constraints,

example/lib/indicators/test_indicator.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,9 @@ final testIndicator = CustomIndicatorConfig(
1919
width: double.infinity,
2020
color: Colors.red,
2121
),
22-
AnimatedBuilder(
22+
Transform.translate(
23+
offset: Offset(0, controller.value * 100),
2324
child: child,
24-
animation: controller,
25-
builder: (context, child) => Transform.translate(
26-
offset: Offset(0, controller.value * 100),
27-
child: child,
28-
),
2925
),
3026
]),
3127
);

example/lib/indicators/warp_indicator.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ class _WarpIndicatorState extends State<WarpIndicator>
131131
leadingScrollIndicatorVisible: false,
132132
trailingScrollIndicatorVisible: false,
133133
onRefresh: widget.onRefresh,
134+
autoRebuild: false,
134135
onStateChanged: (change) {
135136
if (change.didChange(to: IndicatorState.loading)) {
136137
_startShakeAnimation();

example/lib/screens/example_indicator_screen.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class ExampleIndicatorScreen extends StatelessWidget {
1919
leadingScrollIndicatorVisible: false,
2020
trailingScrollIndicatorVisible: false,
2121
offsetToArmed: 200.0,
22+
autoRebuild: true,
2223
builder: customIndicator.builder,
2324
onRefresh: () => Future.delayed(const Duration(seconds: 2)),
2425
child: const ExampleList(

lib/src/custom_refresh_indicator.dart

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,24 @@ class CustomRefreshIndicator extends StatefulWidget {
134134
/// Defaults to [IndicatorTriggerMode.onEdge].
135135
final IndicatorTriggerMode triggerMode;
136136

137+
/// When set to true, the [builder] function will be called whenever the controller changes.
138+
/// It is set to `true` by default.
139+
///
140+
/// This can be useful for optimizing performance in complex widgets.
141+
/// When setting this to false, you can manage which part of the ui you want to rebuild,
142+
/// such as using the AnimationBuilder widget.
143+
final bool autoRebuild;
144+
137145
const CustomRefreshIndicator({
138146
Key? key,
139147
required this.child,
140148
required this.onRefresh,
141149
required this.builder,
150+
this.controller,
142151
this.trigger = IndicatorTrigger.startEdge,
143152
this.triggerMode = IndicatorTriggerMode.onEdge,
144153
this.notificationPredicate = defaultScrollNotificationPredicate,
145-
this.controller,
154+
this.autoRebuild = true,
146155
this.offsetToArmed,
147156
this.onStateChanged,
148157
double? containerExtentPercentageToArmed,
@@ -558,17 +567,26 @@ class CustomRefreshIndicatorState extends State<CustomRefreshIndicator>
558567

559568
@override
560569
Widget build(BuildContext context) {
561-
return widget.builder(
562-
context,
563-
NotificationListener<ScrollNotification>(
564-
onNotification: _handleScrollNotification,
565-
child: NotificationListener<OverscrollIndicatorNotification>(
566-
onNotification: _handleScrollIndicatorNotification,
567-
child: widget.child,
568-
),
570+
final child = NotificationListener<ScrollNotification>(
571+
onNotification: _handleScrollNotification,
572+
child: NotificationListener<OverscrollIndicatorNotification>(
573+
onNotification: _handleScrollIndicatorNotification,
574+
child: widget.child,
569575
),
570-
controller,
571576
);
577+
578+
if (widget.autoRebuild) {
579+
return AnimatedBuilder(
580+
animation: controller,
581+
builder: (context, _) => widget.builder(context, child, controller),
582+
);
583+
} else {
584+
return widget.builder(
585+
context,
586+
child,
587+
controller,
588+
);
589+
}
572590
}
573591

574592
@override

0 commit comments

Comments
 (0)