sliver_refresh.dart 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  1. import 'dart:async';
  2. import 'dart:math';
  3. import 'package:eye_video/framework/uikit/refresher/pretty_refresher.dart';
  4. import 'package:flutter/rendering.dart';
  5. import 'package:flutter/scheduler.dart';
  6. import 'package:flutter/services.dart';
  7. import 'package:flutter/widgets.dart';
  8. class _SliverRefresh extends SingleChildRenderObjectWidget {
  9. const _SliverRefresh({
  10. Key? key,
  11. this.refreshIndicatorLayoutExtent = 0.0,
  12. this.hasLayoutExtent = false,
  13. this.enableInfiniteRefresh = false,
  14. this.headerFloat = false,
  15. this.axisDirectionNotifier,
  16. required this.infiniteRefresh,
  17. Widget child,
  18. }) : assert(refreshIndicatorLayoutExtent != null),
  19. assert(refreshIndicatorLayoutExtent >= 0.0),
  20. assert(hasLayoutExtent != null),
  21. super(key: key, child: child);
  22. // The amount of space the indicator should occupy in the sliver in a
  23. // resting state when in the refreshing mode.
  24. final double refreshIndicatorLayoutExtent;
  25. // _RenderRefreshSliverRefresh will paint the child in the available
  26. // space either way but this instructs the _RenderRefreshSliverRefresh
  27. // on whether to also occupy any layoutExtent space or not.
  28. final bool hasLayoutExtent;
  29. // 是否开启无限刷新
  30. final bool enableInfiniteRefresh;
  31. // 无限加载回调
  32. final VoidCallback infiniteRefresh;
  33. // Header浮动
  34. final bool headerFloat;
  35. // 列表方向
  36. final ValueNotifier<AxisDirection> axisDirectionNotifier;
  37. @override
  38. _RenderRefreshSliverRefresh createRenderObject(BuildContext context) {
  39. return _RenderRefreshSliverRefresh(
  40. refreshIndicatorExtent: refreshIndicatorLayoutExtent,
  41. hasLayoutExtent: hasLayoutExtent,
  42. enableInfiniteRefresh: enableInfiniteRefresh,
  43. infiniteRefresh: infiniteRefresh,
  44. headerFloat: headerFloat,
  45. axisDirectionNotifier: axisDirectionNotifier,
  46. );
  47. }
  48. @override
  49. void updateRenderObject(BuildContext context,
  50. covariant _RenderRefreshSliverRefresh renderObject) {
  51. renderObject
  52. ..refreshIndicatorLayoutExtent = refreshIndicatorLayoutExtent
  53. ..hasLayoutExtent = hasLayoutExtent
  54. ..enableInfiniteRefresh = enableInfiniteRefresh
  55. ..headerFloat = headerFloat;
  56. }
  57. }
  58. // RenderSliver object that gives its child RenderBox object space to paint
  59. // in the overscrolled gap and may or may not hold that overscrolled gap
  60. // around the RenderBox depending on whether [layoutExtent] is set.
  61. //
  62. // The [layoutExtentOffsetCompensation] field keeps internal accounting to
  63. // prevent scroll position jumps as the [layoutExtent] is set and unset.
  64. class _RenderRefreshSliverRefresh extends RenderSliverSingleBoxAdapter {
  65. _RenderRefreshSliverRefresh({
  66. required double refreshIndicatorExtent,
  67. required bool hasLayoutExtent,
  68. required bool enableInfiniteRefresh,
  69. required this.infiniteRefresh,
  70. required bool headerFloat,
  71. required this.axisDirectionNotifier,
  72. RenderBox child,
  73. }) : assert(refreshIndicatorExtent != null),
  74. assert(refreshIndicatorExtent >= 0.0),
  75. assert(hasLayoutExtent != null),
  76. _refreshIndicatorExtent = refreshIndicatorExtent,
  77. _enableInfiniteRefresh = enableInfiniteRefresh,
  78. _hasLayoutExtent = hasLayoutExtent,
  79. _headerFloat = headerFloat {
  80. this.child = child;
  81. }
  82. // The amount of layout space the indicator should occupy in the sliver in a
  83. // resting state when in the refreshing mode.
  84. double get refreshIndicatorLayoutExtent => _refreshIndicatorExtent;
  85. double _refreshIndicatorExtent;
  86. set refreshIndicatorLayoutExtent(double value) {
  87. assert(value != null);
  88. assert(value >= 0.0);
  89. if (value == _refreshIndicatorExtent) return;
  90. _refreshIndicatorExtent = value;
  91. markNeedsLayout();
  92. }
  93. // 列表方向
  94. final ValueNotifier<AxisDirection> axisDirectionNotifier;
  95. // The child box will be laid out and painted in the available space either
  96. // way but this determines whether to also occupy any
  97. // [SliverGeometry.layoutExtent] space or not.
  98. bool get hasLayoutExtent => _hasLayoutExtent;
  99. bool _hasLayoutExtent;
  100. set hasLayoutExtent(bool value) {
  101. assert(value != null);
  102. if (value == _hasLayoutExtent) return;
  103. _hasLayoutExtent = value;
  104. markNeedsLayout();
  105. }
  106. // 是否开启无限刷新
  107. bool get enableInfiniteRefresh => _enableInfiniteRefresh;
  108. bool _enableInfiniteRefresh;
  109. set enableInfiniteRefresh(bool value) {
  110. assert(value != null);
  111. if (value == _enableInfiniteRefresh) return;
  112. _enableInfiniteRefresh = value;
  113. markNeedsLayout();
  114. }
  115. // Header是否浮动
  116. bool get headerFloat => _headerFloat;
  117. bool _headerFloat;
  118. set headerFloat(bool value) {
  119. assert(value != null);
  120. if (value == _headerFloat) return;
  121. _headerFloat = value;
  122. markNeedsLayout();
  123. }
  124. // 无限加载回调
  125. final VoidCallback infiniteRefresh;
  126. // 触发无限刷新
  127. bool _triggerInfiniteRefresh = false;
  128. // 获取子组件大小
  129. double get childSize =>
  130. constraints.axis == Axis.vertical ? child.size.height : child.size.width;
  131. // This keeps track of the previously applied scroll offsets to the scrollable
  132. // so that when [refreshIndicatorLayoutExtent] or [hasLayoutExtent] changes,
  133. // the appropriate delta can be applied to keep everything in the same place
  134. // visually.
  135. double layoutExtentOffsetCompensation = 0.0;
  136. @override
  137. double get centerOffsetAdjustment {
  138. // Header浮动时去掉越界
  139. if (headerFloat) {
  140. final RenderViewportBase renderViewport = parent;
  141. return max(0.0, -renderViewport.offset.pixels);
  142. }
  143. return super.centerOffsetAdjustment;
  144. }
  145. @override
  146. void layout(Constraints constraints, {bool parentUsesSize = false}) {
  147. // Header浮动时保持刷新
  148. if (headerFloat) {
  149. final RenderViewportBase renderViewport = parent;
  150. super.layout(
  151. (constraints as SliverConstraints)
  152. .copyWith(overlap: min(0.0, renderViewport.offset.pixels)),
  153. parentUsesSize: true);
  154. } else {
  155. super.layout(constraints, parentUsesSize: parentUsesSize);
  156. }
  157. }
  158. @override
  159. void performLayout() {
  160. // Only pulling to refresh from the top is currently supported.
  161. // 注释以支持reverse
  162. // assert(constraints.axisDirection == AxisDirection.down);
  163. axisDirectionNotifier.value = constraints.axisDirection;
  164. assert(constraints.growthDirection == GrowthDirection.forward);
  165. // 判断是否触发无限刷新
  166. if (enableInfiniteRefresh &&
  167. constraints.scrollOffset < _refreshIndicatorExtent &&
  168. constraints.userScrollDirection != ScrollDirection.idle) {
  169. if (!_triggerInfiniteRefresh) {
  170. _triggerInfiniteRefresh = true;
  171. infiniteRefresh();
  172. }
  173. } else {
  174. if (constraints.scrollOffset > _refreshIndicatorExtent) {
  175. if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle) {
  176. _triggerInfiniteRefresh = false;
  177. } else {
  178. SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
  179. _triggerInfiniteRefresh = false;
  180. });
  181. }
  182. }
  183. }
  184. // The layout extent this sliver should now have.
  185. final double layoutExtent =
  186. (_hasLayoutExtent || enableInfiniteRefresh ? 1.0 : 0.0) *
  187. _refreshIndicatorExtent;
  188. // If the layoutExtent instructive changed, the SliverGeometry's
  189. // layoutExtent will take that value (on the next performLayout run). Shift
  190. // the scroll offset first so it doesn't make the scroll position suddenly jump.
  191. // 如果Header浮动则不用过渡
  192. if (!headerFloat) {
  193. if (layoutExtent != layoutExtentOffsetCompensation) {
  194. geometry = SliverGeometry(
  195. scrollOffsetCorrection: layoutExtent - layoutExtentOffsetCompensation,
  196. );
  197. layoutExtentOffsetCompensation = layoutExtent;
  198. // Return so we don't have to do temporary accounting and adjusting the
  199. // child's constraints accounting for this one transient frame using a
  200. // combination of existing layout extent, layout extent change and
  201. // the overlap.
  202. return;
  203. }
  204. }
  205. final bool active = constraints.overlap < 0.0 || layoutExtent > 0.0;
  206. final double overscrolledExtent =
  207. constraints.overlap < 0.0 ? constraints.overlap.abs() : 0.0;
  208. // Layout the child giving it the space of the currently dragged overscroll
  209. // which may or may not include a sliver layout extent space that it will
  210. // keep after the user lets go during the refresh process.
  211. // Header浮动时不用layoutExtent,不然会有跳动
  212. if (headerFloat) {
  213. child.layout(
  214. constraints.asBoxConstraints(
  215. maxExtent: _hasLayoutExtent
  216. ? overscrolledExtent > _refreshIndicatorExtent
  217. ? overscrolledExtent
  218. // 如果为double.infinity则占满列表
  219. : _refreshIndicatorExtent == double.infinity
  220. ? constraints.viewportMainAxisExtent
  221. : _refreshIndicatorExtent
  222. : overscrolledExtent,
  223. ),
  224. parentUsesSize: true,
  225. );
  226. } else {
  227. child.layout(
  228. constraints.asBoxConstraints(
  229. maxExtent: layoutExtent
  230. // Plus only the overscrolled portion immediately preceding this
  231. // sliver.
  232. +
  233. overscrolledExtent,
  234. ),
  235. parentUsesSize: true,
  236. );
  237. }
  238. if (active) {
  239. // 判断Header是否浮动
  240. if (headerFloat) {
  241. geometry = SliverGeometry(
  242. scrollExtent: 0.0,
  243. paintOrigin: 0.0,
  244. paintExtent: childSize,
  245. maxPaintExtent: childSize,
  246. layoutExtent: max(-constraints.scrollOffset, 0.0),
  247. visible: true,
  248. hasVisualOverflow: true,
  249. );
  250. } else {
  251. geometry = SliverGeometry(
  252. scrollExtent: layoutExtent,
  253. paintOrigin: -overscrolledExtent - constraints.scrollOffset,
  254. paintExtent: min(
  255. max(
  256. // Check child size (which can come from overscroll) because
  257. // layoutExtent may be zero. Check layoutExtent also since even
  258. // with a layoutExtent, the indicator builder may decide to not
  259. // build anything.
  260. max(childSize, layoutExtent) - constraints.scrollOffset,
  261. 0.0,
  262. ),
  263. constraints.remainingPaintExtent),
  264. maxPaintExtent: max(
  265. max(childSize, layoutExtent) - constraints.scrollOffset,
  266. 0.0,
  267. ),
  268. layoutExtent: max(layoutExtent - constraints.scrollOffset, 0.0),
  269. );
  270. }
  271. } else {
  272. // If we never started overscrolling, return no geometry.
  273. geometry = SliverGeometry.zero;
  274. }
  275. }
  276. @override
  277. void paint(PaintingContext paintContext, Offset offset) {
  278. if (constraints.overlap < 0.0 || constraints.scrollOffset + childSize > 0) {
  279. paintContext.paintChild(child, offset);
  280. }
  281. }
  282. // Nothing special done here because this sliver always paints its child
  283. // exactly between paintOrigin and paintExtent.
  284. @override
  285. void applyPaintTransform(RenderObject child, Matrix4 transform) {}
  286. }
  287. enum RefreshMode {
  288. // Initial state, when not being overscrolled into, or after the overscroll
  289. // is canceled or after done and the sliver retracted away.
  290. inactive,
  291. // While being overscrolled but not far enough yet to trigger the refresh.
  292. drag,
  293. // Dragged far enough that the onRefresh callback will run and the dragged
  294. // displacement is not yet at the final refresh resting state.
  295. armed,
  296. // While the onRefresh task is running.
  297. refresh,
  298. // 刷新完成
  299. refreshed,
  300. // While the indicator is animating away after refreshing.
  301. done,
  302. }
  303. typedef RefreshControlBuilder = Widget Function(
  304. BuildContext context,
  305. RefreshMode refreshState,
  306. double pulledExtent,
  307. double refreshTriggerPullDistance,
  308. double refreshIndicatorExtent,
  309. AxisDirection axisDirection,
  310. bool float,
  311. Duration completeDuration,
  312. bool enableInfiniteRefresh,
  313. bool success,
  314. bool noMore);
  315. typedef OnRefreshCallback = Future<void> Function();
  316. // 结束刷新
  317. // success 为是否成功(为false时,noMore无效)
  318. // noMore 为是否有更多数据
  319. typedef FinishRefresh = void Function({
  320. bool success,
  321. bool noMore,
  322. });
  323. // 绑定刷新指示剂
  324. typedef BindRefreshIndicator = void Function(
  325. FinishRefresh finishRefresh, VoidCallback resetRefreshState);
  326. class RefreshSliverRefreshControl extends StatefulWidget {
  327. const RefreshSliverRefreshControl({
  328. Key? key,
  329. this.refreshTriggerPullDistance = _defaultRefreshTriggerPullDistance,
  330. this.refreshIndicatorExtent = _defaultRefreshIndicatorExtent,
  331. required this.builder,
  332. this.completeDuration,
  333. this.onRefresh,
  334. this.focusNotifier,
  335. this.taskNotifier,
  336. this.callRefreshNotifier,
  337. this.taskIndependence,
  338. this.bindRefreshIndicator,
  339. this.enableControlFinishRefresh = false,
  340. this.enableInfiniteRefresh = false,
  341. this.enableHapticFeedback = false,
  342. this.headerFloat = false,
  343. }) : assert(refreshTriggerPullDistance != null),
  344. assert(refreshTriggerPullDistance > 0.0),
  345. assert(refreshIndicatorExtent != null),
  346. assert(refreshIndicatorExtent >= 0.0),
  347. assert(
  348. headerFloat || refreshTriggerPullDistance >= refreshIndicatorExtent,
  349. 'The refresh indicator cannot take more space in its final state '
  350. 'than the amount initially created by overscrolling.'),
  351. super(key: key);
  352. final double refreshTriggerPullDistance;
  353. final double refreshIndicatorExtent;
  354. final RefreshControlBuilder builder;
  355. final OnRefreshCallback onRefresh;
  356. // 完成延时
  357. final Duration completeDuration;
  358. // 绑定刷新指示器
  359. final BindRefreshIndicator bindRefreshIndicator;
  360. // 是否开启控制结束
  361. final bool enableControlFinishRefresh;
  362. // 是否开启无限刷新
  363. final bool enableInfiniteRefresh;
  364. // 开启震动反馈
  365. final bool enableHapticFeedback;
  366. // 滚动状态
  367. final ValueNotifier<bool> focusNotifier;
  368. // 触发刷新状态
  369. final ValueNotifier<bool> callRefreshNotifier;
  370. // 任务状态
  371. final ValueNotifier<TaskState> taskNotifier;
  372. // 是否任务独立
  373. final bool taskIndependence;
  374. // Header浮动
  375. final bool headerFloat;
  376. static const double _defaultRefreshTriggerPullDistance = 100.0;
  377. static const double _defaultRefreshIndicatorExtent = 60.0;
  378. // Retrieve the current state of the RefreshSliverRefreshControl. The same as the
  379. // state that gets passed into the [builder] function. Used for testing.
  380. /*@visibleForTesting
  381. static RefreshMode state(BuildContext context) {
  382. final _RefreshSliverRefreshControlState state = context
  383. .findAncestorStateOfType<_RefreshSliverRefreshControlState>();
  384. return state.refreshState;
  385. }*/
  386. @override
  387. _RefreshSliverRefreshControlState createState() =>
  388. _RefreshSliverRefreshControlState();
  389. }
  390. class _RefreshSliverRefreshControlState
  391. extends State<RefreshSliverRefreshControl> {
  392. // Reset the state from done to inactive when only this fraction of the
  393. // original `refreshTriggerPullDistance` is left.
  394. static const double _inactiveResetOverscrollFraction = 0.1;
  395. RefreshMode refreshState;
  396. // [Future] returned by the widget's `onRefresh`.
  397. Future<void> _refreshTask;
  398. Future<void> get refreshTask => _refreshTask;
  399. bool get hasTask {
  400. return widget.taskIndependence
  401. ? _refreshTask != null
  402. : widget.taskNotifier.value.loading ||
  403. widget.taskNotifier.value.refreshing;
  404. }
  405. set refreshTask(Future<void> task) {
  406. _refreshTask = task;
  407. if (!widget.taskIndependence) {
  408. widget.taskNotifier.value =
  409. widget.taskNotifier.value.copy(refreshing: task != null);
  410. }
  411. }
  412. // The amount of space available from the inner indicator box's perspective.
  413. //
  414. // The value is the sum of the sliver's layout extent and the overscroll
  415. // (which partially gets transferred into the layout extent when the refresh
  416. // triggers).
  417. //
  418. // The value of latestIndicatorBoxExtent doesn't change when the sliver scrolls
  419. // away without retracting; it is independent from the sliver's scrollOffset.
  420. double latestIndicatorBoxExtent = 0.0;
  421. bool hasSliverLayoutExtent = false;
  422. // 滚动焦点
  423. bool get _focus => widget.focusNotifier.value;
  424. // 刷新完成
  425. bool _success;
  426. // 没有更多数据
  427. bool _noMore;
  428. // 列表方向
  429. ValueNotifier<AxisDirection> _axisDirectionNotifier;
  430. // 初始化
  431. @override
  432. void initState() {
  433. super.initState();
  434. refreshState = RefreshMode.inactive;
  435. _axisDirectionNotifier = ValueNotifier<AxisDirection>(AxisDirection.down);
  436. // 绑定刷新指示器
  437. if (widget.bindRefreshIndicator != null) {
  438. widget.bindRefreshIndicator(finishRefresh, resetRefreshState);
  439. }
  440. }
  441. // 销毁
  442. @override
  443. void dispose() {
  444. super.dispose();
  445. }
  446. // 完成刷新
  447. void finishRefresh({
  448. bool success,
  449. bool noMore,
  450. }) {
  451. _success = success;
  452. _noMore = _success == false ? false : noMore;
  453. widget.taskNotifier.value =
  454. widget.taskNotifier.value.copy(refreshNoMore: _noMore);
  455. if (widget.enableControlFinishRefresh && refreshTask != null) {
  456. if (widget.enableInfiniteRefresh) {
  457. refreshState = RefreshMode.inactive;
  458. }
  459. setState(() => refreshTask = null);
  460. refreshState = transitionNextState();
  461. }
  462. }
  463. // 恢复状态
  464. void resetRefreshState() {
  465. if (mounted) {
  466. setState(() {
  467. _success = true;
  468. _noMore = false;
  469. refreshState = RefreshMode.inactive;
  470. hasSliverLayoutExtent = false;
  471. });
  472. }
  473. }
  474. // 无限刷新
  475. void _infiniteRefresh() {
  476. if (!hasTask &&
  477. widget.enableInfiniteRefresh &&
  478. _noMore != true &&
  479. !widget.callRefreshNotifier.value) {
  480. if (widget.enableHapticFeedback) {
  481. HapticFeedback.mediumImpact();
  482. }
  483. SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
  484. refreshState = RefreshMode.refresh;
  485. refreshTask = widget.onRefresh()
  486. ..then((_) {
  487. if (mounted && !widget.enableControlFinishRefresh) {
  488. refreshState = RefreshMode.refresh;
  489. setState(() => refreshTask = null);
  490. // Trigger one more transition because by this time, BoxConstraint's
  491. // maxHeight might already be resting at 0 in which case no
  492. // calls to [transitionNextState] will occur anymore and the
  493. // state may be stuck in a non-inactive state.
  494. refreshState = transitionNextState();
  495. }
  496. });
  497. setState(() => hasSliverLayoutExtent = true);
  498. });
  499. }
  500. }
  501. // A state machine transition calculator. Multiple states can be transitioned
  502. // through per single call.
  503. RefreshMode transitionNextState() {
  504. RefreshMode nextState;
  505. // 判断是否没有更多
  506. if (_noMore == true && widget.enableInfiniteRefresh) {
  507. return refreshState;
  508. } else if (_noMore == true &&
  509. refreshState != RefreshMode.refresh &&
  510. refreshState != RefreshMode.refreshed &&
  511. refreshState != RefreshMode.done) {
  512. return refreshState;
  513. } else if (widget.enableInfiniteRefresh &&
  514. refreshState == RefreshMode.done) {
  515. return RefreshMode.inactive;
  516. }
  517. // 结束
  518. void goToDone() {
  519. nextState = RefreshMode.done;
  520. refreshState = RefreshMode.done;
  521. // Either schedule the RenderSliver to re-layout on the next frame
  522. // when not currently in a frame or schedule it on the next frame.
  523. if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle) {
  524. setState(() => hasSliverLayoutExtent = false);
  525. } else {
  526. SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
  527. setState(() => hasSliverLayoutExtent = false);
  528. });
  529. }
  530. }
  531. // 完成
  532. RefreshMode goToFinish() {
  533. // 判断刷新完成
  534. RefreshMode state = RefreshMode.refreshed;
  535. // 添加延时
  536. if (widget.completeDuration == null || widget.enableInfiniteRefresh) {
  537. goToDone();
  538. return null;
  539. } else {
  540. Future.delayed(widget.completeDuration, () {
  541. if (mounted) {
  542. goToDone();
  543. }
  544. });
  545. return state;
  546. }
  547. }
  548. switch (refreshState) {
  549. case RefreshMode.inactive:
  550. if (latestIndicatorBoxExtent <= 0 ||
  551. (!_focus && !widget.callRefreshNotifier.value)) {
  552. return RefreshMode.inactive;
  553. } else {
  554. if (widget.callRefreshNotifier.value) {
  555. widget.callRefreshNotifier.value = false;
  556. }
  557. nextState = RefreshMode.drag;
  558. }
  559. continue drag;
  560. drag:
  561. case RefreshMode.drag:
  562. if (latestIndicatorBoxExtent == 0) {
  563. return RefreshMode.inactive;
  564. } else if (latestIndicatorBoxExtent <=
  565. widget.refreshTriggerPullDistance) {
  566. // 如果未触发刷新则取消固定高度
  567. if (hasSliverLayoutExtent && !hasTask) {
  568. SchedulerBinding.instance
  569. .addPostFrameCallback((Duration timestamp) {
  570. setState(() => hasSliverLayoutExtent = false);
  571. });
  572. }
  573. return RefreshMode.drag;
  574. } else {
  575. // 提前固定高度,防止列表回弹
  576. SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
  577. if (!hasSliverLayoutExtent) {
  578. setState(() => hasSliverLayoutExtent = true);
  579. }
  580. });
  581. if (widget.onRefresh != null && !hasTask) {
  582. if (!_focus) {
  583. if (widget.callRefreshNotifier.value) {
  584. widget.callRefreshNotifier.value = false;
  585. }
  586. if (widget.enableHapticFeedback) {
  587. HapticFeedback.mediumImpact();
  588. }
  589. // 触发刷新任务
  590. SchedulerBinding.instance
  591. .addPostFrameCallback((Duration timestamp) {
  592. refreshTask = widget.onRefresh()
  593. ..then((_) {
  594. if (mounted && !widget.enableControlFinishRefresh) {
  595. if (widget.enableInfiniteRefresh) {
  596. refreshState = RefreshMode.inactive;
  597. }
  598. setState(() => refreshTask = null);
  599. if (!widget.enableInfiniteRefresh)
  600. refreshState = transitionNextState();
  601. }
  602. });
  603. });
  604. return RefreshMode.armed;
  605. }
  606. return RefreshMode.drag;
  607. }
  608. return RefreshMode.drag;
  609. }
  610. // Don't continue here. We can never possibly call onRefresh and
  611. // progress to the next state in one [computeNextState] call.
  612. break;
  613. case RefreshMode.armed:
  614. if (refreshState == RefreshMode.armed && !hasTask) {
  615. // 完成
  616. var state = goToFinish();
  617. if (state != null) return state;
  618. continue done;
  619. }
  620. if (latestIndicatorBoxExtent != widget.refreshIndicatorExtent) {
  621. return RefreshMode.armed;
  622. } else {
  623. nextState = RefreshMode.refresh;
  624. }
  625. continue refresh;
  626. refresh:
  627. case RefreshMode.refresh:
  628. if (refreshTask != null) {
  629. return RefreshMode.refresh;
  630. } else {
  631. // 完成
  632. var state = goToFinish();
  633. if (state != null) return state;
  634. }
  635. continue done;
  636. done:
  637. case RefreshMode.done:
  638. // Let the transition back to inactive trigger before strictly going
  639. // to 0.0 since the last bit of the animation can take some time and
  640. // can feel sluggish if not going all the way back to 0.0 prevented
  641. // a subsequent pull-to-refresh from starting.
  642. if (latestIndicatorBoxExtent >
  643. widget.refreshTriggerPullDistance *
  644. _inactiveResetOverscrollFraction) {
  645. return RefreshMode.done;
  646. } else {
  647. nextState = RefreshMode.inactive;
  648. }
  649. break;
  650. case RefreshMode.refreshed:
  651. nextState = refreshState;
  652. break;
  653. default:
  654. break;
  655. }
  656. return nextState;
  657. }
  658. @override
  659. Widget build(BuildContext context) {
  660. return _SliverRefresh(
  661. refreshIndicatorLayoutExtent: widget.refreshIndicatorExtent,
  662. hasLayoutExtent: hasSliverLayoutExtent,
  663. enableInfiniteRefresh: widget.enableInfiniteRefresh,
  664. infiniteRefresh: _infiniteRefresh,
  665. headerFloat: widget.headerFloat,
  666. axisDirectionNotifier: _axisDirectionNotifier,
  667. // A LayoutBuilder lets the sliver's layout changes be fed back out to
  668. // its owner to trigger state changes.
  669. child: LayoutBuilder(
  670. builder: (BuildContext context, BoxConstraints constraints) {
  671. // 判断是否有加载任务
  672. if (!widget.taskIndependence && widget.taskNotifier.value.loading) {
  673. return SizedBox();
  674. }
  675. // 是否为垂直方向
  676. bool isVertical =
  677. _axisDirectionNotifier.value == AxisDirection.down ||
  678. _axisDirectionNotifier.value == AxisDirection.up;
  679. latestIndicatorBoxExtent =
  680. isVertical ? constraints.maxHeight : constraints.maxWidth;
  681. refreshState = transitionNextState();
  682. if (widget.builder != null && latestIndicatorBoxExtent >= 0) {
  683. return widget.builder(
  684. context,
  685. refreshState,
  686. latestIndicatorBoxExtent,
  687. widget.refreshTriggerPullDistance,
  688. widget.refreshIndicatorExtent,
  689. _axisDirectionNotifier.value,
  690. widget.headerFloat,
  691. widget.completeDuration,
  692. widget.enableInfiniteRefresh,
  693. _success ?? true,
  694. _noMore ?? false,
  695. );
  696. }
  697. return Container();
  698. },
  699. ),
  700. );
  701. }
  702. }