当前位置:   article > 正文

Flutter 底部列表抽屉,三阶滑动 , 支持列表Sliver布局_flutter 底部抽屉

flutter 底部抽屉

Flutter 底部是列表的抽屉,三阶滑动,支持列表Sliver布局,Head布局可以触发抽屉滑动, 内部的Sliver布局也可以触发抽屉滑动;

抽屉在最大高度时,Sliver布局可以滑动,其他高度会触发抽屉滑动;

 

  1. import 'package:flutter/material.dart';
  2. class SliverPanel3Controller {
  3. _SliverPanel3ViewState? onState;
  4. _addState(_SliverPanel3ViewState? onState){
  5. this.onState = onState;
  6. }
  7. void setPanel3State(Panel3State state){
  8. onState?.setPanel3state(state);
  9. }
  10. Panel3State getPanel3State(){
  11. return onState?._panel3state.value ?? Panel3State.CENTER;
  12. }
  13. }
  14. enum Panel3State { OPEN, CENTER, CLOSE, EXIT }
  15. class SliverPanel3View extends StatefulWidget {
  16. final double heightOpen; //展开高度
  17. final double heightCenter; //中间高度
  18. final double heightClose; //闭合高度
  19. final Widget headWidget; //标题布局
  20. final Widget Function(ScrollController sc , ScrollPhysics? physics)? bodyWidget; //内容布局
  21. final Panel3State initPanel3state; //初始状态
  22. final Color backColor; //背景色
  23. final SliverPanel3Controller? sliverPanel3Controller; //控制器
  24. const SliverPanel3View(
  25. {Key? key,
  26. this.heightOpen = 600,
  27. this.heightCenter = 360,
  28. this.heightClose = 100,
  29. required this.headWidget,
  30. required this.bodyWidget,
  31. this.sliverPanel3Controller,
  32. this.initPanel3state = Panel3State.CENTER,
  33. this.backColor = Colors.transparent})
  34. : super(key: key);
  35. @override
  36. State<SliverPanel3View> createState() => _SliverPanel3ViewState();
  37. }
  38. class _SliverPanel3ViewState extends State<SliverPanel3View> {
  39. double heightClose = 100;
  40. double heightCenter = 360;
  41. double heightOpen = 600;
  42. final ValueNotifier<Panel3State> _panel3state = ValueNotifier(Panel3State.CENTER);
  43. SliverPanel3Controller? sliverPanel3Controller;
  44. ScrollController _sc = ScrollController();
  45. ScrollPhysics? _physics;
  46. @override
  47. void initState() {
  48. super.initState();
  49. heightClose = widget.heightClose;
  50. heightCenter = widget.heightCenter;
  51. heightOpen = widget.heightOpen;
  52. _panel3state.value = widget.initPanel3state;
  53. sliverPanel3Controller = widget.sliverPanel3Controller;
  54. sliverPanel3Controller?._addState(this);
  55. }
  56. void setPanel3state(Panel3State s){
  57. _panel3state.value = s;
  58. }
  59. double panelHeight() {
  60. if(_panel3state.value == Panel3State.OPEN){
  61. _physics = const AlwaysScrollableScrollPhysics();
  62. }else{
  63. _physics = const NeverScrollableScrollPhysics();
  64. }
  65. if (_panel3state.value == Panel3State.OPEN) {
  66. return heightOpen;
  67. } else if (_panel3state.value == Panel3State.CENTER) {
  68. return heightCenter;
  69. } else if (_panel3state.value == Panel3State.CLOSE) {
  70. return heightClose;
  71. } else {
  72. return 0;
  73. }
  74. }
  75. @override
  76. Widget build(BuildContext context) {
  77. return ValueListenableBuilder(
  78. valueListenable: _panel3state,
  79. builder: (context, state, child) {
  80. return AnimatedContainer(
  81. color: widget.backColor,
  82. duration: const Duration(milliseconds: 220),
  83. width: double.infinity,
  84. height: panelHeight(),
  85. child: Column(
  86. children: [HeadView(), Expanded(child: BodyView())],
  87. ),
  88. );
  89. },
  90. );
  91. }
  92. double pointerMove = 0;
  93. bool isCan = true;
  94. double scOffset = 0;
  95. Widget HeadView() {
  96. return Listener(
  97. onPointerDown: (e) {
  98. pointerMove = e.position.dy;
  99. isCan = true;
  100. },
  101. onPointerMove: (e) {
  102. if (e.position.dy - pointerMove > 36 && isCan) {
  103. // print("手指下滑触发 -- ");
  104. isCan = false;
  105. if (_panel3state.value == Panel3State.OPEN) {
  106. _panel3state.value = Panel3State.CENTER;
  107. } else if (_panel3state.value == Panel3State.CENTER) {
  108. _panel3state.value = Panel3State.CLOSE;
  109. }
  110. } else if (e.position.dy - pointerMove < -36 && isCan) {
  111. // print("手指上滑触发 -- ");
  112. isCan = false;
  113. if (_panel3state.value == Panel3State.CLOSE) {
  114. _panel3state.value = Panel3State.CENTER;
  115. } else if (_panel3state.value == Panel3State.CENTER) {
  116. _panel3state.value = Panel3State.OPEN;
  117. }
  118. }
  119. },
  120. onPointerUp: (e) {
  121. isCan = true;
  122. },
  123. child: widget.headWidget,
  124. );
  125. }
  126. Widget BodyView() {
  127. return Listener(
  128. onPointerDown: (e) {
  129. pointerMove = e.position.dy;
  130. isCan = true;
  131. },
  132. onPointerMove: (e) {
  133. scOffset = _sc.hasClients ? _sc.offset : 0; //滑动控制器是否绑定
  134. if (e.position.dy - pointerMove > 36 && isCan) {
  135. // print("手指下滑触发 -- ");
  136. isCan = false;
  137. if (_panel3state.value == Panel3State.OPEN && scOffset <= 0) {
  138. _panel3state.value = Panel3State.CENTER;
  139. } else if (_panel3state.value == Panel3State.CENTER) {
  140. _panel3state.value = Panel3State.CLOSE;
  141. }
  142. } else if (e.position.dy - pointerMove < -36 && isCan) {
  143. // print("手指上滑触发 -- ");
  144. isCan = false;
  145. if (_panel3state.value == Panel3State.CLOSE) {
  146. _panel3state.value = Panel3State.CENTER;
  147. } else if (_panel3state.value == Panel3State.CENTER) {
  148. _panel3state.value = Panel3State.OPEN;
  149. }
  150. }
  151. },
  152. onPointerUp: (e) {
  153. isCan = true;
  154. },
  155. child: Container(child: widget.bodyWidget!(_sc , _physics),),
  156. );
  157. }
  158. }

