@@ -16,7 +16,7 @@ import 'theme.dart';
16
16
/// * [CupertinoTabScaffold] , a similar widget for tabbed applications.
17
17
/// * [CupertinoPageRoute] , a modal page route that typically hosts a
18
18
/// [CupertinoPageScaffold] with support for iOS-style page transitions.
19
- class CupertinoPageScaffold extends StatelessWidget {
19
+ class CupertinoPageScaffold extends StatefulWidget {
20
20
/// Creates a layout for pages with a navigation bar at the top.
21
21
const CupertinoPageScaffold ({
22
22
Key key,
@@ -61,32 +61,51 @@ class CupertinoPageScaffold extends StatelessWidget {
61
61
/// Defaults to true and cannot be null.
62
62
final bool resizeToAvoidBottomInset;
63
63
64
+ @override
65
+ _CupertinoPageScaffoldState createState () => _CupertinoPageScaffoldState ();
66
+ }
67
+
68
+ class _CupertinoPageScaffoldState extends State <CupertinoPageScaffold > {
69
+ final ScrollController _primaryScrollController = ScrollController ();
70
+
71
+ void _handleStatusBarTap () {
72
+ // Only act on the scroll controller if it has any attached scroll positions.
73
+ if (_primaryScrollController.hasClients) {
74
+ _primaryScrollController.animateTo (
75
+ 0.0 ,
76
+ // Eyeballed from iOS.
77
+ duration: const Duration (milliseconds: 500 ),
78
+ curve: Curves .linearToEaseOut,
79
+ );
80
+ }
81
+ }
82
+
64
83
@override
65
84
Widget build (BuildContext context) {
66
85
final List <Widget > stacked = < Widget > [];
67
86
68
- Widget paddedContent = child;
69
- if (navigationBar != null ) {
70
- final MediaQueryData existingMediaQuery = MediaQuery .of (context);
87
+ Widget paddedContent = widget.child;
71
88
89
+ final MediaQueryData existingMediaQuery = MediaQuery .of (context);
90
+ if (widget.navigationBar != null ) {
72
91
// TODO(xster): Use real size after partial layout instead of preferred size.
73
92
// https://github.com/flutter/flutter/issues/12912
74
93
final double topPadding =
75
- navigationBar.preferredSize.height + existingMediaQuery.padding.top;
94
+ widget. navigationBar.preferredSize.height + existingMediaQuery.padding.top;
76
95
77
96
// Propagate bottom padding and include viewInsets if appropriate
78
- final double bottomPadding = resizeToAvoidBottomInset
97
+ final double bottomPadding = widget. resizeToAvoidBottomInset
79
98
? existingMediaQuery.viewInsets.bottom
80
99
: 0.0 ;
81
100
82
- final EdgeInsets newViewInsets = resizeToAvoidBottomInset
101
+ final EdgeInsets newViewInsets = widget. resizeToAvoidBottomInset
83
102
// The insets are consumed by the scaffolds and no longer exposed to
84
103
// the descendant subtree.
85
104
? existingMediaQuery.viewInsets.copyWith (bottom: 0.0 )
86
105
: existingMediaQuery.viewInsets;
87
106
88
107
final bool fullObstruction =
89
- navigationBar.fullObstruction ?? CupertinoTheme .of (context).barBackgroundColor.alpha == 0xFF ;
108
+ widget. navigationBar.fullObstruction ?? CupertinoTheme .of (context).barBackgroundColor.alpha == 0xFF ;
90
109
91
110
// If navigation bar is opaquely obstructing, directly shift the main content
92
111
// down. If translucent, let main content draw behind navigation bar but hint the
@@ -101,7 +120,7 @@ class CupertinoPageScaffold extends StatelessWidget {
101
120
),
102
121
child: Padding (
103
122
padding: EdgeInsets .only (top: topPadding, bottom: bottomPadding),
104
- child: child ,
123
+ child: paddedContent ,
105
124
),
106
125
);
107
126
} else {
@@ -114,27 +133,44 @@ class CupertinoPageScaffold extends StatelessWidget {
114
133
),
115
134
child: Padding (
116
135
padding: EdgeInsets .only (bottom: bottomPadding),
117
- child: child ,
136
+ child: paddedContent ,
118
137
),
119
138
);
120
139
}
121
140
}
122
141
123
142
// The main content being at the bottom is added to the stack first.
124
- stacked.add (paddedContent);
143
+ stacked.add (PrimaryScrollController (
144
+ controller: _primaryScrollController,
145
+ child: paddedContent,
146
+ ));
125
147
126
- if (navigationBar != null ) {
148
+ if (widget. navigationBar != null ) {
127
149
stacked.add (Positioned (
128
150
top: 0.0 ,
129
151
left: 0.0 ,
130
152
right: 0.0 ,
131
- child: navigationBar,
153
+ child: widget. navigationBar,
132
154
));
133
155
}
134
156
157
+ // Add a touch handler the size of the status bar on top of all contents
158
+ // to handle scroll to top by status bar taps.
159
+ stacked.add (Positioned (
160
+ top: 0.0 ,
161
+ left: 0.0 ,
162
+ right: 0.0 ,
163
+ height: existingMediaQuery.padding.top,
164
+ child: GestureDetector (
165
+ excludeFromSemantics: true ,
166
+ onTap: _handleStatusBarTap,
167
+ ),
168
+ ),
169
+ );
170
+
135
171
return DecoratedBox (
136
172
decoration: BoxDecoration (
137
- color: backgroundColor ?? CupertinoTheme .of (context).scaffoldBackgroundColor,
173
+ color: widget. backgroundColor ?? CupertinoTheme .of (context).scaffoldBackgroundColor,
138
174
),
139
175
child: Stack (
140
176
children: stacked,
0 commit comments