Skip to content

Commit 3b957a0

Browse files
committed
增加详情页动态效果
1 parent f86b53b commit 3b957a0

File tree

5 files changed

+819
-27
lines changed

5 files changed

+819
-27
lines changed

lib/page/repostory_detail_info_page.dart

Lines changed: 147 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import 'dart:async';
2+
import 'dart:math' as math;
23

34
import 'package:flutter/material.dart';
5+
import 'package:flutter/rendering.dart';
46
import 'package:gsy_github_app_flutter/common/dao/repos_dao.dart';
57
import 'package:gsy_github_app_flutter/common/model/RepoCommit.dart';
68
import 'package:gsy_github_app_flutter/common/model/Repository.dart';
9+
import 'package:gsy_github_app_flutter/common/utils/common_utils.dart';
710
import 'package:gsy_github_app_flutter/common/utils/event_utils.dart';
811
import 'package:gsy_github_app_flutter/common/utils/navigator_utils.dart';
912
import 'package:gsy_github_app_flutter/page/repository_detail_page.dart';
1013
import 'package:gsy_github_app_flutter/widget/event_item.dart';
1114
import 'package:gsy_github_app_flutter/widget/gsy_common_option_widget.dart';
1215
import 'package:gsy_github_app_flutter/widget/gsy_list_state.dart';
1316
import 'package:gsy_github_app_flutter/widget/gsy_pull_load_widget.dart';
17+
import 'package:gsy_github_app_flutter/widget/gsy_select_item_widget.dart';
18+
import 'package:gsy_github_app_flutter/widget/nested/gsy_nested_pull_load_widget.dart';
19+
import 'package:gsy_github_app_flutter/widget/nested/nested_refresh.dart';
1420
import 'package:gsy_github_app_flutter/widget/repos_header_item.dart';
1521
import 'package:scoped_model/scoped_model.dart';
1622

