11import 'package:flutter/material.dart' ;
22import 'package:provider/provider.dart' ;
3+ import 'package:flutter_heatmap_calendar/flutter_heatmap_calendar.dart' ;
34import '../viewmodel/user_profile_viewmodel.dart' ;
45import 'widgets/profile_header.dart' ;
56import 'widgets/stat_card.dart' ;
@@ -44,7 +45,7 @@ class UserProfileView extends StatelessWidget {
4445 duration: const Duration (milliseconds: 300 ),
4546 child: vm.isLoading
4647 ? Center (
47- key: ValueKey ('loading' ),
48+ key: const ValueKey ('loading' ),
4849 child: CircularProgressIndicator (
4950 color: Theme .of (context).colorScheme.primary,
5051 ),
@@ -55,11 +56,11 @@ class UserProfileView extends StatelessWidget {
5556 child: Text ("Error loading profile" ),
5657 )
5758 : Builder (
58- builder: (innerContext) {
59- vm.syncThemeWithProfile (innerContext);
60- return _buildProfileContent (innerContext, vm);
61- },
62- ),
59+ builder: (innerContext) {
60+ vm.syncThemeWithProfile (innerContext);
61+ return _buildProfileContent (innerContext, vm);
62+ },
63+ ),
6364 );
6465 },
6566 ),
@@ -93,7 +94,7 @@ class UserProfileView extends StatelessWidget {
9394 child: StatCard (
9495 value: user.streaks.toString (),
9596 label: 'Streaks' ,
96- onTap: () {} ,
97+ onTap: () => _showHeatmapBottomSheet (context) ,
9798 ),
9899 ),
99100 ],
@@ -114,7 +115,7 @@ class UserProfileView extends StatelessWidget {
114115 activeColor: Theme .of (context).colorScheme.surface,
115116 activeTrackColor: Theme .of (context).colorScheme.primary,
116117 inactiveThumbColor:
117- Theme .of (context).colorScheme.onSurfaceVariant,
118+ Theme .of (context).colorScheme.onSurfaceVariant,
118119 inactiveTrackColor: Theme .of (context).colorScheme.outline,
119120 onChanged: (val) => vm.toggleNotification (val),
120121 ),
@@ -153,4 +154,97 @@ class UserProfileView extends StatelessWidget {
153154 ),
154155 );
155156 }
157+
158+ void _showHeatmapBottomSheet (BuildContext context) {
159+ // mock data for testing
160+ final now = DateTime .now ();
161+ final today = DateTime (now.year, now.month, now.day);
162+
163+ final Map <DateTime , int > mockHeatmapData = {
164+ today.subtract (const Duration (days: 1 )): 3 ,
165+ today.subtract (const Duration (days: 2 )): 7 ,
166+ today.subtract (const Duration (days: 3 )): 4 ,
167+ today.subtract (const Duration (days: 4 )): 8 ,
168+ today.subtract (const Duration (days: 5 )): 2 ,
169+ today.subtract (const Duration (days: 8 )): 5 ,
170+ };
171+
172+ showModalBottomSheet (
173+ context: context,
174+ isScrollControlled: true ,
175+ backgroundColor: Theme .of (context).colorScheme.surface,
176+ shape: const RoundedRectangleBorder (
177+ borderRadius: BorderRadius .vertical (top: Radius .circular (24 )),
178+ ),
179+ builder: (bottomSheetContext) {
180+ return SafeArea (
181+ child: Padding (
182+ padding: const EdgeInsets .all (24.0 ),
183+ child: Column (
184+ mainAxisSize: MainAxisSize .min,
185+ crossAxisAlignment: CrossAxisAlignment .start,
186+ children: [
187+ Center (
188+ child: Container (
189+ width: 40 ,
190+ height: 4 ,
191+ decoration: BoxDecoration (
192+ color: Theme .of (context).colorScheme.outline,
193+ borderRadius: BorderRadius .circular (2 ),
194+ ),
195+ ),
196+ ),
197+ const SizedBox (height: 24 ),
198+
199+ Text (
200+ 'Bản đồ hoạt động' ,
201+ style: TextStyle (
202+ fontSize: 20 ,
203+ fontWeight: FontWeight .bold,
204+ color: Theme .of (context).colorScheme.onSurface,
205+ ),
206+ ),
207+ const SizedBox (height: 8 ),
208+ Text (
209+ 'Giữ vững phong độ nhé! 🔥' ,
210+ style: TextStyle (
211+ color: Theme .of (context).colorScheme.onSurfaceVariant,
212+ ),
213+ ),
214+ const SizedBox (height: 24 ),
215+
216+ Center (
217+ child: HeatMap (
218+ datasets: mockHeatmapData,
219+ colorMode: ColorMode .opacity,
220+ showText: false ,
221+ scrollable: true ,
222+ size: 30 ,
223+
224+ colorsets: {
225+ 1 : Theme .of (context).colorScheme.primary.withValues (alpha: 0.2 ),
226+ 3 : Theme .of (context).colorScheme.primary.withValues (alpha: 0.4 ),
227+ 5 : Theme .of (context).colorScheme.primary.withValues (alpha: 0.6 ),
228+ 7 : Theme .of (context).colorScheme.primary.withValues (alpha: 0.8 ),
229+ 9 : Theme .of (context).colorScheme.primary,
230+ },
231+ onClick: (value) {
232+ // Khi bấm vào 1 ô vuông, hiện số task hoàn thành ngày đó
233+ ScaffoldMessenger .of (context).showSnackBar (
234+ SnackBar (
235+ content: Text ('Đã hoàn thành $value công việc' ),
236+ behavior: SnackBarBehavior .floating,
237+ ),
238+ );
239+ },
240+ ),
241+ ),
242+ const SizedBox (height: 16 ),
243+ ],
244+ ),
245+ ),
246+ );
247+ },
248+ );
249+ }
156250}
0 commit comments