From d789be61bb536522c9c6de0f45be334a055e7ebf Mon Sep 17 00:00:00 2001 From: thaihuynhxyz Date: Tue, 10 Mar 2026 17:12:02 +0700 Subject: [PATCH] fix: improve mobile UX with swipe gestures and compact hours - Add swipe left/right on today card to change day - Add swipe left/right on calendar to change month - Hours grid: single row of 12 emoji icons, time shown as tooltip - Fits all screen widths without overflow Made-with: Cursor --- lib/main_view.dart | 131 +++++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 57 deletions(-) diff --git a/lib/main_view.dart b/lib/main_view.dart index 1d550b9..ac2fd67 100644 --- a/lib/main_view.dart +++ b/lib/main_view.dart @@ -52,20 +52,41 @@ class _MainViewState extends State { }, ), const SizedBox(height: 12), - _TodayCard( - solar: solar, - lunar: lunar, - bloc: bloc, - lunarFocus: _lunarFocus, + GestureDetector( + onHorizontalDragEnd: (details) { + if (details.primaryVelocity == null) return; + if (details.primaryVelocity! < 0) { + bloc.add(const NextSelected()); + } else if (details.primaryVelocity! > 0) { + bloc.add(const PreviousSelected()); + } + }, + child: _TodayCard( + solar: solar, + lunar: lunar, + bloc: bloc, + lunarFocus: _lunarFocus, + ), ), const SizedBox(height: 12), - _MonthCalendar( - selectedDate: solar, - selectedLunar: lunar, - lunarFocus: _lunarFocus, - onDateSelected: (date) { - bloc.add(SolarSelected(solar: date)); + GestureDetector( + onHorizontalDragEnd: (details) { + if (details.primaryVelocity == null) return; + final prevMonth = DateTime( + solar.year, + solar.month + (details.primaryVelocity! < 0 ? 1 : -1), + solar.day.clamp(1, 28), + ); + bloc.add(SolarSelected(solar: prevMonth)); }, + child: _MonthCalendar( + selectedDate: solar, + selectedLunar: lunar, + lunarFocus: _lunarFocus, + onDateSelected: (date) { + bloc.add(SolarSelected(solar: date)); + }, + ), ), const SizedBox(height: 12), _InfoPanel(lunar: lunar), @@ -779,59 +800,55 @@ class _InfoPanel extends StatelessWidget { List activeHours, Color primary, ) { - return GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 6, - mainAxisSpacing: 4, - crossAxisSpacing: 4, - ), - itemCount: 12, - itemBuilder: (context, i) { + return Row( + children: List.generate(12, (i) { final isActive = activeHours.contains(LunarConverter.hourNames[i]); - return Container( - decoration: BoxDecoration( - color: - isActive ? primary.withValues(alpha: 0.15) : Colors.transparent, - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: isActive - ? primary.withValues(alpha: 0.4) - : theme.textTheme.bodyMedium!.color!.withValues(alpha: 0.15), - ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - _hourEmoji[i], - style: TextStyle( - fontSize: 18, - color: isActive ? null : Colors.grey, - ), - ), - const SizedBox(height: 2), - Text( - _hourLabels[i], - style: TextStyle( - fontSize: 10, - fontWeight: isActive ? FontWeight.w600 : FontWeight.w400, - color: isActive ? primary : theme.textTheme.bodyMedium?.color, + return Expanded( + child: Tooltip( + message: '${_hourLabels[i]}: ${_hourTimes[i]}h', + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 2), + padding: const EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + color: isActive + ? primary.withValues(alpha: 0.15) + : Colors.transparent, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: isActive + ? primary.withValues(alpha: 0.4) + : theme.textTheme.bodyMedium!.color! + .withValues(alpha: 0.1), ), ), - Text( - '${_hourTimes[i]}h', - style: TextStyle( - fontSize: 9, - color: theme.textTheme.bodyMedium?.color, - ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + _hourEmoji[i], + style: TextStyle( + fontSize: 14, + color: isActive ? null : Colors.grey, + ), + ), + const SizedBox(height: 2), + Text( + _hourLabels[i], + style: TextStyle( + fontSize: 8, + fontWeight: isActive ? FontWeight.w600 : FontWeight.w400, + color: isActive + ? primary + : theme.textTheme.bodyMedium?.color, + ), + ), + ], ), - ], + ), ), ); - }, + }), ); } }