Showing
7 changed files
with
284 additions
and
215 deletions
| 1 | import 'package:doublefeel_flutter/app/modules/home/widgets/df_tab_bar.dart'; | 1 | import 'package:doublefeel_flutter/app/modules/home/widgets/df_tab_bar.dart'; |
| 2 | +import 'package:doublefeel_flutter/app/modules/interact/widgets/friend_interaction_entry.dart'; | ||
| 2 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | 3 | import 'package:doublefeel_flutter/app/routes/app_pages.dart'; |
| 3 | import 'package:doublefeel_flutter/core/constants/intent_keys.dart'; | 4 | import 'package:doublefeel_flutter/core/constants/intent_keys.dart'; |
| 4 | import 'package:doublefeel_flutter/core/util/app_toast.dart'; | 5 | import 'package:doublefeel_flutter/core/util/app_toast.dart'; |
| @@ -32,52 +33,51 @@ class FriendsTab extends GetView<FriendsController> { | @@ -32,52 +33,51 @@ class FriendsTab extends GetView<FriendsController> { | ||
| 32 | Expanded( | 33 | Expanded( |
| 33 | child: ColoredBox( | 34 | child: ColoredBox( |
| 34 | color: const Color(0xFFF5F2FF), | 35 | color: const Color(0xFFF5F2FF), |
| 35 | - child: Obx( | ||
| 36 | - () { | ||
| 37 | - final friends = controller.friends; | ||
| 38 | - final hasFriends = friends.isNotEmpty; | ||
| 39 | - final isFull = controller.isFull; | ||
| 40 | - final healthData = controller.selfHealthData.value; | ||
| 41 | - final currentUser = | ||
| 42 | - userPreferences.preferences.value.meUserInfo; | ||
| 43 | - final isSelfInitialLoading = | ||
| 44 | - controller.isLoading.value && healthData == null; | ||
| 45 | - final isFriendsInitialLoading = | ||
| 46 | - controller.isLoading.value && !hasFriends; | ||
| 47 | - final selfHealthCard = _SelfHealthCard( | ||
| 48 | - name: currentUser?.nickname?.trim().isNotEmpty == true | ||
| 49 | - ? currentUser!.nickname!.trim() | ||
| 50 | - : '-', | ||
| 51 | - avatarUrl: currentUser?.avatar, | ||
| 52 | - updatedAt: healthData?.updatedAt ?? | ||
| 53 | - context.l10n.friendsWaitingForData, | ||
| 54 | - sleepQualityScore: healthData?.sleepQualityScore, | ||
| 55 | - steps: healthData?.steps, | ||
| 56 | - activeCalories: healthData?.activeCalories, | ||
| 57 | - stressState: | ||
| 58 | - healthData?.stressState ?? FriendStressState.wait, | ||
| 59 | - stressValueText: healthData?.stressValueText, | ||
| 60 | - isLoading: isSelfInitialLoading, | ||
| 61 | - ); | 36 | + child: Obx(() { |
| 37 | + final friends = controller.friends; | ||
| 38 | + final hasFriends = friends.isNotEmpty; | ||
| 39 | + final isFull = controller.isFull; | ||
| 40 | + final healthData = controller.selfHealthData.value; | ||
| 41 | + final currentUser = | ||
| 42 | + userPreferences.preferences.value.meUserInfo; | ||
| 43 | + final isSelfInitialLoading = | ||
| 44 | + controller.isLoading.value && healthData == null; | ||
| 45 | + final isFriendsInitialLoading = | ||
| 46 | + controller.isLoading.value && !hasFriends; | ||
| 47 | + final selfHealthCard = _SelfHealthCard( | ||
| 48 | + name: currentUser?.nickname?.trim().isNotEmpty == true | ||
| 49 | + ? currentUser!.nickname!.trim() | ||
| 50 | + : '-', | ||
| 51 | + avatarUrl: currentUser?.avatar, | ||
| 52 | + updatedAt: | ||
| 53 | + healthData?.updatedAt ?? | ||
| 54 | + context.l10n.friendsWaitingForData, | ||
| 55 | + sleepQualityScore: healthData?.sleepQualityScore, | ||
| 56 | + steps: healthData?.steps, | ||
| 57 | + activeCalories: healthData?.activeCalories, | ||
| 58 | + stressState: | ||
| 59 | + healthData?.stressState ?? FriendStressState.wait, | ||
| 60 | + stressValueText: healthData?.stressValueText, | ||
| 61 | + isLoading: isSelfInitialLoading, | ||
| 62 | + ); | ||
| 62 | 63 | ||
| 63 | - if (isFriendsInitialLoading) { | ||
| 64 | - return _buildLoadingFriendsView(context, selfHealthCard); | ||
| 65 | - } | ||
| 66 | - if (!hasFriends) { | ||
| 67 | - return _buildEmptyFriendsView( | ||
| 68 | - context, | ||
| 69 | - selfHealthCard, | ||
| 70 | - isFull: isFull, | ||
| 71 | - ); | ||
| 72 | - } | ||
| 73 | - return _buildFriendsListView( | 64 | + if (isFriendsInitialLoading) { |
| 65 | + return _buildLoadingFriendsView(context, selfHealthCard); | ||
| 66 | + } | ||
| 67 | + if (!hasFriends) { | ||
| 68 | + return _buildEmptyFriendsView( | ||
| 74 | context, | 69 | context, |
| 75 | selfHealthCard, | 70 | selfHealthCard, |
| 76 | - friends: friends, | ||
| 77 | isFull: isFull, | 71 | isFull: isFull, |
| 78 | ); | 72 | ); |
| 79 | - }, | ||
| 80 | - ), | 73 | + } |
| 74 | + return _buildFriendsListView( | ||
| 75 | + context, | ||
| 76 | + selfHealthCard, | ||
| 77 | + friends: friends, | ||
| 78 | + isFull: isFull, | ||
| 79 | + ); | ||
| 80 | + }), | ||
| 81 | ), | 81 | ), |
| 82 | ), | 82 | ), |
| 83 | ], | 83 | ], |
| @@ -86,24 +86,13 @@ class FriendsTab extends GetView<FriendsController> { | @@ -86,24 +86,13 @@ class FriendsTab extends GetView<FriendsController> { | ||
| 86 | ); | 86 | ); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | - Widget _buildLoadingFriendsView( | ||
| 90 | - BuildContext context, | ||
| 91 | - Widget selfHealthCard, | ||
| 92 | - ) { | 89 | + Widget _buildLoadingFriendsView(BuildContext context, Widget selfHealthCard) { |
| 93 | return RefreshIndicator( | 90 | return RefreshIndicator( |
| 94 | onRefresh: controller.refreshData, | 91 | onRefresh: controller.refreshData, |
| 95 | child: ListView( | 92 | child: ListView( |
| 96 | physics: const AlwaysScrollableScrollPhysics(), | 93 | physics: const AlwaysScrollableScrollPhysics(), |
| 97 | - padding: EdgeInsets.fromLTRB( | ||
| 98 | - 0, | ||
| 99 | - 0, | ||
| 100 | - 0, | ||
| 101 | - _emptyListBottomPadding(context), | ||
| 102 | - ), | ||
| 103 | - children: [ | ||
| 104 | - selfHealthCard, | ||
| 105 | - const _FriendsLoadingIndicator(), | ||
| 106 | - ], | 94 | + padding: EdgeInsets.fromLTRB(0, 0, 0, _emptyListBottomPadding(context)), |
| 95 | + children: [selfHealthCard, const _FriendsLoadingIndicator()], | ||
| 107 | ), | 96 | ), |
| 108 | ); | 97 | ); |
| 109 | } | 98 | } |
| @@ -117,12 +106,7 @@ class FriendsTab extends GetView<FriendsController> { | @@ -117,12 +106,7 @@ class FriendsTab extends GetView<FriendsController> { | ||
| 117 | onRefresh: controller.refreshData, | 106 | onRefresh: controller.refreshData, |
| 118 | child: ListView( | 107 | child: ListView( |
| 119 | physics: const AlwaysScrollableScrollPhysics(), | 108 | physics: const AlwaysScrollableScrollPhysics(), |
| 120 | - padding: EdgeInsets.fromLTRB( | ||
| 121 | - 0, | ||
| 122 | - 0, | ||
| 123 | - 0, | ||
| 124 | - _emptyListBottomPadding(context), | ||
| 125 | - ), | 109 | + padding: EdgeInsets.fromLTRB(0, 0, 0, _emptyListBottomPadding(context)), |
| 126 | children: [ | 110 | children: [ |
| 127 | selfHealthCard, | 111 | selfHealthCard, |
| 128 | _EmptyFriendsView( | 112 | _EmptyFriendsView( |
| @@ -225,6 +209,11 @@ class FriendsTab extends GetView<FriendsController> { | @@ -225,6 +209,11 @@ class FriendsTab extends GetView<FriendsController> { | ||
| 225 | ], | 209 | ], |
| 226 | ), | 210 | ), |
| 227 | ), | 211 | ), |
| 212 | + Positioned( | ||
| 213 | + right: 16, | ||
| 214 | + bottom: _floatingAddButtonBottom(context) + 52, | ||
| 215 | + child: const FriendInteractionFloatingEntry(), | ||
| 216 | + ), | ||
| 228 | ], | 217 | ], |
| 229 | ); | 218 | ); |
| 230 | } | 219 | } |
| @@ -238,8 +227,9 @@ class FriendsTab extends GetView<FriendsController> { | @@ -238,8 +227,9 @@ class FriendsTab extends GetView<FriendsController> { | ||
| 238 | } | 227 | } |
| 239 | 228 | ||
| 240 | double _tabBarAvoidanceBottom(BuildContext context) { | 229 | double _tabBarAvoidanceBottom(BuildContext context) { |
| 241 | - final bottomInset = | ||
| 242 | - Theme.of(context).platform == TargetPlatform.iOS ? 34.0 : 0.0; | 230 | + final bottomInset = Theme.of(context).platform == TargetPlatform.iOS |
| 231 | + ? 34.0 | ||
| 232 | + : 0.0; | ||
| 243 | return bottomInset + DfTabBar.height + DfTabBar.bottomMargin; | 233 | return bottomInset + DfTabBar.height + DfTabBar.bottomMargin; |
| 244 | } | 234 | } |
| 245 | 235 | ||
| @@ -265,10 +255,7 @@ class FriendsTab extends GetView<FriendsController> { | @@ -265,10 +255,7 @@ class FriendsTab extends GetView<FriendsController> { | ||
| 265 | } | 255 | } |
| 266 | 256 | ||
| 267 | void _openFriendHome(FriendHealthData friend) { | 257 | void _openFriendHome(FriendHealthData friend) { |
| 268 | - Get.toNamed( | ||
| 269 | - Routes.FRIEND_HOME, | ||
| 270 | - arguments: friend.friendItem, | ||
| 271 | - ); | 258 | + Get.toNamed(Routes.FRIEND_HOME, arguments: friend.friendItem); |
| 272 | } | 259 | } |
| 273 | 260 | ||
| 274 | void _handleFriendAction(FriendCardAction action, FriendHealthData friend) { | 261 | void _handleFriendAction(FriendCardAction action, FriendHealthData friend) { |
| @@ -386,11 +373,7 @@ class _FriendsHeader extends StatelessWidget { | @@ -386,11 +373,7 @@ class _FriendsHeader extends StatelessWidget { | ||
| 386 | onPressed: () { | 373 | onPressed: () { |
| 387 | _navigatorToPrivacySettings(context); | 374 | _navigatorToPrivacySettings(context); |
| 388 | }, | 375 | }, |
| 389 | - icon: Image.asset( | ||
| 390 | - R.assetsImagesSettings, | ||
| 391 | - width: 24, | ||
| 392 | - height: 24, | ||
| 393 | - ), | 376 | + icon: Image.asset(R.assetsImagesSettings, width: 24, height: 24), |
| 394 | padding: EdgeInsets.zero, | 377 | padding: EdgeInsets.zero, |
| 395 | constraints: const BoxConstraints.tightFor(width: 40, height: 40), | 378 | constraints: const BoxConstraints.tightFor(width: 40, height: 40), |
| 396 | ), | 379 | ), |
| @@ -406,10 +389,7 @@ _navigatorToPrivacySettings(BuildContext context) { | @@ -406,10 +389,7 @@ _navigatorToPrivacySettings(BuildContext context) { | ||
| 406 | } | 389 | } |
| 407 | 390 | ||
| 408 | class _EmptyFriendsView extends StatelessWidget { | 391 | class _EmptyFriendsView extends StatelessWidget { |
| 409 | - const _EmptyFriendsView({ | ||
| 410 | - required this.enabled, | ||
| 411 | - this.onTap, | ||
| 412 | - }); | 392 | + const _EmptyFriendsView({required this.enabled, this.onTap}); |
| 413 | 393 | ||
| 414 | final bool enabled; | 394 | final bool enabled; |
| 415 | final VoidCallback? onTap; | 395 | final VoidCallback? onTap; |
| @@ -441,10 +421,7 @@ class _EmptyFriendsView extends StatelessWidget { | @@ -441,10 +421,7 @@ class _EmptyFriendsView extends StatelessWidget { | ||
| 441 | ), | 421 | ), |
| 442 | ), | 422 | ), |
| 443 | const SizedBox(height: 12), | 423 | const SizedBox(height: 12), |
| 444 | - _AddFriendButton( | ||
| 445 | - enabled: enabled, | ||
| 446 | - onTap: onTap, | ||
| 447 | - ), | 424 | + _AddFriendButton(enabled: enabled, onTap: onTap), |
| 448 | ], | 425 | ], |
| 449 | ), | 426 | ), |
| 450 | ); | 427 | ); |
| @@ -471,52 +448,53 @@ class _AddFriendButton extends StatelessWidget { | @@ -471,52 +448,53 @@ class _AddFriendButton extends StatelessWidget { | ||
| 471 | onTap: onTap, | 448 | onTap: onTap, |
| 472 | behavior: HitTestBehavior.opaque, | 449 | behavior: HitTestBehavior.opaque, |
| 473 | child: SizedBox( | 450 | child: SizedBox( |
| 474 | - width: 240, | ||
| 475 | - height: 48, | ||
| 476 | - child: Stack( | ||
| 477 | - children: [ | ||
| 478 | - Container( | ||
| 479 | - width: double.infinity, | ||
| 480 | - height: double.infinity, | 451 | + width: 240, |
| 452 | + height: 48, | ||
| 453 | + child: Stack( | ||
| 454 | + children: [ | ||
| 455 | + Container( | ||
| 456 | + width: double.infinity, | ||
| 457 | + height: double.infinity, | ||
| 458 | + decoration: BoxDecoration( | ||
| 459 | + color: Colors.white, | ||
| 460 | + borderRadius: BorderRadius.circular(24), | ||
| 461 | + ), | ||
| 462 | + ), | ||
| 463 | + Opacity( | ||
| 464 | + opacity: enabled ? 1 : 0.4, | ||
| 465 | + child: Container( | ||
| 466 | + width: 240, | ||
| 467 | + height: 48, | ||
| 481 | decoration: BoxDecoration( | 468 | decoration: BoxDecoration( |
| 482 | - color: Colors.white, | 469 | + color: const Color(0xFF845EEE), |
| 483 | borderRadius: BorderRadius.circular(24), | 470 | borderRadius: BorderRadius.circular(24), |
| 484 | ), | 471 | ), |
| 485 | - ), | ||
| 486 | - Opacity( | ||
| 487 | - opacity: enabled ? 1 : 0.4, | ||
| 488 | - child: Container( | ||
| 489 | - width: 240, | ||
| 490 | - height: 48, | ||
| 491 | - decoration: BoxDecoration( | ||
| 492 | - color: const Color(0xFF845EEE), | ||
| 493 | - borderRadius: BorderRadius.circular(24), | ||
| 494 | - ), | ||
| 495 | - child: Row( | ||
| 496 | - mainAxisAlignment: MainAxisAlignment.center, | ||
| 497 | - children: [ | ||
| 498 | - Image.asset( | ||
| 499 | - 'assets/images/common/ic_add.png', | ||
| 500 | - width: 18, | ||
| 501 | - height: 18, | 472 | + child: Row( |
| 473 | + mainAxisAlignment: MainAxisAlignment.center, | ||
| 474 | + children: [ | ||
| 475 | + Image.asset( | ||
| 476 | + 'assets/images/common/ic_add.png', | ||
| 477 | + width: 18, | ||
| 478 | + height: 18, | ||
| 479 | + color: Colors.white, | ||
| 480 | + ), | ||
| 481 | + const SizedBox(width: 6), | ||
| 482 | + Text( | ||
| 483 | + _buttonText(context), | ||
| 484 | + style: const TextStyle( | ||
| 502 | color: Colors.white, | 485 | color: Colors.white, |
| 486 | + fontSize: 16, | ||
| 487 | + fontWeight: FontWeight.w600, | ||
| 488 | + height: 1.4, | ||
| 503 | ), | 489 | ), |
| 504 | - const SizedBox(width: 6), | ||
| 505 | - Text( | ||
| 506 | - _buttonText(context), | ||
| 507 | - style: const TextStyle( | ||
| 508 | - color: Colors.white, | ||
| 509 | - fontSize: 16, | ||
| 510 | - fontWeight: FontWeight.w600, | ||
| 511 | - height: 1.4, | ||
| 512 | - ), | ||
| 513 | - ), | ||
| 514 | - ], | ||
| 515 | - ), | 490 | + ), |
| 491 | + ], | ||
| 516 | ), | 492 | ), |
| 517 | - ) | ||
| 518 | - ], | ||
| 519 | - )), | 493 | + ), |
| 494 | + ), | ||
| 495 | + ], | ||
| 496 | + ), | ||
| 497 | + ), | ||
| 520 | ), | 498 | ), |
| 521 | ); | 499 | ); |
| 522 | } | 500 | } |
| @@ -54,6 +54,7 @@ class AppHomeController extends GetxController { | @@ -54,6 +54,7 @@ class AppHomeController extends GetxController { | ||
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | bool get hasWatchedFriend => watchedFriend.value != null; | 56 | bool get hasWatchedFriend => watchedFriend.value != null; |
| 57 | + bool get hasAnyFriend => _friendsController.friends.isNotEmpty; | ||
| 57 | bool get isShowingSelf => selectedTargetId.value == selfTodayTag; | 58 | bool get isShowingSelf => selectedTargetId.value == selfTodayTag; |
| 58 | String get selfName { | 59 | String get selfName { |
| 59 | final info = _userPreferencesStorage.preferences.value.meUserInfo; | 60 | final info = _userPreferencesStorage.preferences.value.meUserInfo; |
| @@ -42,6 +42,7 @@ class AppHomeTab extends GetView<AppHomeController> { | @@ -42,6 +42,7 @@ class AppHomeTab extends GetView<AppHomeController> { | ||
| 42 | stressCard: showPeopleSwitcher | 42 | stressCard: showPeopleSwitcher |
| 43 | ? AppHomeStressCard(controller: todayController) | 43 | ? AppHomeStressCard(controller: todayController) |
| 44 | : null, | 44 | : null, |
| 45 | + showInteractionEntry: controller.hasAnyFriend, | ||
| 45 | ); | 46 | ); |
| 46 | } | 47 | } |
| 47 | 48 |
| 1 | import 'package:doublefeel_flutter/app/modules/home/widgets/today/hrv_measurement_bottom_sheet.dart'; | 1 | import 'package:doublefeel_flutter/app/modules/home/widgets/today/hrv_measurement_bottom_sheet.dart'; |
| 2 | +import 'package:doublefeel_flutter/app/modules/interact/widgets/friend_interaction_entry.dart'; | ||
| 2 | import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart'; | 3 | import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart'; |
| 3 | import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_bottom_slogan.dart'; | 4 | import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_bottom_slogan.dart'; |
| 4 | import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_date_picker_sheet.dart'; | 5 | import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_date_picker_sheet.dart'; |
| @@ -40,6 +41,7 @@ class TodayTabBody extends StatelessWidget { | @@ -40,6 +41,7 @@ class TodayTabBody extends StatelessWidget { | ||
| 40 | this.useHomeHeader = false, | 41 | this.useHomeHeader = false, |
| 41 | this.headerFooter, | 42 | this.headerFooter, |
| 42 | this.stressCard, | 43 | this.stressCard, |
| 44 | + this.showInteractionEntry = false, | ||
| 43 | }); | 45 | }); |
| 44 | 46 | ||
| 45 | final TodayController controller; | 47 | final TodayController controller; |
| @@ -55,6 +57,8 @@ class TodayTabBody extends StatelessWidget { | @@ -55,6 +57,8 @@ class TodayTabBody extends StatelessWidget { | ||
| 55 | /// The card starts below the week calendar so [headerFooter] can overlap it. | 57 | /// The card starts below the week calendar so [headerFooter] can overlap it. |
| 56 | final Widget? stressCard; | 58 | final Widget? stressCard; |
| 57 | 59 | ||
| 60 | + final bool showInteractionEntry; | ||
| 61 | + | ||
| 58 | final _weekCalendarHeight = 58.0; | 62 | final _weekCalendarHeight = 58.0; |
| 59 | final _topContentHeight = /*_topBarHeight + _weekCalendarHeight*/ 106.0; | 63 | final _topContentHeight = /*_topBarHeight + _weekCalendarHeight*/ 106.0; |
| 60 | static const _peopleSwitcherAspectRatio = 252 / 1029; | 64 | static const _peopleSwitcherAspectRatio = 252 / 1029; |
| @@ -156,6 +160,12 @@ class TodayTabBody extends StatelessWidget { | @@ -156,6 +160,12 @@ class TodayTabBody extends StatelessWidget { | ||
| 156 | ), | 160 | ), |
| 157 | ), | 161 | ), |
| 158 | ), | 162 | ), |
| 163 | + if (controller.isFriend || showInteractionEntry) | ||
| 164 | + const Positioned( | ||
| 165 | + right: 16, | ||
| 166 | + bottom: 92, | ||
| 167 | + child: FriendInteractionFloatingEntry(), | ||
| 168 | + ), | ||
| 159 | ], | 169 | ], |
| 160 | ), | 170 | ), |
| 161 | ); | 171 | ); |
| 1 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 1 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| 2 | +import 'package:doublefeel_flutter/app/modules/interact/widgets/friend_interaction_entry.dart'; | ||
| 2 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; | 3 | import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; |
| 3 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; |
| 4 | import 'package:get/get.dart'; | 5 | import 'package:get/get.dart'; |
| @@ -21,7 +22,9 @@ class TodayHrvNumberCard extends StatelessWidget { | @@ -21,7 +22,9 @@ class TodayHrvNumberCard extends StatelessWidget { | ||
| 21 | ), | 22 | ), |
| 22 | child: Obx(() { | 23 | child: Obx(() { |
| 23 | var sameDay = DateUtils.isSameDay( | 24 | var sameDay = DateUtils.isSameDay( |
| 24 | - controller.selectedDate.value, controller.lastSelectableDay.value); | 25 | + controller.selectedDate.value, |
| 26 | + controller.lastSelectableDay.value, | ||
| 27 | + ); | ||
| 25 | 28 | ||
| 26 | var otherHrv = (sameDay | 29 | var otherHrv = (sameDay |
| 27 | ? context.l10n.todaySAverageHrv | 30 | ? context.l10n.todaySAverageHrv |
| @@ -39,6 +42,7 @@ class TodayHrvNumberCard extends StatelessWidget { | @@ -39,6 +42,7 @@ class TodayHrvNumberCard extends StatelessWidget { | ||
| 39 | : otherHrv, | 42 | : otherHrv, |
| 40 | value: hrvAvg > 0 ? hrvAvg.toString() : '-', | 43 | value: hrvAvg > 0 ? hrvAvg.toString() : '-', |
| 41 | unit: 'ms', | 44 | unit: 'ms', |
| 45 | + interactionEnabled: controller.isFriend && hrvAvg > 0, | ||
| 42 | ), | 46 | ), |
| 43 | ), | 47 | ), |
| 44 | 48 | ||
| @@ -46,12 +50,18 @@ class TodayHrvNumberCard extends StatelessWidget { | @@ -46,12 +50,18 @@ class TodayHrvNumberCard extends StatelessWidget { | ||
| 46 | child: _NumberItem( | 50 | child: _NumberItem( |
| 47 | label: controller.isFriend | 51 | label: controller.isFriend |
| 48 | ? context.l10n.reportOtherPossessiveTitle( | 52 | ? context.l10n.reportOtherPossessiveTitle( |
| 49 | - context.l10n.restingHeartRate) | 53 | + context.l10n.restingHeartRate, |
| 54 | + ) | ||
| 50 | : context.l10n.restingHeartRate, | 55 | : context.l10n.restingHeartRate, |
| 51 | - value: controller.v2HealthData.value?.lastRestingHrValue | 56 | + value: |
| 57 | + controller.v2HealthData.value?.lastRestingHrValue | ||
| 52 | ?.toString() ?? | 58 | ?.toString() ?? |
| 53 | '-', | 59 | '-', |
| 54 | unit: 'bpm', | 60 | unit: 'bpm', |
| 61 | + interactionEnabled: | ||
| 62 | + controller.isFriend && | ||
| 63 | + (controller.v2HealthData.value?.lastRestingHrValue ?? 0) > | ||
| 64 | + 0, | ||
| 55 | ), | 65 | ), |
| 56 | ), | 66 | ), |
| 57 | ], | 67 | ], |
| @@ -65,11 +75,13 @@ class _NumberItem extends StatelessWidget { | @@ -65,11 +75,13 @@ class _NumberItem extends StatelessWidget { | ||
| 65 | final String label; | 75 | final String label; |
| 66 | final String value; | 76 | final String value; |
| 67 | final String unit; | 77 | final String unit; |
| 78 | + final bool? interactionEnabled; | ||
| 68 | 79 | ||
| 69 | const _NumberItem({ | 80 | const _NumberItem({ |
| 70 | required this.label, | 81 | required this.label, |
| 71 | required this.value, | 82 | required this.value, |
| 72 | required this.unit, | 83 | required this.unit, |
| 84 | + this.interactionEnabled, | ||
| 73 | }); | 85 | }); |
| 74 | 86 | ||
| 75 | @override | 87 | @override |
| @@ -79,13 +91,23 @@ class _NumberItem extends StatelessWidget { | @@ -79,13 +91,23 @@ class _NumberItem extends StatelessWidget { | ||
| 79 | child: Column( | 91 | child: Column( |
| 80 | crossAxisAlignment: CrossAxisAlignment.start, | 92 | crossAxisAlignment: CrossAxisAlignment.start, |
| 81 | children: [ | 93 | children: [ |
| 82 | - Text( | ||
| 83 | - label, | ||
| 84 | - style: TextStyle( | ||
| 85 | - fontSize: 12, | ||
| 86 | - color: context.colors.textTertiary, | ||
| 87 | - fontWeight: FontWeight.w400, | ||
| 88 | - ), | 94 | + Row( |
| 95 | + children: [ | ||
| 96 | + Expanded( | ||
| 97 | + child: Text( | ||
| 98 | + label, | ||
| 99 | + maxLines: 1, | ||
| 100 | + overflow: TextOverflow.ellipsis, | ||
| 101 | + style: TextStyle( | ||
| 102 | + fontSize: 12, | ||
| 103 | + color: context.colors.textTertiary, | ||
| 104 | + fontWeight: FontWeight.w400, | ||
| 105 | + ), | ||
| 106 | + ), | ||
| 107 | + ), | ||
| 108 | + if (interactionEnabled != null) | ||
| 109 | + FriendInteractionButton(enabled: interactionEnabled!), | ||
| 110 | + ], | ||
| 89 | ), | 111 | ), |
| 90 | const SizedBox(height: 6), | 112 | const SizedBox(height: 6), |
| 91 | Row( | 113 | Row( |
| 1 | import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; | 1 | import 'package:doublefeel_flutter/app/utils/platform_compact.dart'; |
| 2 | +import 'package:doublefeel_flutter/app/modules/interact/widgets/friend_interaction_entry.dart'; | ||
| 2 | import 'package:doublefeel_flutter/app/widget/circular_gradient_progress/arc_progress_widget.dart'; | 3 | import 'package:doublefeel_flutter/app/widget/circular_gradient_progress/arc_progress_widget.dart'; |
| 3 | import 'package:doublefeel_flutter/app/widget/circular_gradient_progress/combine.dart'; | 4 | import 'package:doublefeel_flutter/app/widget/circular_gradient_progress/combine.dart'; |
| 4 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; | 5 | import 'package:doublefeel_flutter/core/theme/app_theme.dart'; |
| @@ -28,29 +29,31 @@ class TodaySleepCard extends StatelessWidget { | @@ -28,29 +29,31 @@ class TodaySleepCard extends StatelessWidget { | ||
| 28 | Color sleepStateColor = sleepDuration == 0 | 29 | Color sleepStateColor = sleepDuration == 0 |
| 29 | ? context.colors.textPrimary | 30 | ? context.colors.textPrimary |
| 30 | : healthData?.sleepStateColor(context) ?? | 31 | : healthData?.sleepStateColor(context) ?? |
| 31 | - context.colors.textPrimary; | ||
| 32 | - String sleepStateStr = | ||
| 33 | - sleepDuration == 0 ? '-' : healthData?.sleepStateStr() ?? '-'; | 32 | + context.colors.textPrimary; |
| 33 | + String sleepStateStr = sleepDuration == 0 | ||
| 34 | + ? '-' | ||
| 35 | + : healthData?.sleepStateStr() ?? '-'; | ||
| 34 | String sleepAverageHeartRate = | 36 | String sleepAverageHeartRate = |
| 35 | (healthData?.hrAvg == null || healthData?.hrAvg == 0) | 37 | (healthData?.hrAvg == null || healthData?.hrAvg == 0) |
| 36 | - ? '-' | ||
| 37 | - : '${healthData?.hrAvg}'; | 38 | + ? '-' |
| 39 | + : '${healthData?.hrAvg}'; | ||
| 38 | double sleepDurationProgress = | 40 | double sleepDurationProgress = |
| 39 | (activityTarget?.sleepTargetDuration ?? 28800) == 0 || | 41 | (activityTarget?.sleepTargetDuration ?? 28800) == 0 || |
| 40 | - sleepDuration == 0 | ||
| 41 | - ? -1 | ||
| 42 | - : ((healthData?.sleepDuration ?? 0) / | ||
| 43 | - (activityTarget?.sleepTargetDuration ?? 28800) * | ||
| 44 | - 360); | ||
| 45 | - double sleepScoreProgress = | ||
| 46 | - sleepDuration == 0 ? -1 : (healthData?.sleepScore ?? 0.0) * 3.6; | 42 | + sleepDuration == 0 |
| 43 | + ? -1 | ||
| 44 | + : ((healthData?.sleepDuration ?? 0) / | ||
| 45 | + (activityTarget?.sleepTargetDuration ?? 28800) * | ||
| 46 | + 360); | ||
| 47 | + double sleepScoreProgress = sleepDuration == 0 | ||
| 48 | + ? -1 | ||
| 49 | + : (healthData?.sleepScore ?? 0.0) * 3.6; | ||
| 47 | 50 | ||
| 48 | double? sleepDurationRatio = | 51 | double? sleepDurationRatio = |
| 49 | (activityTarget?.sleepTargetDuration ?? 28800) == 0 || | 52 | (activityTarget?.sleepTargetDuration ?? 28800) == 0 || |
| 50 | - sleepDuration == 0 | ||
| 51 | - ? null | ||
| 52 | - : ((healthData?.sleepDuration ?? 0) / | ||
| 53 | - (activityTarget?.sleepTargetDuration ?? 28800)); | 53 | + sleepDuration == 0 |
| 54 | + ? null | ||
| 55 | + : ((healthData?.sleepDuration ?? 0) / | ||
| 56 | + (activityTarget?.sleepTargetDuration ?? 28800)); | ||
| 54 | double? sleepScoreRatio = sleepDuration == 0 | 57 | double? sleepScoreRatio = sleepDuration == 0 |
| 55 | ? null | 58 | ? null |
| 56 | : ((healthData?.sleepScore ?? 0.0) / 100.0); | 59 | : ((healthData?.sleepScore ?? 0.0) / 100.0); |
| @@ -62,50 +65,51 @@ class TodaySleepCard extends StatelessWidget { | @@ -62,50 +65,51 @@ class TodaySleepCard extends StatelessWidget { | ||
| 62 | actionText: l10n.viewSleepReport, | 65 | actionText: l10n.viewSleepReport, |
| 63 | metrics: [ | 66 | metrics: [ |
| 64 | _MetricBlock( | 67 | _MetricBlock( |
| 65 | - label: l10n.duration, | ||
| 66 | - labelColor: context.colors.primary, | ||
| 67 | - value: (sleepDuration > 0) | ||
| 68 | - ? Text.rich( | ||
| 69 | - TextSpan( | ||
| 70 | - style: TextStyle( | ||
| 71 | - color: context.colors.textPrimary, | ||
| 72 | - fontSize: 16, | ||
| 73 | - fontWeight: FontWeight.w600, | ||
| 74 | - ), | ||
| 75 | - children: [ | ||
| 76 | - if (sleepHours > 0) ...[ | ||
| 77 | - TextSpan(text: '$sleepHours'), | ||
| 78 | - TextSpan( | ||
| 79 | - text: l10n.sleepDailyUnitHour, | ||
| 80 | - style: TextStyle( | ||
| 81 | - color: context.colors.textSecondary, | ||
| 82 | - fontSize: 12, | ||
| 83 | - fontWeight: FontWeight.w400, | ||
| 84 | - ), | ||
| 85 | - ) | ||
| 86 | - ], | ||
| 87 | - if (sleepMinutes > 0) ...[ | ||
| 88 | - TextSpan(text: '$sleepMinutes'), | ||
| 89 | - TextSpan( | ||
| 90 | - text: l10n.sleepDailyUnitMinute, | ||
| 91 | - style: TextStyle( | ||
| 92 | - color: context.colors.textSecondary, | ||
| 93 | - fontSize: 12, | ||
| 94 | - fontWeight: FontWeight.w400, | ||
| 95 | - ), | ||
| 96 | - ), | ||
| 97 | - ] | ||
| 98 | - ], | ||
| 99 | - ), | ||
| 100 | - ) | ||
| 101 | - : Text( | ||
| 102 | - '-', | 68 | + label: l10n.duration, |
| 69 | + labelColor: context.colors.primary, | ||
| 70 | + value: (sleepDuration > 0) | ||
| 71 | + ? Text.rich( | ||
| 72 | + TextSpan( | ||
| 103 | style: TextStyle( | 73 | style: TextStyle( |
| 104 | color: context.colors.textPrimary, | 74 | color: context.colors.textPrimary, |
| 105 | fontSize: 16, | 75 | fontSize: 16, |
| 106 | fontWeight: FontWeight.w600, | 76 | fontWeight: FontWeight.w600, |
| 107 | ), | 77 | ), |
| 108 | - )), | 78 | + children: [ |
| 79 | + if (sleepHours > 0) ...[ | ||
| 80 | + TextSpan(text: '$sleepHours'), | ||
| 81 | + TextSpan( | ||
| 82 | + text: l10n.sleepDailyUnitHour, | ||
| 83 | + style: TextStyle( | ||
| 84 | + color: context.colors.textSecondary, | ||
| 85 | + fontSize: 12, | ||
| 86 | + fontWeight: FontWeight.w400, | ||
| 87 | + ), | ||
| 88 | + ), | ||
| 89 | + ], | ||
| 90 | + if (sleepMinutes > 0) ...[ | ||
| 91 | + TextSpan(text: '$sleepMinutes'), | ||
| 92 | + TextSpan( | ||
| 93 | + text: l10n.sleepDailyUnitMinute, | ||
| 94 | + style: TextStyle( | ||
| 95 | + color: context.colors.textSecondary, | ||
| 96 | + fontSize: 12, | ||
| 97 | + fontWeight: FontWeight.w400, | ||
| 98 | + ), | ||
| 99 | + ), | ||
| 100 | + ], | ||
| 101 | + ], | ||
| 102 | + ), | ||
| 103 | + ) | ||
| 104 | + : Text( | ||
| 105 | + '-', | ||
| 106 | + style: TextStyle( | ||
| 107 | + color: context.colors.textPrimary, | ||
| 108 | + fontSize: 16, | ||
| 109 | + fontWeight: FontWeight.w600, | ||
| 110 | + ), | ||
| 111 | + ), | ||
| 112 | + ), | ||
| 109 | _MetricBlock( | 113 | _MetricBlock( |
| 110 | label: l10n.quality, | 114 | label: l10n.quality, |
| 111 | labelColor: const Color(0xFF7B9BFB), | 115 | labelColor: const Color(0xFF7B9BFB), |
| @@ -192,8 +196,10 @@ class TodaySleepCard extends StatelessWidget { | @@ -192,8 +196,10 @@ class TodaySleepCard extends StatelessWidget { | ||
| 192 | ], | 196 | ], |
| 193 | centerCircleSizeRatio: 0.3, | 197 | centerCircleSizeRatio: 0.3, |
| 194 | gapRatio: 0.1, | 198 | gapRatio: 0.1, |
| 195 | - interpolatedColorRatio: 1), | 199 | + interpolatedColorRatio: 1, |
| 200 | + ), | ||
| 196 | ), | 201 | ), |
| 202 | + interactionEnabled: controller.isFriend && sleepDuration > 0, | ||
| 197 | ); | 203 | ); |
| 198 | }), | 204 | }), |
| 199 | ); | 205 | ); |
| @@ -252,9 +258,7 @@ class TodayActivityCard extends StatelessWidget { | @@ -252,9 +258,7 @@ class TodayActivityCard extends StatelessWidget { | ||
| 252 | fontSize: 16, | 258 | fontSize: 16, |
| 253 | fontWeight: FontWeight.w600, | 259 | fontWeight: FontWeight.w600, |
| 254 | ), | 260 | ), |
| 255 | - children: [ | ||
| 256 | - const TextSpan(text: '-'), | ||
| 257 | - ], | 261 | + children: [const TextSpan(text: '-')], |
| 258 | ), | 262 | ), |
| 259 | ); | 263 | ); |
| 260 | } | 264 | } |
| @@ -290,20 +294,18 @@ class TodayActivityCard extends StatelessWidget { | @@ -290,20 +294,18 @@ class TodayActivityCard extends StatelessWidget { | ||
| 290 | fontSize: 16, | 294 | fontSize: 16, |
| 291 | fontWeight: FontWeight.w600, | 295 | fontWeight: FontWeight.w600, |
| 292 | ), | 296 | ), |
| 293 | - children: [ | ||
| 294 | - const TextSpan(text: '-'), | ||
| 295 | - ], | 297 | + children: [const TextSpan(text: '-')], |
| 296 | ), | 298 | ), |
| 297 | ); | 299 | ); |
| 298 | } | 300 | } |
| 299 | double activityProgress = | 301 | double activityProgress = |
| 300 | (activity > 0 && (activityTarget?.move ?? 0) > 0) | 302 | (activity > 0 && (activityTarget?.move ?? 0) > 0) |
| 301 | - ? (activity / (activityTarget?.move ?? 0)) * 360 | ||
| 302 | - : -1; | 303 | + ? (activity / (activityTarget?.move ?? 0)) * 360 |
| 304 | + : -1; | ||
| 303 | double exerciseProgress = | 305 | double exerciseProgress = |
| 304 | (exercise > 0 && (activityTarget?.exercise ?? 0) > 0) | 306 | (exercise > 0 && (activityTarget?.exercise ?? 0) > 0) |
| 305 | - ? (exercise / (activityTarget?.exercise ?? 0)) * 360 | ||
| 306 | - : -1; | 307 | + ? (exercise / (activityTarget?.exercise ?? 0)) * 360 |
| 308 | + : -1; | ||
| 307 | double standProgress = (stand > 0 && (activityTarget?.stand ?? 0) > 0) | 309 | double standProgress = (stand > 0 && (activityTarget?.stand ?? 0) > 0) |
| 308 | ? (stand / (activityTarget?.stand ?? 0)) * 360 | 310 | ? (stand / (activityTarget?.stand ?? 0)) * 360 |
| 309 | : -1; | 311 | : -1; |
| @@ -312,8 +314,8 @@ class TodayActivityCard extends StatelessWidget { | @@ -312,8 +314,8 @@ class TodayActivityCard extends StatelessWidget { | ||
| 312 | : null; | 314 | : null; |
| 313 | double? exerciseRatio = | 315 | double? exerciseRatio = |
| 314 | (exercise > 0 && (activityTarget?.exercise ?? 0) > 0) | 316 | (exercise > 0 && (activityTarget?.exercise ?? 0) > 0) |
| 315 | - ? (exercise / (activityTarget?.exercise ?? 0)) | ||
| 316 | - : null; | 317 | + ? (exercise / (activityTarget?.exercise ?? 0)) |
| 318 | + : null; | ||
| 317 | double? standRatio = (stand > 0 && (activityTarget?.stand ?? 0) > 0) | 319 | double? standRatio = (stand > 0 && (activityTarget?.stand ?? 0) > 0) |
| 318 | ? (stand / (activityTarget?.stand ?? 0)) | 320 | ? (stand / (activityTarget?.stand ?? 0)) |
| 319 | : null; | 321 | : null; |
| @@ -405,7 +407,7 @@ class TodayActivityCard extends StatelessWidget { | @@ -405,7 +407,7 @@ class TodayActivityCard extends StatelessWidget { | ||
| 405 | sweepAngles: [ | 407 | sweepAngles: [ |
| 406 | activityProgress, | 408 | activityProgress, |
| 407 | exerciseProgress, | 409 | exerciseProgress, |
| 408 | - standProgress | 410 | + standProgress, |
| 409 | ], | 411 | ], |
| 410 | backgroundColors: [ | 412 | backgroundColors: [ |
| 411 | context.colors.chartPink.withOpacity(0.4), | 413 | context.colors.chartPink.withOpacity(0.4), |
| @@ -413,20 +415,17 @@ class TodayActivityCard extends StatelessWidget { | @@ -413,20 +415,17 @@ class TodayActivityCard extends StatelessWidget { | ||
| 413 | context.colors.chartBlue.withOpacity(0.4), | 415 | context.colors.chartBlue.withOpacity(0.4), |
| 414 | ], | 416 | ], |
| 415 | gradientColors: [ | 417 | gradientColors: [ |
| 416 | - [ | ||
| 417 | - context.colors.chartPink, | ||
| 418 | - ], | ||
| 419 | - [ | ||
| 420 | - context.colors.chartGreen, | ||
| 421 | - ], | ||
| 422 | - [ | ||
| 423 | - context.colors.chartBlue, | ||
| 424 | - ], | 418 | + [context.colors.chartPink], |
| 419 | + [context.colors.chartGreen], | ||
| 420 | + [context.colors.chartBlue], | ||
| 425 | ], | 421 | ], |
| 426 | ), | 422 | ), |
| 427 | ], | 423 | ], |
| 428 | ), | 424 | ), |
| 429 | ), | 425 | ), |
| 426 | + interactionEnabled: | ||
| 427 | + controller.isFriend && | ||
| 428 | + (activity > 0 || exercise > 0 || stand > 0), | ||
| 430 | ); | 429 | ); |
| 431 | }), | 430 | }), |
| 432 | ); | 431 | ); |
| @@ -441,6 +440,7 @@ class _TodaySummaryCard extends StatelessWidget { | @@ -441,6 +440,7 @@ class _TodaySummaryCard extends StatelessWidget { | ||
| 441 | required this.actionText, | 440 | required this.actionText, |
| 442 | required this.metrics, | 441 | required this.metrics, |
| 443 | required this.rightWidget, | 442 | required this.rightWidget, |
| 443 | + this.interactionEnabled, | ||
| 444 | }); | 444 | }); |
| 445 | 445 | ||
| 446 | final String iconAsset; | 446 | final String iconAsset; |
| @@ -450,6 +450,7 @@ class _TodaySummaryCard extends StatelessWidget { | @@ -450,6 +450,7 @@ class _TodaySummaryCard extends StatelessWidget { | ||
| 450 | final List<Widget> metrics; | 450 | final List<Widget> metrics; |
| 451 | 451 | ||
| 452 | final Widget rightWidget; | 452 | final Widget rightWidget; |
| 453 | + final bool? interactionEnabled; | ||
| 453 | 454 | ||
| 454 | @override | 455 | @override |
| 455 | Widget build(BuildContext context) { | 456 | Widget build(BuildContext context) { |
| @@ -466,12 +467,7 @@ class _TodaySummaryCard extends StatelessWidget { | @@ -466,12 +467,7 @@ class _TodaySummaryCard extends StatelessWidget { | ||
| 466 | children: [ | 467 | children: [ |
| 467 | Row( | 468 | Row( |
| 468 | children: [ | 469 | children: [ |
| 469 | - Image.asset( | ||
| 470 | - iconAsset, | ||
| 471 | - width: 16, | ||
| 472 | - height: 16, | ||
| 473 | - color: iconColor, | ||
| 474 | - ), | 470 | + Image.asset(iconAsset, width: 16, height: 16, color: iconColor), |
| 475 | const SizedBox(width: 4), | 471 | const SizedBox(width: 4), |
| 476 | Text( | 472 | Text( |
| 477 | title, | 473 | title, |
| @@ -481,6 +477,10 @@ class _TodaySummaryCard extends StatelessWidget { | @@ -481,6 +477,10 @@ class _TodaySummaryCard extends StatelessWidget { | ||
| 481 | fontWeight: FontWeight.w600, | 477 | fontWeight: FontWeight.w600, |
| 482 | ), | 478 | ), |
| 483 | ), | 479 | ), |
| 480 | + if (interactionEnabled != null) ...[ | ||
| 481 | + const SizedBox(width: 8), | ||
| 482 | + FriendInteractionButton(enabled: interactionEnabled!), | ||
| 483 | + ], | ||
| 484 | const Spacer(), | 484 | const Spacer(), |
| 485 | Text( | 485 | Text( |
| 486 | actionText, | 486 | actionText, |
| @@ -510,7 +510,7 @@ class _TodaySummaryCard extends StatelessWidget { | @@ -510,7 +510,7 @@ class _TodaySummaryCard extends StatelessWidget { | ||
| 510 | ], | 510 | ], |
| 511 | ), | 511 | ), |
| 512 | ), | 512 | ), |
| 513 | - rightWidget | 513 | + rightWidget, |
| 514 | ], | 514 | ], |
| 515 | ), | 515 | ), |
| 516 | ], | 516 | ], |
| 1 | +import 'package:doublefeel_flutter/app/routes/app_pages.dart'; | ||
| 2 | +import 'package:flutter/material.dart'; | ||
| 3 | +import 'package:get/get.dart'; | ||
| 4 | + | ||
| 5 | +class FriendInteractionButton extends StatelessWidget { | ||
| 6 | + const FriendInteractionButton({ | ||
| 7 | + super.key, | ||
| 8 | + required this.enabled, | ||
| 9 | + this.size = 24, | ||
| 10 | + }); | ||
| 11 | + | ||
| 12 | + final bool enabled; | ||
| 13 | + final double size; | ||
| 14 | + | ||
| 15 | + @override | ||
| 16 | + Widget build(BuildContext context) => Opacity( | ||
| 17 | + opacity: enabled ? 1 : 0.4, | ||
| 18 | + child: GestureDetector( | ||
| 19 | + behavior: HitTestBehavior.opaque, | ||
| 20 | + onTap: enabled ? _openInteraction : null, | ||
| 21 | + child: SizedBox( | ||
| 22 | + width: size, | ||
| 23 | + height: size, | ||
| 24 | + child: DecoratedBox( | ||
| 25 | + decoration: const BoxDecoration( | ||
| 26 | + color: Color(0xFF845EEE), | ||
| 27 | + shape: BoxShape.circle, | ||
| 28 | + ), | ||
| 29 | + child: Padding( | ||
| 30 | + padding: EdgeInsets.all(size / 8), | ||
| 31 | + child: Image.asset('assets/images/interact/icon_stick.png'), | ||
| 32 | + ), | ||
| 33 | + ), | ||
| 34 | + ), | ||
| 35 | + ), | ||
| 36 | + ); | ||
| 37 | + | ||
| 38 | + void _openInteraction() => Get.toNamed(Routes.INTERACT); | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +class FriendInteractionFloatingEntry extends StatelessWidget { | ||
| 42 | + const FriendInteractionFloatingEntry({super.key, this.size = 72}); | ||
| 43 | + | ||
| 44 | + final double size; | ||
| 45 | + | ||
| 46 | + @override | ||
| 47 | + Widget build(BuildContext context) => GestureDetector( | ||
| 48 | + behavior: HitTestBehavior.opaque, | ||
| 49 | + onTap: () => Get.toNamed(Routes.INTERACT), | ||
| 50 | + child: Image.asset( | ||
| 51 | + 'assets/images/interact/interaction_float.png', | ||
| 52 | + width: size, | ||
| 53 | + height: size, | ||
| 54 | + fit: BoxFit.contain, | ||
| 55 | + ), | ||
| 56 | + ); | ||
| 57 | +} |
-
Please register or login to post a comment