@@ -22,19 +22,16 @@ class _ClubDetailState extends State<ClubDetail> {
2222 late ScrollController _scrollController;
2323 late Future <String > _content;
2424
25- static const _expandedHeight = 300.0 ;
25+ static const _expandedHeight = 280.0 ;
26+ static const _maxWidth = 600.0 ;
2627
27- bool _isShrunk = false ;
28+ double _opacity = 0.0 ;
2829
2930 void _scrollListener () {
30- final shouldBeShrunk = _scrollController.hasClients &&
31- _scrollController.offset > (_expandedHeight - kToolbarHeight);
32-
33- if (shouldBeShrunk != _isShrunk) {
34- setState (() {
35- _isShrunk = shouldBeShrunk;
36- });
37- }
31+ _opacity = (_scrollController.offset / (_expandedHeight - kToolbarHeight));
32+ if (_opacity < 0.0 ) _opacity = 0.0 ;
33+ if (_opacity >= 1.0 ) _opacity = 1.0 ;
34+ setState (() {});
3835 }
3936
4037 @override
@@ -54,135 +51,164 @@ class _ClubDetailState extends State<ClubDetail> {
5451
5552 @override
5653 Widget build (BuildContext context) {
57- return Scaffold (
58- body: CustomScrollView (
59- controller: _scrollController,
60- slivers: [
61- SliverAppBar (
62- expandedHeight: _expandedHeight,
63- centerTitle: false ,
64- pinned: true ,
65- title: Visibility (
66- visible: _isShrunk,
67- child: Text (widget.info.title),
68- ),
69- elevation: 0 ,
70- //leading: const BackButton(),
71- flexibleSpace: FlexibleSpaceBar (
72- background: Column (
73- crossAxisAlignment: CrossAxisAlignment .center,
74- mainAxisAlignment: MainAxisAlignment .center,
75- children: [
76- Image (
77- image: widget.info.icon,
78- height: 100 ,
79- width: 100 ,
80- ).clipOval (),
81- const SizedBox (
82- height: 10 ,
83- ),
84- Text (
85- widget.info.title,
86- ),
87- const SizedBox (
88- height: 10 ,
89- ),
90- Text (
91- widget.info.intro,
92- ),
93- const SizedBox (
94- height: 10 ,
95- ),
96- Text (
97- widget.info.typeList
98- .map <String >((type) => switch (type) {
99- ClubType .tech => "技术" ,
100- ClubType .acg => "晒你系" ,
101- ClubType .union => "官方" ,
102- ClubType .profit => "商业" ,
103- ClubType .sport => "体育" ,
104- ClubType .art => "文化" ,
105- ClubType .unknown => "未知" ,
106- })
107- .join ("; " ),
108- //style: TextStyle(fontSize: 14),
109- ),
110- ],
111- ).padding (top: kToolbarHeight),
112- ),
54+ return Theme (
55+ data: ThemeData (
56+ colorScheme: ColorScheme .fromSeed (
57+ seedColor: Colors .yellow,
58+ brightness: Theme .of (context).brightness,
11359 ),
114- SliverToBoxAdapter (
115- child: ConstrainedBox (
116- constraints: BoxConstraints (
117- minHeight: MediaQuery .of (context).size.height,
118- ),
119- child: Material (
120- elevation: 7 ,
121- child: [
122- [
123- TextButton (
124- child: Text ("QQ" ),
125- onPressed: () async {
60+ ),
61+ child: Scaffold (
62+ body: CustomScrollView (
63+ controller: _scrollController,
64+ slivers: [
65+ SliverAppBar (
66+ expandedHeight: _expandedHeight,
67+ centerTitle: false ,
68+ pinned: true ,
69+ title: Opacity (
70+ opacity: _opacity,
71+ child: Text (widget.info.title),
72+ ),
73+ elevation: 0 ,
74+ //leading: const BackButton(),
75+ flexibleSpace: FlexibleSpaceBar (
76+ background: Column (
77+ crossAxisAlignment: CrossAxisAlignment .center,
78+ mainAxisAlignment: MainAxisAlignment .center,
79+ children: [
80+ Image (
81+ image: widget.info.icon,
82+ height: 100 ,
83+ width: 100 ,
84+ fit: BoxFit .fill,
85+ ).clipOval (),
86+ const SizedBox (height: 16 ),
87+ Text (
88+ widget.info.title,
89+ style: TextStyle (
90+ fontSize: 16 ,
91+ fontWeight: FontWeight .bold,
92+ ),
93+ ),
94+ const SizedBox (height: 8 ),
95+ Text (widget.info.intro),
96+ const SizedBox (height: 6 ),
97+ Row (
98+ mainAxisAlignment: MainAxisAlignment .center,
99+ children: widget.info.typeList
100+ .map <Widget >((type) => TagsBoxes (
101+ text: switch (type) {
102+ ClubType .tech => "技术" ,
103+ ClubType .acg => "晒你系" ,
104+ ClubType .union => "官方" ,
105+ ClubType .profit => "商业" ,
106+ ClubType .sport => "体育" ,
107+ ClubType .art => "文化" ,
108+ ClubType .unknown => "未知" ,
109+ }))
110+ .toList (),
111+ )
112+ ],
113+ ).padding (top: kToolbarHeight, bottom: 46 ),
114+ ),
115+ bottom: PreferredSize (
116+ preferredSize: Size .fromHeight (46.0 ),
117+ child: [
118+ InkWell (
119+ onTap: () async {
126120 await Clipboard .setData (
127121 ClipboardData (text: widget.info.qq));
128122 if (context.mounted) {
129123 showToast (context: context, msg: "QQ 号已经复制到剪贴板" );
130124 }
131125 },
132- ),
133- TextButton (
134- child: Text ("邀请链接" ),
135- onPressed: () async {
126+ child: Ink (
127+ height: 46.0 ,
128+ child: Text (
129+ "QQ" ,
130+ style: TextStyle (
131+ fontSize: 16 ,
132+ fontWeight: FontWeight .w500,
133+ ),
134+ ).center (),
135+ ),
136+ ).expanded (),
137+ InkWell (
138+ onTap: () async {
136139 if (widget.info.qqlink.isEmpty) {
137140 showToast (context: context, msg: "未提供入群链接" );
138141 }
139142 launchUrlString (widget.info.qqlink);
140143 },
141- )
142- ].toRow (mainAxisAlignment: MainAxisAlignment .spaceAround),
143- Divider (color: Colors .transparent),
144- if (widget.info.pic > 0 ) ...[
145- ListView .builder (
146- scrollDirection: Axis .horizontal,
147- shrinkWrap: true ,
148- physics: ClampingScrollPhysics (),
149- itemCount: widget.info.pic,
150- itemBuilder: (context, index) => CachedNetworkImage (
151- imageUrl: getClubImage (widget.info.code, index),
152- height: 300 ,
153- ).clipRRect (all: 12 ).padding (horizontal: 4 ),
154- ).clipRRect (all: 12 ).constrained (height: 300 ),
155- Divider (color: Colors .transparent),
156- ],
157- SelectionArea (
158- child: FutureBuilder <String >(
159- future: _content,
160- builder: (context, snapshot) {
161- if (snapshot.connectionState == ConnectionState .done) {
162- try {
163- return HtmlWidget (
164- snapshot.data ?? '''<p>加载遇到问题</p>''' ,
165- ).padding (horizontal: 4 );
166- } catch (e) {
167- return ReloadWidget (
168- function: () {
169- setState (() {
170- _content = getClubArticle (widget.info.code);
171- });
172- },
144+ child: Ink (
145+ height: 46.0 ,
146+ child: Text (
147+ "邀请链接" ,
148+ style: TextStyle (
149+ fontSize: 16 ,
150+ fontWeight: FontWeight .w500,
151+ ),
152+ ).center (),
153+ ),
154+ ).expanded ()
155+ ]
156+ .toRow (mainAxisAlignment: MainAxisAlignment .spaceAround)
157+ .constrained (maxWidth: _maxWidth),
158+ ),
159+ ),
160+ SliverToBoxAdapter (
161+ child: ConstrainedBox (
162+ constraints: BoxConstraints (
163+ minHeight: MediaQuery .of (context).size.height,
164+ ),
165+ child: [
166+ if (widget.info.pic > 0 ) ...[
167+ ListView .builder (
168+ scrollDirection: Axis .horizontal,
169+ shrinkWrap: true ,
170+ physics: ClampingScrollPhysics (),
171+ itemCount: widget.info.pic,
172+ itemBuilder: (context, index) => CachedNetworkImage (
173+ imageUrl: getClubImage (widget.info.code, index),
174+ height: 300 ,
175+ ).clipRRect (all: 12 ).padding (horizontal: 4 ),
176+ )
177+ .clipRRect (all: 12 )
178+ .constrained (height: 300 , maxWidth: 600 ),
179+ Divider (color: Colors .transparent),
180+ ],
181+ SelectionArea (
182+ child: FutureBuilder <String >(
183+ future: _content,
184+ builder: (context, snapshot) {
185+ if (snapshot.connectionState == ConnectionState .done) {
186+ try {
187+ return HtmlWidget (
188+ snapshot.data ?? '''<p>加载遇到问题</p>''' ,
189+ ).padding (horizontal: 4 );
190+ } catch (e) {
191+ return ReloadWidget (
192+ function: () {
193+ setState (() {
194+ _content = getClubArticle (widget.info.code);
195+ });
196+ },
197+ );
198+ }
199+ } else {
200+ return const Center (
201+ child: CircularProgressIndicator (),
173202 );
174203 }
175- } else {
176- return const Center (child: CircularProgressIndicator ());
177- }
178- },
179- )),
180- ].toColumn (),
204+ },
205+ )).constrained (maxWidth: _maxWidth),
206+ Divider (color: Colors .transparent),
207+ ].toColumn ().padding (horizontal: 8 ),
208+ ),
181209 ),
182- ),
183- )
184- ],
185- ),
186- );
210+ ],
211+ ),
212+ ));
187213 }
188214}
0 commit comments