使用:

  1. import 'package:flutter/material.dart';
  2. openTestSlidingPanel3Page(BuildContext context) {
  3. Navigator.push(context, CupertinoPageRoute(builder: (BuildContext context) {
  4. return TestSlidingPanel3Page();
  5. }));
  6. }
  7. class TestSlidingPanel3Page extends StatefulWidget {
  8. const TestSlidingPanel3Page({Key? key}) : super(key: key);
  9. @override
  10. State<TestSlidingPanel3Page> createState() => _TestSlidingPanel3PageState();
  11. }
  12. class _TestSlidingPanel3PageState extends State<TestSlidingPanel3Page> {
  13. SliverPanel3Controller slidingPanel3Controller = SliverPanel3Controller();
  14. @override
  15. Widget build(BuildContext context) {
  16. return Scaffold(
  17. body: Stack(
  18. children: [
  19. InkWell(
  20. onTap: (){
  21. slidingPanel3Controller.setPanel3State(Panel3State.CLOSE);
  22. },
  23. child: Container(
  24. width: double.infinity,
  25. height: double.infinity,
  26. color: Colors.blue.withAlpha(88),
  27. ),
  28. ),
  29. Align(
  30. alignment: AlignmentDirectional.bottomCenter,
  31. child: SliverPanel3View(
  32. heightClose: ScreenUtils.getDip(108),
  33. heightCenter: ScreenUtils.getDip(330),
  34. heightOpen: ScreenUtils.getDip(680),
  35. headWidget: headView(),
  36. bodyWidget: (ScrollController sc , ScrollPhysics? physics){
  37. return BodyView(sc , physics);
  38. },
  39. sliverPanel3Controller: slidingPanel3Controller,
  40. initPanel3state: Panel3State.CENTER,
  41. )),
  42. ],
  43. ),
  44. );
  45. }
  46. Widget headView() {
  47. return Container(
  48. height: ScreenUtils.getDip(108),
  49. width: ScreenUtils.getScreenWidth(),
  50. padding: ScreenUtils.edge(20, 10, 20, 0),
  51. decoration: const BoxDecoration(
  52. color: Colors.white,
  53. borderRadius: BorderRadius.only(
  54. topLeft: Radius.circular(8),
  55. topRight: Radius.circular(8),
  56. )),
  57. child: Column(
  58. mainAxisSize: MainAxisSize.min,
  59. children: [
  60. cuttingHorizontalSliver(),
  61. SizedBox(
  62. height: ScreenUtils.getDip(12),
  63. ),
  64. InkWell(
  65. onTap: (){
  66. slidingPanel3Controller.setPanel3State(Panel3State.EXIT);
  67. LocationPluginUtils.get().then((value) {
  68. SGMLogger.info(value);
  69. });
  70. },
  71. child: Container(
  72. margin: ScreenUtils.getMargin20(value: 4),
  73. width: double.infinity,
  74. height: ScreenUtils.getDip(40),
  75. alignment: AlignmentDirectional.center,
  76. decoration: BoxDecoration(
  77. color: color_f6f6f6,
  78. borderRadius: BorderRadius.circular(ScreenUtils.getDip(20)),
  79. ),
  80. child: Row(
  81. mainAxisSize: MainAxisSize.min,
  82. children: [
  83. Icon(Icons.search , color: Colors.grey.withAlpha(80),size: 20,),
  84. const SizedBox(
  85. width: 5,
  86. ),
  87. Text(
  88. 'Search',
  89. style: TextStyle(
  90. color: color_00131D.withOpacity(.3),
  91. fontSize: ScreenUtils.getSp(14),
  92. fontWeight: FontWeight.w400),
  93. ),
  94. ],
  95. ),
  96. ),
  97. ),
  98. Container(
  99. height: ScreenUtils.getDip(40),
  100. child: Row(
  101. children: [
  102. Icon(Icons.ac_unit , color: Colors.redAccent ,size: 20,),
  103. const SizedBox(
  104. width: 8,
  105. ),
  106. Expanded(
  107. child: Text(
  108. '惊喜来袭!!! 森林公园免门票 快冲...',
  109. maxLines: 1,
  110. overflow: TextOverflow.ellipsis,
  111. style: TextStyle(
  112. color: Colors.redAccent,
  113. height: 1.2,
  114. fontSize: ScreenUtils.getSp(14),
  115. fontWeight: FontWeight.w400),
  116. ),
  117. ),
  118. ],
  119. ),
  120. ),
  121. ],
  122. ),
  123. );
  124. }
  125. Widget BodyView(ScrollController sc , ScrollPhysics? physics) {
  126. // return Container(height: 999,width: 380, color: Colors.yellowAccent,);
  127. Widget _itemView(int i) {
  128. return Container(
  129. color: color_fff,
  130. padding: ScreenUtils.edge(20, 21, 20, 0),
  131. height: ScreenUtils.getDip(94),
  132. child: Row(
  133. children: [
  134. Icon(Icons.add_a_photo_rounded , color: Colors.orange, size: 66,),
  135. const SizedBox(
  136. width: 18,
  137. ),
  138. Expanded(
  139. child: Column(
  140. crossAxisAlignment: CrossAxisAlignment.start,
  141. children: [
  142. Text(
  143. '森林公园游乐场',
  144. maxLines: 1,
  145. overflow: TextOverflow.ellipsis,
  146. style: TextStyle(
  147. color: color_00131D,
  148. fontSize: ScreenUtils.getSp(16),
  149. fontWeight: FontWeight.w500),
  150. ),
  151. const SizedBox(
  152. height: 8,
  153. ),
  154. Text(
  155. '景点热度
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/628182
    推荐阅读
    相关标签