feat: 完善主页页面、消息页面开发
BIN
assets/icon/access_authorization.png
Normal file
|
After Width: | Height: | Size: 886 B |
BIN
assets/icon/access_management.png
Normal file
|
After Width: | Height: | Size: 738 B |
BIN
assets/icon/announcement.png
Normal file
|
After Width: | Height: | Size: 563 B |
BIN
assets/icon/approval_record.png
Normal file
|
After Width: | Height: | Size: 764 B |
BIN
assets/icon/attendance_setting.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/icon/attendance_statistics.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/icon/bar/home.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
assets/icon/bar/home_selected.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/icon/bar/mine.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/icon/bar/mine_selected.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/icon/bar/notification.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/icon/bar/notification_selected.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/icon/broadcast.png
Normal file
|
After Width: | Height: | Size: 619 B |
BIN
assets/icon/business_trip.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/icon/call_relationship.png
Normal file
|
After Width: | Height: | Size: 833 B |
BIN
assets/icon/call_reminder.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/icon/device_management.png
Normal file
|
After Width: | Height: | Size: 502 B |
BIN
assets/icon/go_out.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 431 B |
BIN
assets/icon/icon_table_menu.png
Normal file
|
After Width: | Height: | Size: 662 B |
BIN
assets/icon/info_publish.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/icon/initiate_approval.png
Normal file
|
After Width: | Height: | Size: 857 B |
BIN
assets/icon/intelligent_analysis.png
Normal file
|
After Width: | Height: | Size: 949 B |
BIN
assets/icon/intelligent_inspection.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icon/intercom_device.png
Normal file
|
After Width: | Height: | Size: 894 B |
BIN
assets/icon/leave_request.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/icon/make_up_card.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/icon/mobile_checkin.png
Normal file
|
After Width: | Height: | Size: 986 B |
BIN
assets/icon/my_attendance.png
Normal file
|
After Width: | Height: | Size: 480 B |
BIN
assets/icon/my_visitor.png
Normal file
|
After Width: | Height: | Size: 887 B |
BIN
assets/icon/one_click_open_door.png
Normal file
|
After Width: | Height: | Size: 712 B |
BIN
assets/icon/password_open_door.png
Normal file
|
After Width: | Height: | Size: 728 B |
BIN
assets/icon/person_capture.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/icon/personnel_management.png
Normal file
|
After Width: | Height: | Size: 840 B |
BIN
assets/icon/team_qrcode.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/icon/traffic_record.png
Normal file
|
After Width: | Height: | Size: 887 B |
BIN
assets/icon/vehicle_management.png
Normal file
|
After Width: | Height: | Size: 972 B |
BIN
assets/icon/video_center.png
Normal file
|
After Width: | Height: | Size: 707 B |
BIN
assets/icon/video_search.png
Normal file
|
After Width: | Height: | Size: 616 B |
BIN
assets/icon/visitor_invitation.png
Normal file
|
After Width: | Height: | Size: 903 B |
BIN
assets/icon/visitor_management.png
Normal file
|
After Width: | Height: | Size: 949 B |
@ -3,4 +3,61 @@ class AppImages{
|
||||
static const String iconOneKeyDoor = 'assets/icon/icon_one_key_door.png';
|
||||
static const String iconOneKeyDoorKey = 'assets/icon/icon_one_key_door_key.png';
|
||||
static const String bgOneKeyDoor = 'assets/images/bg_one_key_door.png';
|
||||
static const String mockImage = 'assets/images/mockImage.jpg';
|
||||
|
||||
// 视频类图标
|
||||
static const String iconVideoCenter = 'assets/icon/video_center.png';
|
||||
static const String iconIntelligentInspection = 'assets/icon/intelligent_inspection.png';
|
||||
static const String iconVideoSearch = 'assets/icon/video_search.png';
|
||||
static const String iconIntelligentAnalysis = 'assets/icon/intelligent_analysis.png';
|
||||
static const String iconPersonCapture = 'assets/icon/person_capture.png';
|
||||
static const String iconVehicleManagement = 'assets/icon/vehicle_management.png';
|
||||
|
||||
// 人员通行类图标
|
||||
static const String iconAccessManagement = 'assets/icon/access_management.png';
|
||||
static const String iconAccessAuthorization = 'assets/icon/access_authorization.png';
|
||||
static const String iconOneClickOpenDoor = 'assets/icon/one_click_open_door.png';
|
||||
static const String iconPasswordOpenDoor = 'assets/icon/password_open_door.png';
|
||||
static const String iconTrafficRecord = 'assets/icon/traffic_record.png';
|
||||
static const String iconVisitorManagement = 'assets/icon/visitor_management.png';
|
||||
static const String iconMyVisitor = 'assets/icon/my_visitor.png';
|
||||
static const String iconVisitorInvitation = 'assets/icon/visitor_invitation.png';
|
||||
|
||||
// 考勤类图标
|
||||
static const String iconAttendanceSetting = 'assets/icon/attendance_setting.png';
|
||||
static const String iconAttendanceStatistics = 'assets/icon/attendance_statistics.png';
|
||||
static const String iconMyAttendance = 'assets/icon/my_attendance.png';
|
||||
static const String iconMobileCheckin = 'assets/icon/mobile_checkin.png';
|
||||
static const String iconLeaveRequest = 'assets/icon/leave_request.png';
|
||||
static const String iconMakeUpCard = 'assets/icon/make_up_card.png';
|
||||
static const String iconBusinessTrip = 'assets/icon/business_trip.png';
|
||||
static const String iconGoOut = 'assets/icon/go_out.png';
|
||||
|
||||
// 审批类图标
|
||||
static const String iconInitiateApproval = 'assets/icon/initiate_approval.png';
|
||||
static const String iconApprovalRecord = 'assets/icon/approval_record.png';
|
||||
|
||||
// 可视对讲类图标
|
||||
static const String iconIntercomDevice = 'assets/icon/intercom_device.png';
|
||||
static const String iconCallRelationship = 'assets/icon/call_relationship.png';
|
||||
static const String iconCallReminder = 'assets/icon/call_reminder.png';
|
||||
|
||||
// 信息发布类图标
|
||||
static const String iconBroadcast = 'assets/icon/broadcast.png';
|
||||
static const String iconAnnouncement = 'assets/icon/announcement.png';
|
||||
static const String iconInfoPublish = 'assets/icon/info_publish.png';
|
||||
|
||||
// 基础应用类图标
|
||||
static const String iconPersonnelManagement = 'assets/icon/personnel_management.png';
|
||||
static const String iconTeamQrcode = 'assets/icon/team_qrcode.png';
|
||||
static const String iconDeviceManagement = 'assets/icon/device_management.png';
|
||||
static const String iconTableMenu = 'assets/icon/icon_table_menu.png';
|
||||
|
||||
// 底部导航栏图标
|
||||
static const String iconHome = 'assets/icon/bar/home.png';
|
||||
static const String iconHomeSelected = 'assets/icon/bar/home_selected.png';
|
||||
static const String iconNotification = 'assets/icon/bar/notification.png';
|
||||
static const String iconNotificationSelected = 'assets/icon/bar/notification_selected.png';
|
||||
static const String iconMine = 'assets/icon/bar/mine.png';
|
||||
static const String iconMineSelected = 'assets/icon/bar/mine_selected.png';
|
||||
}
|
||||
@ -4,43 +4,89 @@ import 'package:get/get.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:starwork_flutter/base/app_permission.dart';
|
||||
import 'package:starwork_flutter/base/base_controller.dart';
|
||||
import 'package:starwork_flutter/views/main/main_controller.dart';
|
||||
|
||||
class HomeController extends BaseController {
|
||||
|
||||
final mainController = Get.find<MainController>();
|
||||
|
||||
final isOpenNotificationPermission = false.obs;
|
||||
|
||||
// 页面加载状态
|
||||
final isLoading = true.obs;
|
||||
|
||||
var carouselCurrentIndex = 0.obs;
|
||||
|
||||
|
||||
// 渐变颜色列表
|
||||
final List<Color> gradientColors = [
|
||||
const Color(0xFFBFCBEF), // #bfcbef
|
||||
const Color(0xFFECBE9B), // 原来的颜色
|
||||
const Color(0xFFD5F3D5), // 清新绿色
|
||||
const Color(0xFFFFB6C1), // 温柔粉色
|
||||
const Color(0xFFBFCBEF),
|
||||
const Color(0xFFECBE9B),
|
||||
];
|
||||
|
||||
|
||||
// 当前渐变颜色
|
||||
var currentGradientColor = const Color(0xFFBFCBEF).obs;
|
||||
|
||||
@override
|
||||
void onInit() async {
|
||||
super.onInit();
|
||||
isOpenNotificationPermission.value = await AppPermission.checkPermission(
|
||||
permission: Permission.notification,
|
||||
);
|
||||
|
||||
// 模拟初始化加载
|
||||
await _initializeData();
|
||||
|
||||
// 监听轮播图切换,更新渐变颜色
|
||||
carouselCurrentIndex.listen((index) {
|
||||
updateGradientColor(index);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 初始化数据
|
||||
Future<void> _initializeData() async {
|
||||
// 模拟加载延迟(2秒)
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
// 检查通知权限
|
||||
isOpenNotificationPermission.value = await AppPermission.checkPermission(
|
||||
permission: Permission.notification,
|
||||
);
|
||||
|
||||
// 加载完成,隐藏骨架屏
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
// 根据轮播图索引更新渐变颜色
|
||||
void updateGradientColor(int index) {
|
||||
if (index < gradientColors.length) {
|
||||
currentGradientColor.value = gradientColors[index];
|
||||
} else {
|
||||
// 如果索引超出颜色数量,使用模运算轮环
|
||||
currentGradientColor.value = gradientColors[index % gradientColors.length];
|
||||
currentGradientColor.value =
|
||||
gradientColors[index % gradientColors.length];
|
||||
}
|
||||
}
|
||||
|
||||
// 首页刷新方法
|
||||
Future<void> refreshHome() async {
|
||||
// 显示加载状态(可选)
|
||||
// isLoading.value = true;
|
||||
|
||||
// 模拟网络请求延迟
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
|
||||
// 这里可以添加实际的刷新逻辑,比如:
|
||||
// 1. 重新获取轮播图数据
|
||||
// 2. 刷新统计数据
|
||||
// 3. 更新功能列表
|
||||
// 4. 刷新考勤图表数据
|
||||
// 5. 更新门禁列表
|
||||
|
||||
// 重新检查通知权限
|
||||
isOpenNotificationPermission.value = await AppPermission.checkPermission(
|
||||
permission: Permission.notification,
|
||||
);
|
||||
|
||||
// 隐藏加载状态
|
||||
// isLoading.value = false;
|
||||
|
||||
print('首页数据刷新完成');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
@ -18,9 +17,6 @@ import 'home_controller.dart';
|
||||
class HomeView extends GetView<HomeController> {
|
||||
const HomeView({super.key});
|
||||
|
||||
static final GlobalKey<ScaffoldState> _scaffoldKey =
|
||||
GlobalKey<ScaffoldState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(
|
||||
@ -36,68 +32,26 @@ class HomeView extends GetView<HomeController> {
|
||||
stops: const [0.1, 1.0], // 第一个颜色到10%,然后第二个颜色开始直到100%
|
||||
),
|
||||
),
|
||||
child: Scaffold(
|
||||
key: _scaffoldKey,
|
||||
backgroundColor: Colors.transparent,
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
width: 1.sw,
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 4.h),
|
||||
child: Column(
|
||||
children: [
|
||||
// 固定的上半部分
|
||||
_buildPageHead(context),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
_buildSystemNotificationPermissionRow(),
|
||||
child: SafeArea(
|
||||
child: Container(
|
||||
width: 1.sw,
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 4.h),
|
||||
child: Column(
|
||||
children: [
|
||||
// 固定的上半部分
|
||||
_buildPageHead(context),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
_buildSystemNotificationPermissionRow(),
|
||||
|
||||
// 可滚动的下半部分
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
HomeCarouselAreaWidget(
|
||||
carouselCurrentIndex:
|
||||
controller.carouselCurrentIndex,
|
||||
),
|
||||
HomeTeamNoticeRowWidget(),
|
||||
HomeStatisticsRowWidget(
|
||||
personCount: 12,
|
||||
deviceCount: 1,
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
HomeFunctionListAreaWidget(),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
HomeOnButtonDoorOpeningWidget(
|
||||
doorList: const [
|
||||
'主门门禁',
|
||||
'车库门禁',
|
||||
'后门门禁',
|
||||
'单元门门禁',
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
HomeAttendanceChartAreaWidget(),
|
||||
SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 可滚动的下半部分(带下拉刷新)
|
||||
Expanded(
|
||||
child: _buildRefreshableContent(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
drawer: HomeLeftDrawerWidget(
|
||||
teamList: ['家庭群组', '测试团队1', '测试团队2'],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -106,7 +60,8 @@ class HomeView extends GetView<HomeController> {
|
||||
_buildPageHead(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_scaffoldKey.currentState?.openDrawer();
|
||||
// 使用MainController的专用方法
|
||||
controller.mainController.openDrawer();
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@ -199,4 +154,68 @@ class HomeView extends GetView<HomeController> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 带下拉刷新的内容区域
|
||||
Widget _buildRefreshableContent() {
|
||||
return RefreshIndicator(
|
||||
onRefresh: _onRefresh,
|
||||
// 基础样式配置
|
||||
color: const Color(0xFF4A90E2), // 刷新指示器颜色
|
||||
backgroundColor: Colors.white, // 背景颜色
|
||||
|
||||
// 控制下拉触发距离的关键属性
|
||||
displacement: 60.0, // 刷新指示器距离顶部的距离
|
||||
edgeOffset: 0.0, // 边缘偏移量
|
||||
|
||||
// 控制下拉幅度的关键属性
|
||||
triggerMode: RefreshIndicatorTriggerMode.onEdge, // 触发模式
|
||||
|
||||
// 描边宽度
|
||||
strokeWidth: 2.5, // 刷新指示器的描边宽度
|
||||
|
||||
// 语义标签(用于无障碍功能)
|
||||
semanticsLabel: '下拉刷新首页内容',
|
||||
semanticsValue: '刷新中...',
|
||||
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(
|
||||
// 添加弹性滚动效果
|
||||
parent: BouncingScrollPhysics(),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
HomeCarouselAreaWidget(
|
||||
carouselCurrentIndex: controller.carouselCurrentIndex,
|
||||
),
|
||||
HomeTeamNoticeRowWidget(),
|
||||
HomeStatisticsRowWidget(
|
||||
personCount: 12,
|
||||
deviceCount: 1,
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
HomeFunctionListAreaWidget(),
|
||||
SizedBox(height: 10.h),
|
||||
HomeOnButtonDoorOpeningWidget(
|
||||
doorList: const [
|
||||
'主门门禁',
|
||||
'车库门禁',
|
||||
'后门门禁',
|
||||
'单元门门禁',
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
HomeAttendanceChartAreaWidget(),
|
||||
SizedBox(height: 10.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 处理下拉刷新
|
||||
Future<void> _onRefresh() async {
|
||||
// 调用controller的刷新方法
|
||||
await controller.refreshHome();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/common/constant/app_images.dart';
|
||||
|
||||
class HomeCarouselAreaWidget extends StatefulWidget {
|
||||
const HomeCarouselAreaWidget({
|
||||
@ -18,9 +19,9 @@ class HomeCarouselAreaWidget extends StatefulWidget {
|
||||
|
||||
class _HomeCarouselAreaWidgetState extends State<HomeCarouselAreaWidget> {
|
||||
final List<String> imgList = [
|
||||
'assets/images/mockImage.jpg',
|
||||
'assets/images/mockImage.jpg',
|
||||
'assets/images/mockImage.jpg',
|
||||
AppImages.mockImage,
|
||||
AppImages.mockImage,
|
||||
AppImages.mockImage,
|
||||
];
|
||||
|
||||
@override
|
||||
|
||||
@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:starwork_flutter/common/constant/app_images.dart';
|
||||
|
||||
class HomeFunctionListAreaWidget extends StatefulWidget {
|
||||
HomeFunctionListAreaWidget({super.key});
|
||||
@ -15,18 +16,81 @@ class _HomeFunctionListAreaWidgetState extends State<HomeFunctionListAreaWidget>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
|
||||
// 图片缓存Map
|
||||
final Map<String, ImageProvider> _imageCache = {};
|
||||
|
||||
// 标记是否已经预加载
|
||||
bool _isPreloaded = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: 6, vsync: this);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
// 在这里预加载图片,确保MediaQuery可用
|
||||
if (!_isPreloaded) {
|
||||
_preloadImages();
|
||||
_isPreloaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 预加载所有图片资源
|
||||
void _preloadImages() {
|
||||
final List<String> imagePaths = [
|
||||
AppImages.iconAccessManagement,
|
||||
AppImages.iconAccessAuthorization,
|
||||
AppImages.iconOneClickOpenDoor,
|
||||
AppImages.iconPasswordOpenDoor,
|
||||
AppImages.iconTrafficRecord,
|
||||
AppImages.iconVisitorManagement,
|
||||
AppImages.iconMyVisitor,
|
||||
AppImages.iconVisitorInvitation,
|
||||
AppImages.iconAttendanceSetting,
|
||||
AppImages.iconAttendanceStatistics,
|
||||
AppImages.iconMyAttendance,
|
||||
AppImages.iconMobileCheckin,
|
||||
AppImages.iconLeaveRequest,
|
||||
AppImages.iconMakeUpCard,
|
||||
AppImages.iconBusinessTrip,
|
||||
AppImages.iconGoOut,
|
||||
AppImages.iconInitiateApproval,
|
||||
AppImages.iconApprovalRecord,
|
||||
AppImages.iconIntercomDevice,
|
||||
AppImages.iconCallRelationship,
|
||||
AppImages.iconCallReminder,
|
||||
AppImages.iconBroadcast,
|
||||
AppImages.iconAnnouncement,
|
||||
AppImages.iconInfoPublish,
|
||||
AppImages.iconPersonnelManagement,
|
||||
AppImages.iconTeamQrcode,
|
||||
AppImages.iconDeviceManagement,
|
||||
AppImages.iconVideoCenter,
|
||||
AppImages.iconIntelligentInspection,
|
||||
AppImages.iconVideoSearch,
|
||||
AppImages.iconIntelligentAnalysis,
|
||||
AppImages.iconPersonCapture,
|
||||
];
|
||||
|
||||
for (String path in imagePaths) {
|
||||
_imageCache[path] = AssetImage(path);
|
||||
// 预缓存图片,使用异步方式避免阻塞
|
||||
precacheImage(_imageCache[path]!, context).catchError((error) {
|
||||
// 处理预加载失败的情况
|
||||
debugPrint('预加载图片失败: $path, 错误: $error');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
@ -59,7 +123,7 @@ class _HomeFunctionListAreaWidgetState extends State<HomeFunctionListAreaWidget>
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
Flexible(
|
||||
child: TabBar(
|
||||
controller: _tabController,
|
||||
isScrollable: true,
|
||||
@ -97,10 +161,22 @@ class _HomeFunctionListAreaWidgetState extends State<HomeFunctionListAreaWidget>
|
||||
// 处理菜单点击事件
|
||||
print('菜单被点击');
|
||||
},
|
||||
child: Icon(
|
||||
Icons.menu,
|
||||
size: 18.sp,
|
||||
color: Colors.grey[600],
|
||||
child: Image(
|
||||
image: AssetImage(AppImages.iconTableMenu),
|
||||
width: 20.w,
|
||||
height: 20.w,
|
||||
fit: BoxFit.contain,
|
||||
gaplessPlayback: true,
|
||||
// 防止闪烁
|
||||
filterQuality: FilterQuality.medium,
|
||||
// 优化过滤质量
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Icon(
|
||||
Icons.image_not_supported,
|
||||
size: 26.sp,
|
||||
color: Colors.grey,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -121,27 +197,28 @@ class _HomeFunctionListAreaWidgetState extends State<HomeFunctionListAreaWidget>
|
||||
// 每个分类对应的功能列表
|
||||
final Map<String, List<Map<String, dynamic>>> functionData = {
|
||||
'人员通行': [
|
||||
{'icon': Icons.door_front_door, 'title': '门禁管理'},
|
||||
{'icon': Icons.people, 'title': '门禁授权'},
|
||||
{'icon': Icons.access_time, 'title': '一键开门'},
|
||||
{'icon': Icons.card_membership, 'title': '密码开门'},
|
||||
{'icon': Icons.face, 'title': '我的访客'},
|
||||
{'icon': Icons.fingerprint, 'title': '访客统计'},
|
||||
{'icon': Icons.security, 'title': '访客邀约'},
|
||||
{'icon': AppImages.iconAccessManagement, 'title': '门禁管理'},
|
||||
{'icon': AppImages.iconAccessAuthorization, 'title': '门禁授权'},
|
||||
{'icon': AppImages.iconOneClickOpenDoor, 'title': '一键开门'},
|
||||
{'icon': AppImages.iconPasswordOpenDoor, 'title': '密码开门'},
|
||||
{'icon': AppImages.iconTrafficRecord, 'title': '通行记录'},
|
||||
{'icon': AppImages.iconVisitorManagement, 'title': '访客管理'},
|
||||
{'icon': AppImages.iconMyVisitor, 'title': '我的访客'},
|
||||
{'icon': AppImages.iconVisitorInvitation, 'title': '访客邀约'},
|
||||
],
|
||||
'考勤': [
|
||||
{'icon': Icons.schedule, 'title': '考勤设置'},
|
||||
{'icon': Icons.assignment, 'title': '审批记录'},
|
||||
{'icon': Icons.broadcast_on_personal, 'title': '广播'},
|
||||
{'icon': Icons.announcement, 'title': '信息发布'},
|
||||
{'icon': Icons.bar_chart, 'title': '统计报表'},
|
||||
{'icon': Icons.calendar_today, 'title': '排班管理'},
|
||||
{'icon': Icons.access_alarm, 'title': '打卡记录'},
|
||||
{'icon': Icons.person_add, 'title': '添加常用'},
|
||||
{'icon': AppImages.iconAttendanceSetting, 'title': '考勤设置'},
|
||||
{'icon': AppImages.iconAttendanceStatistics, 'title': '考勤统计'},
|
||||
{'icon': AppImages.iconMyAttendance, 'title': '我的考勤'},
|
||||
{'icon': AppImages.iconMobileCheckin, 'title': '手机打卡'},
|
||||
{'icon': AppImages.iconLeaveRequest, 'title': '请假'},
|
||||
{'icon': AppImages.iconMakeUpCard, 'title': '补卡'},
|
||||
{'icon': AppImages.iconBusinessTrip, 'title': '出差'},
|
||||
{'icon': AppImages.iconGoOut, 'title': '外出'},
|
||||
],
|
||||
'审批': [
|
||||
{'icon': Icons.approval, 'title': '待审批'},
|
||||
{'icon': Icons.check_circle, 'title': '已审批'},
|
||||
{'icon': AppImages.iconInitiateApproval, 'title': '发起审批'},
|
||||
{'icon': AppImages.iconApprovalRecord, 'title': '审批记录'},
|
||||
{'icon': Icons.pending, 'title': '审批中'},
|
||||
{'icon': Icons.history, 'title': '审批历史'},
|
||||
{'icon': Icons.rule, 'title': '审批规则'},
|
||||
@ -150,34 +227,34 @@ class _HomeFunctionListAreaWidgetState extends State<HomeFunctionListAreaWidget>
|
||||
{'icon': Icons.analytics, 'title': '审批统计'},
|
||||
],
|
||||
'可视对讲': [
|
||||
{'icon': Icons.call, 'title': '对讲设备'},
|
||||
{'icon': Icons.call_received, 'title': '呼叫记录'},
|
||||
{'icon': AppImages.iconIntercomDevice, 'title': '对讲设备'},
|
||||
{'icon': AppImages.iconCallRelationship, 'title': '呼叫关系'},
|
||||
{'icon': AppImages.iconCallReminder, 'title': '呼叫提醒'},
|
||||
{'icon': Icons.video_call, 'title': '视频通话'},
|
||||
{'icon': Icons.volume_up, 'title': '广播通知'},
|
||||
{'icon': Icons.settings_phone, 'title': '设备设置'},
|
||||
{'icon': Icons.group_add, 'title': '群组管理'},
|
||||
{'icon': Icons.record_voice_over, 'title': '语音留言'},
|
||||
{'icon': Icons.emergency, 'title': '紧急呼叫'},
|
||||
],
|
||||
'信息发布': [
|
||||
{'icon': Icons.campaign, 'title': '发布信息'},
|
||||
{'icon': Icons.list_alt, 'title': '信息列表'},
|
||||
{'icon': AppImages.iconBroadcast, 'title': '广播'},
|
||||
{'icon': AppImages.iconAnnouncement, 'title': '公告'},
|
||||
{'icon': AppImages.iconInfoPublish, 'title': '信息发布'},
|
||||
{'icon': Icons.schedule_send, 'title': '定时发布'},
|
||||
{'icon': Icons.group, 'title': '目标群体'},
|
||||
{'icon': Icons.image, 'title': '多媒体'},
|
||||
{'icon': Icons.analytics, 'title': '发布统计'},
|
||||
{'icon': Icons.edit, 'title': '编辑模板'},
|
||||
{'icon': Icons.history, 'title': '发布历史'},
|
||||
],
|
||||
'其他应用': [
|
||||
{'icon': Icons.apps, 'title': '应用中心'},
|
||||
{'icon': Icons.extension, 'title': '插件管理'},
|
||||
{'icon': Icons.download, 'title': '下载中心'},
|
||||
{'icon': Icons.update, 'title': '应用更新'},
|
||||
{'icon': Icons.settings, 'title': '应用设置'},
|
||||
{'icon': Icons.help, 'title': '帮助中心'},
|
||||
{'icon': Icons.feedback, 'title': '意见反馈'},
|
||||
{'icon': Icons.info, 'title': '关于我们'},
|
||||
{'icon': AppImages.iconPersonnelManagement, 'title': '人员管理'},
|
||||
{'icon': AppImages.iconTeamQrcode, 'title': '团队二维码'},
|
||||
{'icon': AppImages.iconDeviceManagement, 'title': '设备管理'},
|
||||
{'icon': AppImages.iconVideoCenter, 'title': '视频中心'},
|
||||
{'icon': AppImages.iconIntelligentInspection, 'title': '智能巡检'},
|
||||
{'icon': AppImages.iconVideoSearch, 'title': '录像智搜'},
|
||||
{'icon': AppImages.iconIntelligentAnalysis, 'title': '智能分析'},
|
||||
{'icon': AppImages.iconPersonCapture, 'title': '人员抓拍'},
|
||||
],
|
||||
};
|
||||
|
||||
@ -192,7 +269,7 @@ class _HomeFunctionListAreaWidgetState extends State<HomeFunctionListAreaWidget>
|
||||
shrinkWrap: true,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
childAspectRatio: 1.6,
|
||||
childAspectRatio: 1.4,
|
||||
),
|
||||
itemCount: currentFunctions.length,
|
||||
itemBuilder: (context, index) {
|
||||
@ -208,7 +285,7 @@ class _HomeFunctionListAreaWidgetState extends State<HomeFunctionListAreaWidget>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFunctionItem({required IconData icon, required String title}) {
|
||||
Widget _buildFunctionItem({required dynamic icon, required String title}) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
print('点击了: $title');
|
||||
@ -218,18 +295,21 @@ class _HomeFunctionListAreaWidgetState extends State<HomeFunctionListAreaWidget>
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min, // 重要:让Column最小化
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 20.sp, // 稍微减小图标
|
||||
color: Colors.blue.shade600,
|
||||
),
|
||||
// 优化后的图片渲染
|
||||
icon is String
|
||||
? _buildOptimizedImage(icon)
|
||||
: Icon(
|
||||
icon,
|
||||
size: 26.sp,
|
||||
color: Colors.blue.shade600,
|
||||
),
|
||||
SizedBox(height: 4.h), // 减少间距
|
||||
Flexible(
|
||||
// 使用Flexible而不是Expanded
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp, // 稍微减小字体
|
||||
fontSize: 10.sp, // 稍微减小字体
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
@ -242,4 +322,27 @@ class _HomeFunctionListAreaWidgetState extends State<HomeFunctionListAreaWidget>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 优化的图片组件
|
||||
Widget _buildOptimizedImage(String imagePath) {
|
||||
return RepaintBoundary(
|
||||
child: Image(
|
||||
image: _imageCache[imagePath] ?? AssetImage(imagePath),
|
||||
width: 26.w,
|
||||
height: 26.w,
|
||||
fit: BoxFit.contain,
|
||||
gaplessPlayback: true,
|
||||
// 防止闪烁
|
||||
filterQuality: FilterQuality.medium,
|
||||
// 优化过滤质量
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Icon(
|
||||
Icons.image_not_supported,
|
||||
size: 26.sp,
|
||||
color: Colors.grey,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,28 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/base/base_controller.dart';
|
||||
import 'package:starwork_flutter/common/constant/app_images.dart';
|
||||
import 'package:starwork_flutter/views/home/home_view.dart';
|
||||
import 'package:starwork_flutter/views/messages/messages_view.dart';
|
||||
import 'package:starwork_flutter/views/mine/mine_view.dart';
|
||||
|
||||
class MainController extends BaseController {
|
||||
// 定义底部导航的标题和图标
|
||||
final List<BottomNavigationBarItem> bottomNavItems = [
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.home_rounded),
|
||||
label: '首页',
|
||||
),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.messenger_outline_rounded),
|
||||
label: '消息',
|
||||
),
|
||||
const BottomNavigationBarItem(
|
||||
icon: Icon(Icons.person),
|
||||
label: '我的',
|
||||
),
|
||||
];
|
||||
|
||||
GlobalKey<ScaffoldState> scaffoldKey =
|
||||
GlobalKey<ScaffoldState>();
|
||||
// 当前选中的索引
|
||||
var currentIndex = 0.obs;
|
||||
|
||||
@ -37,4 +26,56 @@ class MainController extends BaseController {
|
||||
void changeIndex(int index) {
|
||||
currentIndex.value = index;
|
||||
}
|
||||
|
||||
// 定义底部导航的标题和图标
|
||||
List<BottomNavigationBarItem> get bottomNavItems => [
|
||||
BottomNavigationBarItem(
|
||||
icon: _buildTabIcon(AppImages.iconHome, AppImages.iconHomeSelected, 0),
|
||||
label: '首页',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: _buildTabIcon(AppImages.iconNotification, AppImages.iconNotificationSelected, 1),
|
||||
label: '消息',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: _buildTabIcon(AppImages.iconMine, AppImages.iconMineSelected, 2),
|
||||
label: '我的',
|
||||
),
|
||||
];
|
||||
|
||||
// 构建自定义图标组件
|
||||
Widget _buildTabIcon(String unselectedIcon, String selectedIcon, int index) {
|
||||
return Obx(() => Image.asset(
|
||||
currentIndex.value == index ? selectedIcon : unselectedIcon,
|
||||
width: 24.w,
|
||||
height: 24.w,
|
||||
fit: BoxFit.contain,
|
||||
));
|
||||
}
|
||||
|
||||
// 打开抽屉的方法
|
||||
void openDrawer() {
|
||||
try {
|
||||
final scaffoldState = scaffoldKey.currentState;
|
||||
if (scaffoldState != null && scaffoldState.hasDrawer) {
|
||||
scaffoldState.openDrawer();
|
||||
} else {
|
||||
print('Cannot open drawer: Scaffold state is null or has no drawer');
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error opening drawer: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭抽屉的方法
|
||||
void closeDrawer() {
|
||||
try {
|
||||
final scaffoldState = scaffoldKey.currentState;
|
||||
if (scaffoldState != null && scaffoldState.isDrawerOpen) {
|
||||
scaffoldState.closeDrawer();
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error closing drawer: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/views/main/widget/main_left_drawer_widget.dart';
|
||||
|
||||
import 'main_controller.dart';
|
||||
|
||||
@ -10,6 +11,7 @@ class MainView extends GetView<MainController> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: controller.scaffoldKey, // 添加这一行!
|
||||
// 使用 Obx 响应 currentIndex 的变化
|
||||
body: Obx(
|
||||
() => IndexedStack(
|
||||
@ -28,6 +30,9 @@ class MainView extends GetView<MainController> {
|
||||
unselectedFontSize: 12.sp,
|
||||
),
|
||||
),
|
||||
drawer: MainLeftDrawerWidget(
|
||||
teamList: ['家庭群组', '测试团队1', '测试团队2'],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
280
lib/views/main/widget/main_left_drawer_widget.dart
Normal file
@ -0,0 +1,280 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
class MainLeftDrawerWidget extends StatefulWidget {
|
||||
MainLeftDrawerWidget({
|
||||
super.key,
|
||||
required this.teamList,
|
||||
this.selectedTeam,
|
||||
this.onTeamSelected,
|
||||
});
|
||||
|
||||
final List<String> teamList;
|
||||
final String? selectedTeam;
|
||||
final Function(String)? onTeamSelected;
|
||||
|
||||
@override
|
||||
State<MainLeftDrawerWidget> createState() => _MainLeftDrawerWidgetState();
|
||||
}
|
||||
|
||||
class _MainLeftDrawerWidgetState extends State<MainLeftDrawerWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Drawer(
|
||||
width: 0.85.sw,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.zero, // 去掉圆角
|
||||
),
|
||||
child: Container(
|
||||
color: const Color(0xFFF6F7FB),
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
// 头部标题栏
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'我的团队',
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
width: 28.w,
|
||||
height: 28.w,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.refresh,
|
||||
size: 18.sp,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// 团队列表
|
||||
Expanded(
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.w),
|
||||
child: ListView.builder(
|
||||
itemCount: widget.teamList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final team = widget.teamList[index];
|
||||
final isSelected = team == widget.selectedTeam;
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.only(bottom: 8.h),
|
||||
padding: EdgeInsets.all(16.w),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? const Color(0xFFE3F2FD) // 选中状态:蓝色背景
|
||||
: Colors.white, // 未选中状态:白色背景
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
border: isSelected
|
||||
? Border.all(color: const Color(0xFF2196F3), width: 1)
|
||||
: Border.all(color: Colors.grey[200]!, width: 1),
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// 处理团队选择
|
||||
if (widget.onTeamSelected != null) {
|
||||
widget.onTeamSelected!(team);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
// 用户图标
|
||||
Container(
|
||||
width: 40.w,
|
||||
height: 40.w,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? const Color(0xFF2196F3)
|
||||
: Colors.grey[400],
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
color: Colors.white,
|
||||
size: 24.sp,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
|
||||
// 团队信息
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
team, // 使用teamList中的元素作为团队昵称
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: isSelected
|
||||
? const Color(0xFF2196F3)
|
||||
: Colors.black,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
isSelected
|
||||
? Icons.check_circle
|
||||
: Icons.check_circle_outline,
|
||||
size: 14.sp,
|
||||
color: isSelected
|
||||
? const Color(0xFF2196F3)
|
||||
: Colors.grey[400],
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
Text(
|
||||
isSelected ? '已选中' : '未选中',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: isSelected
|
||||
? const Color(0xFF2196F3)
|
||||
: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// 设置图标
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// 处理设置点击事件
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8.w),
|
||||
child: Icon(
|
||||
Icons.settings,
|
||||
size: 20.sp,
|
||||
color: isSelected
|
||||
? const Color(0xFF2196F3)
|
||||
: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 底部按钮区域
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
|
||||
child: Column(
|
||||
children: [
|
||||
// 创建团队和加入团队按钮
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// 处理创建团队
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 12.h),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: Colors.grey[300]!,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'创建团队',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// 处理加入团队
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 12.h),
|
||||
child: Text(
|
||||
'加入团队',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// 分割线
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 8.h),
|
||||
height: 0.5,
|
||||
color: Colors.grey[300],
|
||||
),
|
||||
|
||||
// 快捷添加我的设备按钮
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// 处理快捷添加设备
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.symmetric(vertical: 12.h),
|
||||
child: Text(
|
||||
'快捷添加我的设备',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,81 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/base/base_controller.dart';
|
||||
import 'package:starwork_flutter/views/home/home_controller.dart';
|
||||
import 'package:starwork_flutter/views/main/main_controller.dart';
|
||||
import 'messages_model.dart';
|
||||
|
||||
class MessagesController extends BaseController {
|
||||
|
||||
final mainController = Get.find<MainController>();
|
||||
final homeController = Get.find<HomeController>();
|
||||
|
||||
// 响应式消息列表
|
||||
final RxList<MessageItem> messageList = <MessageItem>[].obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_initializeMessages();
|
||||
}
|
||||
|
||||
// 初始化消息数据
|
||||
void _initializeMessages() {
|
||||
messageList.value = [
|
||||
MessageItem(
|
||||
type: MessageType.deviceStatus,
|
||||
title: '设备状态',
|
||||
subtitle: '[设备在线] DS-K(L40959329)',
|
||||
time: '25/08/28',
|
||||
hasRedDot: true,
|
||||
),
|
||||
MessageItem(
|
||||
type: MessageType.accessControl,
|
||||
title: '通行权限',
|
||||
subtitle: '[通行权限] 下发已结束,成功0条,失败1条,成功0条,失败1条,成功0条,失败1条成功0条,失败1条成功0条,失败1条',
|
||||
time: '25/08/26',
|
||||
hasRedDot: false,
|
||||
),
|
||||
MessageItem(
|
||||
type: MessageType.systemNotice,
|
||||
title: '系统通知',
|
||||
subtitle: '没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息',
|
||||
time: '',
|
||||
hasRedDot: false,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
// 刷新消息数据
|
||||
Future<void> refreshMessages() async {
|
||||
// 模拟网络请求延迟
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
|
||||
// 这里可以添加实际的API调用
|
||||
// 模拟新数据
|
||||
_initializeMessages();
|
||||
|
||||
print('消息数据刷新完成');
|
||||
}
|
||||
|
||||
// 添加新消息
|
||||
void addMessage(MessageItem message) {
|
||||
messageList.insert(0, message);
|
||||
}
|
||||
|
||||
// 移除消息
|
||||
void removeMessage(int index) {
|
||||
if (index >= 0 && index < messageList.length) {
|
||||
messageList.removeAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
// 清除所有未读标记
|
||||
void clearAllUnread() {
|
||||
for (int i = 0; i < messageList.length; i++) {
|
||||
if (messageList[i].hasRedDot) {
|
||||
messageList[i] = messageList[i].copyWith(hasRedDot: false);
|
||||
}
|
||||
}
|
||||
messageList.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
78
lib/views/messages/messages_model.dart
Normal file
@ -0,0 +1,78 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// 消息类型枚举
|
||||
enum MessageType {
|
||||
deviceStatus, // 设备状态
|
||||
accessControl, // 通行权限
|
||||
systemNotice, // 系统通知
|
||||
}
|
||||
|
||||
// 消息数据模型
|
||||
class MessageItem {
|
||||
final MessageType type;
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String time;
|
||||
final bool hasRedDot;
|
||||
|
||||
MessageItem({
|
||||
required this.type,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.time,
|
||||
required this.hasRedDot,
|
||||
});
|
||||
|
||||
// 根据消息类型获取图标
|
||||
IconData get icon {
|
||||
switch (type) {
|
||||
case MessageType.deviceStatus:
|
||||
return Icons.devices;
|
||||
case MessageType.accessControl:
|
||||
return Icons.security;
|
||||
case MessageType.systemNotice:
|
||||
return Icons.notifications;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据消息类型获取图标颜色
|
||||
Color get iconColor {
|
||||
switch (type) {
|
||||
case MessageType.deviceStatus:
|
||||
return const Color(0xFF4A90E2);
|
||||
case MessageType.accessControl:
|
||||
return const Color(0xFF4A90E2);
|
||||
case MessageType.systemNotice:
|
||||
return const Color(0xFFFF8C42);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据消息类型获取图标背景色
|
||||
Color get iconBgColor {
|
||||
switch (type) {
|
||||
case MessageType.deviceStatus:
|
||||
return const Color(0xFFE8F4FD);
|
||||
case MessageType.accessControl:
|
||||
return const Color(0xFFE8F4FD);
|
||||
case MessageType.systemNotice:
|
||||
return const Color(0xFFFFF2E8);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建副本,用于更新某些属性
|
||||
MessageItem copyWith({
|
||||
MessageType? type,
|
||||
String? title,
|
||||
String? subtitle,
|
||||
String? time,
|
||||
bool? hasRedDot,
|
||||
}) {
|
||||
return MessageItem(
|
||||
type: type ?? this.type,
|
||||
title: title ?? this.title,
|
||||
subtitle: subtitle ?? this.subtitle,
|
||||
time: time ?? this.time,
|
||||
hasRedDot: hasRedDot ?? this.hasRedDot,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,11 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:starwork_flutter/base/app_permission.dart';
|
||||
|
||||
import 'messages_controller.dart';
|
||||
import 'messages_model.dart';
|
||||
|
||||
class MessagesView extends GetView<MessagesController> {
|
||||
const MessagesView({super.key});
|
||||
@ -9,16 +13,400 @@ class MessagesView extends GetView<MessagesController> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('MessagesView'),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: const Center(
|
||||
child: Text(
|
||||
'MessagesView is working',
|
||||
style: TextStyle(fontSize: 20),
|
||||
backgroundColor: const Color(0xFFF6F7FB),
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
width: 1.sw,
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 4.h),
|
||||
child: Column(
|
||||
children: [
|
||||
// 固定的上半部分
|
||||
_buildPageHead(context),
|
||||
_buildSystemNotificationPermissionRow(),
|
||||
Obx(
|
||||
() => Visibility(
|
||||
visible: controller
|
||||
.homeController.isOpenNotificationPermission.value,
|
||||
child: SizedBox(
|
||||
height: 10.h,
|
||||
),
|
||||
),
|
||||
),
|
||||
// 消息列表
|
||||
Expanded(
|
||||
child: _buildRefreshableMessageList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildPageHead(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// 左侧标题区域
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
controller.mainController.scaffoldKey.currentState?.openDrawer();
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
'19104656的互12312联',
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black87,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_right_rounded,
|
||||
size: 22.sp,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// 右侧功能按钮区域
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 清除未读按钮
|
||||
Flexible(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// 处理清除未读点击事件
|
||||
controller.clearAllUnread();
|
||||
print('清除未读被点击');
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 6.w, // 减小内边距
|
||||
vertical: 3.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF0F0F0),
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
'清除未读',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp, // 减小字体
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// 分隔线
|
||||
Container(
|
||||
height: 16.h, // 减小高度
|
||||
width: 1.w,
|
||||
color: Colors.grey.withOpacity(0.3),
|
||||
),
|
||||
// 通知设置按钮
|
||||
Flexible(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// 处理通知设置点击事件
|
||||
print('通知设置被点击');
|
||||
},
|
||||
child: Text(
|
||||
'通知设置',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp, // 减小字体
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_buildSystemNotificationPermissionRow() {
|
||||
return Obx(
|
||||
() => Visibility(
|
||||
visible: !controller.homeController.isOpenNotificationPermission.value,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFEF2E5),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(8.r),
|
||||
),
|
||||
),
|
||||
margin: EdgeInsets.symmetric(vertical: 8.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'系统通知未开启,报警消息无法通知'.tr,
|
||||
style: TextStyle(
|
||||
color: const Color(0xFFEE9846),
|
||||
fontSize: 12.sp,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
controller.homeController.isOpenNotificationPermission.value =
|
||||
await AppPermission.requestNotificationPermission();
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 4.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(4.r),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'去开启'.tr,
|
||||
style: TextStyle(
|
||||
color: const Color(0xFFEE9846),
|
||||
fontSize: 12.sp,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 14.w,
|
||||
),
|
||||
Icon(
|
||||
Icons.cancel,
|
||||
color: const Color(0xFFEE9846),
|
||||
size: 18.sp,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildRefreshableMessageList() {
|
||||
return RefreshIndicator(
|
||||
onRefresh: _onRefresh,
|
||||
// 基础样式配置
|
||||
color: const Color(0xFF4A90E2), // 刷新指示器颜色
|
||||
backgroundColor: Colors.white, // 背景颜色
|
||||
|
||||
// 控制下拉触发距离的关键属性
|
||||
displacement: 60.0, // 刷新指示器距离顶部的距离(默认40.0)
|
||||
edgeOffset: 0.0, // 边缘偏移量(默认0.0)
|
||||
|
||||
// 控制下拉幅度的关键属性
|
||||
triggerMode: RefreshIndicatorTriggerMode.onEdge, // 触发模式
|
||||
// RefreshIndicatorTriggerMode.onEdge: 在边缘触发(默认)
|
||||
// RefreshIndicatorTriggerMode.anywhere: 在任何位置都可以触发
|
||||
|
||||
// 描边宽度
|
||||
strokeWidth: 2.5, // 刷新指示器的描边宽度(默认2.0)
|
||||
|
||||
// 语义标签(用于无障碍功能)
|
||||
semanticsLabel: '下拉刷新消息列表',
|
||||
semanticsValue: '刷新中...',
|
||||
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(
|
||||
// 控制滚动物理特性
|
||||
parent: BouncingScrollPhysics(), // 添加弹性滚动效果
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildMessageList(),
|
||||
SizedBox(height: 16.h),
|
||||
_buildMessageTip(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onRefresh() async {
|
||||
// 调用controller的刷新方法
|
||||
await controller.refreshMessages();
|
||||
}
|
||||
|
||||
_buildMessageList() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: Obx(
|
||||
() => Column(
|
||||
children: [
|
||||
// 动态生成消息列表项
|
||||
...controller.messageList.asMap().entries.map((entry) {
|
||||
int index = entry.key;
|
||||
MessageItem message = entry.value;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
_buildMessageItem(
|
||||
message: message,
|
||||
onTap: () => _onMessageTap(index, message),
|
||||
),
|
||||
// 如果不是最后一项,显示分隔线
|
||||
if (index < controller.messageList.length - 1)
|
||||
_buildDivider(),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildMessageTip() {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 10.h),
|
||||
child: Text(
|
||||
'只展示最近7天的消息',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.grey[500],
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildMessageItem({
|
||||
required MessageItem message,
|
||||
VoidCallback? onTap,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap ??
|
||||
() {
|
||||
// 默认点击事件
|
||||
print('${message.title} 被点击');
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
|
||||
child: Row(
|
||||
children: [
|
||||
// 左侧图标
|
||||
Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: 40.w,
|
||||
height: 40.w,
|
||||
decoration: BoxDecoration(
|
||||
color: message.iconBgColor,
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: Icon(
|
||||
message.icon,
|
||||
color: message.iconColor,
|
||||
size: 20.sp,
|
||||
),
|
||||
),
|
||||
if (message.hasRedDot)
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
width: 8.w,
|
||||
height: 8.w,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.red,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
// 中间内容
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
message.title,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
Text(
|
||||
message.subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.grey[600],
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 右侧时间
|
||||
if (message.time.isNotEmpty)
|
||||
Text(
|
||||
message.time,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.grey[400],
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 处理消息项点击事件
|
||||
void _onMessageTap(int index, MessageItem message) {
|
||||
print('${message.title} 被点击,索引: $index');
|
||||
// 这里可以添加具体的业务逻辑,比如跳转到详情页面
|
||||
// 如果有未读标记,可以标记为已读
|
||||
if (message.hasRedDot) {
|
||||
// 可以在这里调用controller的方法标记为已读
|
||||
}
|
||||
}
|
||||
|
||||
_buildDivider() {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.w),
|
||||
height: 1.h,
|
||||
color: Colors.grey[200],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,4 +51,5 @@ flutter:
|
||||
- assets/images/
|
||||
- assets/logo/
|
||||
- assets/icon/
|
||||
- assets/icon/bar/
|
||||
|
||||
|
||||