|
5 | 5 | import 'dart:async';
|
6 | 6 | import 'dart:math' as math;
|
7 | 7 |
|
| 8 | +import 'package:flutter/cupertino.dart'; |
8 | 9 | import 'package:flutter/foundation.dart' show clampDouble;
|
9 |
| -import 'package:flutter/widgets.dart'; |
10 | 10 |
|
11 | 11 | import 'debug.dart';
|
12 | 12 | import 'material_localizations.dart';
|
@@ -59,6 +59,8 @@ enum RefreshIndicatorTriggerMode {
|
59 | 59 | onEdge,
|
60 | 60 | }
|
61 | 61 |
|
| 62 | +enum _IndicatorType { material, adaptive } |
| 63 | + |
62 | 64 | /// A widget that supports the Material "swipe to refresh" idiom.
|
63 | 65 | ///
|
64 | 66 | /// {@youtube 560 315 https://www.youtube.com/watch?v=ORApMlzwMdM}
|
@@ -138,7 +140,38 @@ class RefreshIndicator extends StatefulWidget {
|
138 | 140 | this.semanticsValue,
|
139 | 141 | this.strokeWidth = RefreshProgressIndicator.defaultStrokeWidth,
|
140 | 142 | this.triggerMode = RefreshIndicatorTriggerMode.onEdge,
|
141 |
| - }); |
| 143 | + }) : _indicatorType = _IndicatorType.material; |
| 144 | + |
| 145 | + /// Creates an adaptive [RefreshIndicator] based on whether the target |
| 146 | + /// platform is iOS or macOS, following Material design's |
| 147 | + /// [Cross-platform guidelines](https://material.io/design/platform-guidance/cross-platform-adaptation.html). |
| 148 | + /// |
| 149 | + /// When the descendant overscrolls, a different spinning progress indicator |
| 150 | + /// is shown depending on platform. On iOS and macOS, |
| 151 | + /// [CupertinoActivityIndicator] is shown, but on all other platforms, |
| 152 | + /// [CircularProgressIndicator] appears. |
| 153 | + /// |
| 154 | + /// If a [CupertinoActivityIndicator] is shown, the following parameters are ignored: |
| 155 | + /// [backgroundColor], [semanticsLabel], [semanticsValue], [strokeWidth]. |
| 156 | + /// |
| 157 | + /// The target platform is based on the current [Theme]: [ThemeData.platform]. |
| 158 | + /// |
| 159 | + /// Noteably the scrollable widget itself will have slightly different behavior |
| 160 | + /// from [CupertinoSliverRefreshControl], due to a difference in structure. |
| 161 | + const RefreshIndicator.adaptive({ |
| 162 | + super.key, |
| 163 | + required this.child, |
| 164 | + this.displacement = 40.0, |
| 165 | + this.edgeOffset = 0.0, |
| 166 | + required this.onRefresh, |
| 167 | + this.color, |
| 168 | + this.backgroundColor, |
| 169 | + this.notificationPredicate = defaultScrollNotificationPredicate, |
| 170 | + this.semanticsLabel, |
| 171 | + this.semanticsValue, |
| 172 | + this.strokeWidth = RefreshProgressIndicator.defaultStrokeWidth, |
| 173 | + this.triggerMode = RefreshIndicatorTriggerMode.onEdge, |
| 174 | + }) : _indicatorType = _IndicatorType.adaptive; |
142 | 175 |
|
143 | 176 | /// The widget below this widget in the tree.
|
144 | 177 | ///
|
@@ -207,6 +240,8 @@ class RefreshIndicator extends StatefulWidget {
|
207 | 240 | /// By default, the value of [strokeWidth] is 2.0 pixels.
|
208 | 241 | final double strokeWidth;
|
209 | 242 |
|
| 243 | + final _IndicatorType _indicatorType; |
| 244 | + |
210 | 245 | /// Defines how this [RefreshIndicator] can be triggered when users overscroll.
|
211 | 246 | ///
|
212 | 247 | /// The [RefreshIndicator] can be pulled out in two cases,
|
@@ -555,14 +590,37 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS
|
555 | 590 | child: AnimatedBuilder(
|
556 | 591 | animation: _positionController,
|
557 | 592 | builder: (BuildContext context, Widget? child) {
|
558 |
| - return RefreshProgressIndicator( |
| 593 | + final Widget materialIndicator = RefreshProgressIndicator( |
559 | 594 | semanticsLabel: widget.semanticsLabel ?? MaterialLocalizations.of(context).refreshIndicatorSemanticLabel,
|
560 | 595 | semanticsValue: widget.semanticsValue,
|
561 | 596 | value: showIndeterminateIndicator ? null : _value.value,
|
562 | 597 | valueColor: _valueColor,
|
563 | 598 | backgroundColor: widget.backgroundColor,
|
564 | 599 | strokeWidth: widget.strokeWidth,
|
565 | 600 | );
|
| 601 | + |
| 602 | + final Widget cupertinoIndicator = CupertinoActivityIndicator( |
| 603 | + color: widget.color, |
| 604 | + ); |
| 605 | + |
| 606 | + switch(widget._indicatorType) { |
| 607 | + case _IndicatorType.material: |
| 608 | + return materialIndicator; |
| 609 | + |
| 610 | + case _IndicatorType.adaptive: { |
| 611 | + final ThemeData theme = Theme.of(context); |
| 612 | + switch (theme.platform) { |
| 613 | + case TargetPlatform.android: |
| 614 | + case TargetPlatform.fuchsia: |
| 615 | + case TargetPlatform.linux: |
| 616 | + case TargetPlatform.windows: |
| 617 | + return materialIndicator; |
| 618 | + case TargetPlatform.iOS: |
| 619 | + case TargetPlatform.macOS: |
| 620 | + return cupertinoIndicator; |
| 621 | + } |
| 622 | + } |
| 623 | + } |
566 | 624 | },
|
567 | 625 | ),
|
568 | 626 | ),
|
|
0 commit comments