diff --git a/lib/app/core/bindings/initial_binding.dart b/lib/app/core/bindings/initial_binding.dart index 5fe1af3..71f6b46 100644 --- a/lib/app/core/bindings/initial_binding.dart +++ b/lib/app/core/bindings/initial_binding.dart @@ -24,7 +24,7 @@ class InitialBinding implements Bindings { Get.put(GetStorage(), permanent: true); Get.put(Uuid(), permanent: true); Get.put(HttpProvider()); - // Get.put(SQLiteService()); + Get.put(SQLiteService()); Get.put(DatabaseService()); Get.put(NetworkStatusService()); Get.put(UpgraderService()); diff --git a/lib/app/features/navigation/presentation/bindings/navigation_binding.dart b/lib/app/features/navigation/presentation/bindings/navigation_binding.dart index d14a35f..ae97bc4 100644 --- a/lib/app/features/navigation/presentation/bindings/navigation_binding.dart +++ b/lib/app/features/navigation/presentation/bindings/navigation_binding.dart @@ -1,6 +1,7 @@ import 'package:get/get.dart'; import 'package:problem_check_system/app/core/models/problem_sync_status.dart'; import 'package:problem_check_system/app/core/repositories/auth_repository.dart'; +import 'package:problem_check_system/app/core/services/network_status_service.dart'; import 'package:problem_check_system/app/features/problem/data/repositories/problem_repository.dart'; import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_list_controller.dart'; import 'package:problem_check_system/app/features/navigation/presentation/controllers/navigation_controller.dart'; @@ -11,7 +12,11 @@ class NavigationBinding extends Bindings { @override void dependencies() { /// 注册主页控制器 - Get.lazyPut(() => NavigationController()); + Get.lazyPut( + () => NavigationController( + networkStatusService: Get.find(), + ), + ); Get.put(ProblemStateManager(uuid: Get.find(), authRepository: Get.find())); Get.lazyPut(() => EnterpriseListController()); diff --git a/lib/app/features/navigation/presentation/controllers/navigation_controller.dart b/lib/app/features/navigation/presentation/controllers/navigation_controller.dart index 79543af..92352e7 100644 --- a/lib/app/features/navigation/presentation/controllers/navigation_controller.dart +++ b/lib/app/features/navigation/presentation/controllers/navigation_controller.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:problem_check_system/app/core/routes/app_routes.dart'; +import 'package:problem_check_system/app/core/services/network_status_service.dart'; import 'package:problem_check_system/app/features/enterprise/presentation/pages/enterprise_list_page.dart'; import 'package:problem_check_system/app/features/home/views/home_page.dart'; import 'package:problem_check_system/app/features/my/views/my_page.dart'; @@ -7,6 +10,17 @@ import 'package:problem_check_system/app/features/problem/presentation/views/pro class NavigationController extends GetxController { var selectedIndex = 0.obs; + + /// floatingButton 拖动 + final double _fabSize = 56.0; + final double _edgePaddingX = 27.0.w; + final double _edgePaddingY = 111.0.h; + final fabUploadPosition = Offset(337.0, 703.7).obs; + final NetworkStatusService networkStatusService; + NavigationController({required this.networkStatusService}); + + /// get 选中的 + RxBool get isOnline => networkStatusService.isOnline; // 页面列表 final List pages = [ const HomePage(), @@ -20,4 +34,67 @@ class NavigationController extends GetxController { void changePageIndex(int index) { selectedIndex.value = index; } + + /// floatingButton更新位置 + void updateFabUploadPosition(Offset delta) { + final screenWidth = ScreenUtil().screenWidth; + final screenHeight = ScreenUtil().screenHeight; + + Offset newPosition = fabUploadPosition.value + delta; + + // 限制水平范围:按钮左边缘与屏幕左边缘的距离 + double clampedDx = newPosition.dx.clamp( + _edgePaddingX, + screenWidth - _fabSize - _edgePaddingX, + ); + + // 限制垂直范围:按钮上边缘与屏幕上边缘的距离 + double clampedDy = newPosition.dy.clamp( + _edgePaddingY, + screenHeight - _fabSize - _edgePaddingY, + ); + + fabUploadPosition.value = Offset(clampedDx, clampedDy); + } + + /// floatingButton 贴靠 + void snapToEdge() { + final screenWidth = ScreenUtil().screenWidth; + + // 获取当前按钮的水平中心点 + final buttonCenterDx = fabUploadPosition.value.dx + _fabSize / 2; + + double newDx; + + // 判断按钮中心点位于屏幕的左半部分还是右半部分 + if (buttonCenterDx < screenWidth / 2) { + // 贴靠到左侧,按钮左边缘与屏幕左边缘距离为 _edgePaddingX + newDx = _edgePaddingX; + } else { + // 贴靠到右侧,按钮右边缘与屏幕右边缘距离为 _edgePaddingX + newDx = screenWidth - _fabSize - _edgePaddingX; + } + + // 关键:只更新水平位置,垂直位置保持不变 + fabUploadPosition.value = Offset(newDx, fabUploadPosition.value.dy); + } + + void handleFabUploadTap() { + // 使用 switch 语句判断当前选中的页面索引 + switch (selectedIndex.value) { + case 1: // 企业列表页面 + Get.log("当前在企业页面,准备跳转到企业数据上传页..."); + // 使用命名路由进行跳转,这是最佳实践 + // Get.toNamed(AppRoutes.ENTERPRISE_UPLOAD); + break; + case 2: // 问题列表页面 + Get.log("当前在问题页面,准备跳转到问题上传页..."); + // Get.toNamed(AppRoutes.PROBLEM_UPLOAD); + break; + default: + // 在其他页面(如首页、我的)点击时,可以不响应或给一个提示 + Get.snackbar("提示", "当前页面没有可上传的内容", snackPosition: SnackPosition.BOTTOM); + break; + } + } } diff --git a/lib/app/features/navigation/presentation/pages/navigation_page.dart b/lib/app/features/navigation/presentation/pages/navigation_page.dart index 5d84ec3..8491f82 100644 --- a/lib/app/features/navigation/presentation/pages/navigation_page.dart +++ b/lib/app/features/navigation/presentation/pages/navigation_page.dart @@ -1,3 +1,5 @@ +// lib/app/features/navigation/presentation/pages/navigation_page.dart + import 'package:curved_navigation_bar/curved_navigation_bar.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -8,22 +10,58 @@ class NavigationPage extends GetView { @override Widget build(BuildContext context) { - return Scaffold( - bottomNavigationBar: CurvedNavigationBar( - backgroundColor: Colors.transparent, - color: Colors.blueAccent, - items: [ - Icon(Icons.home, color: Colors.white, size: 30), - Icon(Icons.business, color: Colors.white, size: 30), - Icon(Icons.list, color: Colors.white, size: 30), - Icon(Icons.person, color: Colors.white, size: 30), - ], - onTap: (index) { - //Handle button tap - controller.changePageIndex(index); - }, - ), - body: Obx(() => controller.currentPage), + // 1. 将整个页面包裹在一个 Stack 中,以创建一个全局的坐标系 + return Stack( + children: [ + // 2. Scaffold 作为 Stack 的底层,负责主要的页面结构 + Scaffold( + // 3. 移除 Scaffold 的 floatingActionButton 属性 + bottomNavigationBar: CurvedNavigationBar( + backgroundColor: Colors.transparent, + color: Colors.blueAccent, + items: const [ + Icon(Icons.home, color: Colors.white, size: 30), + Icon(Icons.business, color: Colors.white, size: 30), + Icon(Icons.list, color: Colors.white, size: 30), + Icon(Icons.person, color: Colors.white, size: 30), + ], + onTap: (index) { + controller.changePageIndex(index); + }, + ), + body: Obx(() => controller.currentPage), + ), + + // 4. 将悬浮按钮作为 Stack 的顶层,使用 Positioned 进行精确定位 + Obx(() { + final isOnline = controller.isOnline.value; + return Positioned( + left: controller.fabUploadPosition.value.dx, + top: controller.fabUploadPosition.value.dy, + child: GestureDetector( + onPanUpdate: (details) { + controller.updateFabUploadPosition(details.delta); + }, + onPanEnd: (details) { + controller.snapToEdge(); + }, + child: FloatingActionButton( + heroTag: "btn_upload", + onPressed: isOnline + ? () => controller.handleFabUploadTap() + : null, + foregroundColor: Colors.white, + backgroundColor: isOnline ? Colors.red[300] : Colors.grey[400], + child: Icon( + isOnline + ? Icons.file_upload_outlined + : Icons.cloud_off_outlined, + ), + ), + ), + ); + }), + ], ); } }