@@ -37,41 +43,41 @@ class ReposDetailInfoPage extends StatefulWidget {
3743
class ReposDetailInfoPageState extends State<ReposDetailInfoPage>
3844
with
3945
AutomaticKeepAliveClientMixin<ReposDetailInfoPage>,
40-
GSYListState<ReposDetailInfoPage> {
46+
GSYListState<ReposDetailInfoPage>,
47+
TickerProviderStateMixin {
4148
int selectIndex = 0;
4249

4350
ReposDetailInfoPageState();
4451

52+
final GlobalKey<NestedScrollViewRefreshIndicatorState> refreshIKey =
53+
new GlobalKey<NestedScrollViewRefreshIndicatorState>();
54+
55+
@override
56+
showRefreshLoading() {
57+
new Future.delayed(const Duration(seconds: 0), () {
58+
refreshIKey.currentState.show().then((e) {});
59+
return true;
60+
});
61+
}
62+
4563
///渲染时间Item或者提交Item
4664
_renderEventItem(index) {
47-
if (index == 0) {
48-
return new ReposHeaderItem(
49-
ReposHeaderViewModel.fromHttpMap(widget.userName, widget.reposName,
50-
ReposDetailModel.of(context).repository), (index) {
51-
selectIndex = index;
52-
clearData();
53-
showRefreshLoading();
54-
});
55-
}
56-
5765
if (selectIndex == 1) {
5866
///提交
5967
return new EventItem(
60-
EventViewModel.fromCommitMap(pullLoadWidgetControl.dataList[index - 1]),
68+
EventViewModel.fromCommitMap(pullLoadWidgetControl.dataList[index]),
6169
onPressed: () {
62-
RepoCommit model = pullLoadWidgetControl.dataList[index - 1];
70+
RepoCommit model = pullLoadWidgetControl.dataList[index];
6371
NavigatorUtils.goPushDetailPage(
6472
context, widget.userName, widget.reposName, model.sha, false);
6573
},
6674
needImage: false,
6775
);
6876
}
6977
return new EventItem(
70-
EventViewModel.fromEventMap(pullLoadWidgetControl.dataList[index - 1]),
78+
EventViewModel.fromEventMap(pullLoadWidgetControl.dataList[index]),
7179
onPressed: () {
72-
EventUtils.ActionUtils(
73-
context,
74-
pullLoadWidgetControl.dataList[index - 1],
80+
EventUtils.ActionUtils(context, pullLoadWidgetControl.dataList[index],
7581
widget.userName + "/" + widget.reposName);
7682
},
7783
);
@@ -138,21 +144,142 @@ class ReposDetailInfoPageState extends State<ReposDetailInfoPage>
138144
bool get isRefreshFirst => true;
139145

140146
@override
141-
bool get needHeader => true;
147+
bool get needHeader => false;
148+
149+
AnimationController animationController;
150+
151+
@override
152+
void initState() {
153+
super.initState();
154+
animationController = new AnimationController(vsync: this);
155+
}
142156

143157
@override
144158
Widget build(BuildContext context) {
145159
super.build(context);
146160
return ScopedModelDescendant<ReposDetailModel>(
147161
builder: (context, child, model) {
148-
return GSYPullLoadWidget(
162+
return GSYNestedPullLoadWidget(
149163
pullLoadWidgetControl,
150164
(BuildContext context, int index) => _renderEventItem(index),
151165
handleRefresh,
152166
onLoadMore,
153-
refreshKey: refreshIndicatorKey,
167+
refreshKey: refreshIKey,
168+
headerSliverBuilder: (context, _) {
169+
return _sliverBuilder(context, _);
170+
},
154171
);
155172
},
156173
);
157174
}
175+
176+
List<Widget> _sliverBuilder(BuildContext context, bool innerBoxIsScrolled) {
177+
return <Widget>[
178+
SliverPersistentHeader(
179+
delegate: _InfoHeaderDelegate(
180+
maxHeight: 300,
181+
minHeight: 300,
182+
snapConfig: FloatingHeaderSnapConfiguration(
183+
vsync: this,
184+
curve: Curves.bounceInOut,
185+
duration: const Duration(milliseconds: 10),
186+
),
187+
child: new ReposHeaderItem(
188+
ReposHeaderViewModel.fromHttpMap(
189+
widget.userName,
190+
widget.reposName,
191+
ReposDetailModel.of(context).repository), (index) {
192+
selectIndex = index;
193+
clearData();
194+
showRefreshLoading();
195+
})),
196+
),
197+
SliverPersistentHeader(
198+
pinned: true,
199+
delegate: _InfoHeaderDelegate(
200+
maxHeight: 70,
201+
minHeight: 70,
202+
changeSize: true,
203+
snapConfig: FloatingHeaderSnapConfiguration(
204+
vsync: this,
205+
curve: Curves.bounceInOut,
206+
duration: const Duration(milliseconds: 10),
207+
),
208+
builder: (BuildContext context, double shrinkOffset,
209+
bool overlapsContent) {
210+
var lr = 10 - shrinkOffset / 70 * 10;
211+
var radius = Radius.circular(4 - shrinkOffset / 70 * 4);
212+
return SizedBox.expand(
213+
child: Padding(
214+
padding: EdgeInsets.only(bottom: 20, left: lr, right: lr),
215+
child: new GSYSelectItemWidget(
216+
[
217+
CommonUtils.getLocale(context).repos_tab_activity,
218+
CommonUtils.getLocale(context).repos_tab_commits,
219+
],
220+
(index) {
221+
selectIndex = index;
222+
clearData();
223+
showRefreshLoading();
224+
},
225+
margin: EdgeInsets.zero,
226+
shape: new RoundedRectangleBorder(
227+
borderRadius: BorderRadius.all(radius),
228+
),
229+
),
230+
),
231+
);
232+
}),
233+
),
234+
];
235+
}
158236
}
237+
238+
///动态头部处理
239+
class _InfoHeaderDelegate extends SliverPersistentHeaderDelegate {
240+
_InfoHeaderDelegate(
241+
{@required this.minHeight,
242+
@required this.maxHeight,
243+
@required this.snapConfig,
244+
this.child,
245+
this.builder,
246+
this.changeSize = false});
247+
248+
final double minHeight;
249+
final double maxHeight;
250+
final Widget child;
251+
final Builder builder;
252+
final bool changeSize;
253+
final FloatingHeaderSnapConfiguration snapConfig;
254+
AnimationController animationController;
255+
256+
@override
257+
double get minExtent => minHeight;
258+
259+
@override
260+
double get maxExtent => math.max(maxHeight, minHeight);
261+
262+
@override
263+
Widget build(
264+
BuildContext context, double shrinkOffset, bool overlapsContent) {
265+
if (builder != null) {
266+
return builder(context, shrinkOffset, overlapsContent);
267+
}
268+
return SizedBox.expand(
269+
child: child,
270+
);
271+
}
272+
273+
@override
274+
bool shouldRebuild(_InfoHeaderDelegate oldDelegate) {
275+
return maxHeight != oldDelegate.maxHeight ||
276+
minHeight != oldDelegate.minHeight ||
277+
child != oldDelegate.child;
278+
}
279+
280+
@override
281+
FloatingHeaderSnapConfiguration get snapConfiguration => snapConfig;
282+
}
283+
284+
typedef Widget Builder(
285+
BuildContext context, double shrinkOffset, bool overlapsContent);

lib/widget/gsy_select_item_widget.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class GSYSelectItemWidget extends StatefulWidget implements PreferredSizeWidget
1515

1616
final SelectItemChanged selectItemChanged;
1717

18+
final RoundedRectangleBorder shape;
19+
1820
final double elevation;
1921

2022
final double height;
@@ -26,6 +28,7 @@ class GSYSelectItemWidget extends StatefulWidget implements PreferredSizeWidget
2628
this.selectItemChanged, {
2729
this.elevation = 5.0,
2830
this.height = 70.0,
31+
this.shape,
2932
this.margin = const EdgeInsets.all(10.0),
3033
});
3134

@@ -85,7 +88,7 @@ class _GSYSelectItemWidgetState extends State<GSYSelectItemWidget> {
8588
elevation: widget.elevation,
8689
margin: widget.margin,
8790
color: Theme.of(context).primaryColor,
88-
shape: new RoundedRectangleBorder(
91+
shape: widget.shape ?? new RoundedRectangleBorder(
8992
borderRadius: BorderRadius.all(Radius.circular(4.0)),
9093
),
9194
child: new Row(

0 commit comments

Comments
 (0)