flexible_bar.dart 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import 'package:flutter/material.dart';
  2. class FlexibleDetailBar extends StatelessWidget {
  3. ///the content of bar
  4. ///scroll with the parent ScrollView
  5. final Widget content;
  6. ///the background of bar
  7. ///scroll in parallax
  8. final Widget background;
  9. ///custom content interaction with t
  10. ///[t] 0.0 -> Expanded 1.0 -> Collapsed to toolbar
  11. final Widget Function(BuildContext context, double t) builder;
  12. static double percentage(BuildContext context) {
  13. _FlexibleDetail value = context.dependOnInheritedWidgetOfExactType<_FlexibleDetail>();
  14. assert(value != null, 'ooh , can not find');
  15. return value.t;
  16. }
  17. const FlexibleDetailBar({
  18. Key key,
  19. @required this.content,
  20. this.builder,
  21. @required this.background,
  22. }) : assert(content != null),
  23. assert(background != null),
  24. super(key: key);
  25. @override
  26. Widget build(BuildContext context) {
  27. final FlexibleSpaceBarSettings settings = context.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>();
  28. final List<Widget> children = <Widget>[];
  29. final double deltaExtent = settings.maxExtent - settings.minExtent;
  30. // 0.0 -> Expanded
  31. // 1.0 -> Collapsed to toolbar
  32. final double t = (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0);
  33. //背景添加视差滚动效果
  34. children.add(Positioned(
  35. top: -Tween<double>(begin: 0.0, end: deltaExtent / 4.0).transform(t),
  36. left: 0,
  37. right: 0,
  38. height: settings.maxExtent,
  39. child: background,
  40. ));
  41. //为content 添加 底部的 padding
  42. double bottomPadding = 0;
  43. SliverAppBar sliverBar = context.findAncestorWidgetOfExactType<SliverAppBar>();
  44. if (sliverBar != null && sliverBar.bottom != null) {
  45. bottomPadding = sliverBar.bottom.preferredSize.height;
  46. }
  47. children.add(Positioned(
  48. top: settings.currentExtent - settings.maxExtent,
  49. left: 0,
  50. right: 0,
  51. height: settings.maxExtent,
  52. child: Opacity(
  53. opacity: 1 - t,
  54. child: Padding(
  55. padding: EdgeInsets.only(bottom: bottomPadding),
  56. child: Material(
  57. child: DefaultTextStyle(style: Theme.of(context).primaryTextTheme.bodyText2, child: content),
  58. elevation: 0,
  59. color: Colors.transparent),
  60. ),
  61. ),
  62. ));
  63. if (builder != null) {
  64. children.add(Column(children: <Widget>[builder(context, t)]));
  65. }
  66. return _FlexibleDetail(t,
  67. child: ClipRect(
  68. child: DefaultTextStyle(
  69. style: Theme.of(context).primaryTextTheme.bodyText2,
  70. child: Stack(children: children, fit: StackFit.expand))));
  71. }
  72. }
  73. class _FlexibleDetail extends InheritedWidget {
  74. ///0 : Expanded
  75. ///1 : Collapsed
  76. final double t;
  77. _FlexibleDetail(this.t, {Widget child}) : super(child: child);
  78. @override
  79. bool updateShouldNotify(_FlexibleDetail oldWidget) {
  80. return t != oldWidget.t;
  81. }
  82. }
  83. ///
  84. /// 用在 [FlexibleDetailBar.background]
  85. /// child上下滑动的时候会覆盖上黑色阴影
  86. ///
  87. class FlexShadowBackground extends StatelessWidget {
  88. final Widget child;
  89. const FlexShadowBackground({Key key, this.child}) : super(key: key);
  90. @override
  91. Widget build(BuildContext context) {
  92. var t = FlexibleDetailBar.percentage(context);
  93. t = Curves.ease.transform(t) / 2 + 0.2;
  94. return Container(
  95. foregroundDecoration: BoxDecoration(color: Colors.black.withOpacity(t)),
  96. child: child,
  97. );
  98. }
  99. }