demo_scroll1.dart 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:ftrecruiter/comm/utils/log_utils.dart';
  4. import 'package:ftrecruiter/comm/widget/load_state_layout.dart';
  5. import 'package:ftrecruiter/comm/widget/my_load_image.dart';
  6. import 'package:ftrecruiter/modules/zdemo/page_four.dart';
  7. import 'package:ftrecruiter/modules/zdemo/page_one.dart';
  8. import 'package:ftrecruiter/modules/zdemo/page_three.dart';
  9. import 'package:ftrecruiter/modules/zdemo/page_two.dart';
  10. class DemoScroll1Page extends StatelessWidget {
  11. const DemoScroll1Page({Key? key}) : super(key: key);
  12. @override
  13. Widget build(BuildContext context) {
  14. final _tabs = <String>['猜你喜欢', '今日特价', '发现更多'];
  15. // 构建 tabBar
  16. return DefaultTabController(
  17. length: _tabs.length, // This is the number of tabs.
  18. child: Scaffold(
  19. body: NestedScrollView(
  20. headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
  21. return <Widget>[
  22. SliverOverlapAbsorber(
  23. handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
  24. sliver: SliverAppBar(
  25. title: const Text('商城'),
  26. floating: false,
  27. snap: false,
  28. elevation: 0.0,
  29. forceElevated: innerBoxIsScrolled,
  30. pinned: true,
  31. ),
  32. ),
  33. SliverToBoxAdapter(
  34. child: Container(
  35. alignment: Alignment.center,
  36. width: double.infinity,
  37. height: 191,
  38. child: MyLoadImage("https://i04piccdn.sogoucdn.com/a35bde4b5756167a",width: 340, height: 191),
  39. ),
  40. ),
  41. SliverPersistentHeader(
  42. pinned: true, // 固定在顶部
  43. delegate: SliverHeaderDelegate.fixedHeight( //固定高度
  44. height: 50,
  45. child: Container(
  46. color: Colors.pink,
  47. child: TabBar(
  48. tabs: _tabs.map((String name) => Tab(text: name)).toList(),
  49. ),
  50. ),
  51. ),
  52. ),
  53. ];
  54. },
  55. body: TabBarView(
  56. children: _tabs.map((String name) {
  57. return Builder(
  58. builder: (BuildContext context) {
  59. return CustomScrollView(
  60. key: PageStorageKey<String>(name),
  61. slivers: <Widget>[
  62. SliverOverlapInjector(
  63. handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
  64. ),
  65. SliverPadding(
  66. padding: const EdgeInsets.all(8.0),
  67. sliver: buildSliverList(50),
  68. ),
  69. ],
  70. );
  71. },
  72. );
  73. }).toList(),
  74. ),
  75. ),
  76. ),
  77. );
  78. }
  79. // 构建固定高度的SliverList,count为列表项属相
  80. Widget buildSliverList([int count = 5]) {
  81. return SliverFixedExtentList(
  82. itemExtent: 50,
  83. delegate: SliverChildBuilderDelegate(
  84. (context, index) {
  85. return ListTile(title: Text('$index'));
  86. },
  87. childCount: count,
  88. ),
  89. );
  90. }
  91. }
  92. typedef SliverHeaderBuilder = Widget Function(
  93. BuildContext context, double shrinkOffset, bool overlapsContent);
  94. class SliverHeaderDelegate extends SliverPersistentHeaderDelegate {
  95. // child 为 header
  96. SliverHeaderDelegate({
  97. required this.maxHeight,
  98. this.minHeight = 0,
  99. required Widget child,
  100. }) : builder = ((a, b, c) => child),
  101. assert(minHeight <= maxHeight && minHeight >= 0);
  102. //最大和最小高度相同
  103. SliverHeaderDelegate.fixedHeight({
  104. required double height,
  105. required Widget child,
  106. }) : builder = ((a, b, c) => child),
  107. maxHeight = height,
  108. minHeight = height;
  109. //需要自定义builder时使用
  110. SliverHeaderDelegate.builder({
  111. required this.maxHeight,
  112. this.minHeight = 0,
  113. required this.builder,
  114. });
  115. final double maxHeight;
  116. final double minHeight;
  117. final SliverHeaderBuilder builder;
  118. @override
  119. Widget build(
  120. BuildContext context,
  121. double shrinkOffset,
  122. bool overlapsContent,
  123. ) {
  124. Widget child = builder(context, shrinkOffset, overlapsContent);
  125. //测试代码:如果在调试模式,且子组件设置了key,则打印日志
  126. assert(() {
  127. if (child.key != null) {
  128. print('${child.key}: shrink: $shrinkOffset,overlaps:$overlapsContent');
  129. }
  130. return true;
  131. }());
  132. // 让 header 尽可能充满限制的空间;宽度为 Viewport 宽度,
  133. // 高度随着用户滑动在[minHeight,maxHeight]之间变化。
  134. return SizedBox.expand(child: child);
  135. }
  136. @override
  137. double get maxExtent => maxHeight;
  138. @override
  139. double get minExtent => minHeight;
  140. @override
  141. bool shouldRebuild(SliverHeaderDelegate old) {
  142. return old.maxExtent != maxExtent || old.minExtent != minExtent;
  143. }
  144. }