my_flexible_space_bar.dart 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. import 'package:flutter/foundation.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter/widgets.dart';
  7. /// The part of a material design [AppBar] that expands and collapses.
  8. ///
  9. /// Most commonly used in in the [SliverAppBar.flexibleSpace] field, a flexible
  10. /// space bar expands and contracts as the app scrolls so that the [AppBar]
  11. /// reaches from the top of the app to the top of the scrolling contents of the
  12. /// app.
  13. ///
  14. /// The widget that sizes the [AppBar] must wrap it in the widget returned by
  15. /// [FlexibleSpaceBar.createSettings], to convey sizing information down to the
  16. /// [FlexibleSpaceBar].
  17. ///
  18. /// See also:
  19. ///
  20. /// * [SliverAppBar], which implements the expanding and contracting.
  21. /// * [AppBar], which is used by [SliverAppBar].
  22. /// * <https://material.io/design/components/app-bars-top.html#behavior>
  23. class MyFlexibleSpaceBar extends StatefulWidget {
  24. /// Creates a flexible space bar.
  25. ///
  26. /// Most commonly used in the [AppBar.flexibleSpace] field.
  27. const MyFlexibleSpaceBar({
  28. Key key,
  29. this.title,
  30. this.background,
  31. this.centerTitle,
  32. this.titlePadding,
  33. this.collapseMode = CollapseMode.parallax,
  34. }) : assert(collapseMode != null),
  35. super(key: key);
  36. /// The primary contents of the flexible space bar when expanded.
  37. ///
  38. /// Typically a [Text] widget.
  39. final Widget title;
  40. /// Shown behind the [title] when expanded.
  41. ///
  42. /// Typically an [Image] widget with [Image.fit] set to [BoxFit.cover].
  43. final Widget background;
  44. /// Whether the title should be centered.
  45. ///
  46. /// By default this property is true if the current target platform
  47. /// is [TargetPlatform.iOS], false otherwise.
  48. final bool centerTitle;
  49. /// Collapse effect while scrolling.
  50. ///
  51. /// Defaults to [MyCollapseMode.parallax].
  52. final CollapseMode collapseMode;
  53. /// Defines how far the [title] is inset from either the widget's
  54. /// bottom-left or its center.
  55. ///
  56. /// Typically this property is used to adjust how far the title is
  57. /// is inset from the bottom-left and it is specified along with
  58. /// [centerTitle] false.
  59. ///
  60. /// By default the value of this property is
  61. /// `EdgeInsetsDirectional.only(start: 72, bottom: 16)` if the title is
  62. /// not centered, `EdgeInsetsDirectional.only(start 0, bottom: 16)` otherwise.
  63. final EdgeInsetsGeometry titlePadding;
  64. /// Wraps a widget that contains an [AppBar] to convey sizing information down
  65. /// to the [FlexibleSpaceBar].
  66. ///
  67. /// Used by [Scaffold] and [SliverAppBar].
  68. ///
  69. /// `toolbarOpacity` affects how transparent the text within the toolbar
  70. /// appears. `minExtent` sets the minimum height of the resulting
  71. /// [FlexibleSpaceBar] when fully collapsed. `maxExtent` sets the maximum
  72. /// height of the resulting [FlexibleSpaceBar] when fully expanded.
  73. /// `currentExtent` sets the scale of the [FlexibleSpaceBar.background] and
  74. /// [FlexibleSpaceBar.title] widgets of [FlexibleSpaceBar] upon
  75. /// initialization.
  76. ///
  77. /// See also:
  78. ///
  79. /// * [FlexibleSpaceBarSettings] which creates a settings object that can be
  80. /// used to specify these settings to a [FlexibleSpaceBar].
  81. static Widget createSettings({
  82. double toolbarOpacity,
  83. double minExtent,
  84. double maxExtent,
  85. @required double currentExtent,
  86. @required Widget child,
  87. }) {
  88. assert(currentExtent != null);
  89. return FlexibleSpaceBarSettings(
  90. toolbarOpacity: toolbarOpacity ?? 1.0,
  91. minExtent: minExtent ?? currentExtent,
  92. maxExtent: maxExtent ?? currentExtent,
  93. currentExtent: currentExtent,
  94. child: child,
  95. );
  96. }
  97. @override
  98. _FlexibleSpaceBarState createState() => _FlexibleSpaceBarState();
  99. }
  100. class _FlexibleSpaceBarState extends State<MyFlexibleSpaceBar> {
  101. bool _getEffectiveCenterTitle(ThemeData theme) {
  102. if (widget.centerTitle != null)
  103. return widget.centerTitle;
  104. assert(theme.platform != null);
  105. switch (theme.platform) {
  106. case TargetPlatform.android:
  107. case TargetPlatform.fuchsia:
  108. return false;
  109. case TargetPlatform.iOS:
  110. return true;
  111. }
  112. return null;
  113. }
  114. Alignment _getTitleAlignment(bool effectiveCenterTitle) {
  115. if (effectiveCenterTitle)
  116. return Alignment.bottomCenter;
  117. final TextDirection textDirection = Directionality.of(context);
  118. assert(textDirection != null);
  119. switch (textDirection) {
  120. case TextDirection.rtl:
  121. return Alignment.bottomRight;
  122. case TextDirection.ltr:
  123. return Alignment.bottomLeft;
  124. }
  125. return null;
  126. }
  127. double _getCollapsePadding(double t, FlexibleSpaceBarSettings settings) {
  128. switch (widget.collapseMode) {
  129. case CollapseMode.pin:
  130. return -(settings.maxExtent - settings.currentExtent);
  131. case CollapseMode.none:
  132. return 0.0;
  133. case CollapseMode.parallax:
  134. final double deltaExtent = settings.maxExtent - settings.minExtent;
  135. return -Tween<double>(begin: 0.0, end: deltaExtent / 4.0).transform(t);
  136. }
  137. return null;
  138. }
  139. GlobalKey _key = GlobalKey();
  140. double _offset = 0;
  141. @override
  142. void initState() {
  143. //监听Widget是否绘制完毕
  144. WidgetsBinding.instance.addPostFrameCallback((_){
  145. final RenderBox renderBoxRed = _key.currentContext.findRenderObject();
  146. _offset = renderBoxRed.size.width / 2;
  147. });
  148. super.initState();
  149. }
  150. @override
  151. Widget build(BuildContext context) {
  152. Size size = MediaQuery.of(context).size;
  153. final FlexibleSpaceBarSettings settings = context.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>();
  154. assert(settings != null, 'A FlexibleSpaceBar must be wrapped in the widget returned by FlexibleSpaceBar.createSettings().');
  155. final List<Widget> children = <Widget>[];
  156. final double deltaExtent = settings.maxExtent - settings.minExtent;
  157. // 0.0 -> Expanded
  158. // 1.0 -> Collapsed to toolbar
  159. final double t = (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0);
  160. // background image
  161. // if (widget.background != null) {
  162. // final double fadeStart = math.max(0.0, 1.0 - kToolbarHeight / deltaExtent);
  163. // const double fadeEnd = 1.0;
  164. // assert(fadeStart <= fadeEnd);
  165. // final double opacity = 1.0 - Interval(fadeStart, fadeEnd).transform(t);
  166. // if (opacity > 0.0) {
  167. children.add(Positioned(
  168. top: _getCollapsePadding(t, settings),
  169. left: 0.0,
  170. right: 0.0,
  171. height: settings.maxExtent,
  172. child: Opacity(
  173. opacity: 1,
  174. child: widget.background,
  175. ),
  176. ));
  177. // }
  178. // }
  179. if (widget.title != null) {
  180. Widget title;
  181. switch (defaultTargetPlatform) {
  182. case TargetPlatform.iOS:
  183. title = widget.title;
  184. break;
  185. case TargetPlatform.fuchsia:
  186. case TargetPlatform.android:
  187. title = Semantics(
  188. namesRoute: true,
  189. child: widget.title,
  190. );
  191. }
  192. title = Container(
  193. key: _key,
  194. child: title,
  195. );
  196. final ThemeData theme = Theme.of(context);
  197. final double opacity = settings.toolbarOpacity;
  198. if (opacity > 0.0) {
  199. TextStyle titleStyle = theme.primaryTextTheme.title;
  200. titleStyle = titleStyle.copyWith(
  201. color: titleStyle.color.withOpacity(opacity),
  202. fontWeight: t != 0 ? FontWeight.normal : FontWeight.bold
  203. );
  204. final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme);
  205. final EdgeInsetsGeometry padding = widget.titlePadding ??
  206. EdgeInsetsDirectional.only(
  207. start: effectiveCenterTitle ? 0.0 : 72.0,
  208. bottom: 16.0,
  209. );
  210. final double scaleValue = Tween<double>(begin: 1.5, end: 1.0).transform(t);
  211. double width = (size.width - 32.0) / 2 - _offset;
  212. final Matrix4 scaleTransform = Matrix4.identity()
  213. ..scale(scaleValue, scaleValue, 1.0)..translate(t * width, 0.0);
  214. final Alignment titleAlignment = _getTitleAlignment(false);
  215. children.add(Container(
  216. padding: padding,
  217. child: Transform(
  218. alignment: titleAlignment,
  219. transform: scaleTransform,
  220. child: Align(
  221. alignment: titleAlignment,
  222. child: DefaultTextStyle(
  223. style: titleStyle,
  224. child: title,
  225. ),
  226. ),
  227. ),
  228. ));
  229. }
  230. }
  231. return ClipRect(child: Stack(children: children));
  232. }
  233. }