20 changed files with 1343 additions and 201 deletions
@ -1,62 +0,0 @@
|
||||
// lib/data/providers/connectivity_provider.dart |
||||
import 'dart:async'; |
||||
import 'package:connectivity_plus/connectivity_plus.dart'; |
||||
import 'package:get/get.dart'; |
||||
import 'package:flutter/material.dart'; |
||||
|
||||
class ConnectivityProvider extends GetxService { |
||||
final Connectivity _connectivity = Connectivity(); |
||||
final RxBool isOnline = false.obs; |
||||
late StreamSubscription<List<ConnectivityResult>> _connectivitySubscription; |
||||
|
||||
@override |
||||
void onInit() { |
||||
super.onInit(); |
||||
_initConnectivityListener(); |
||||
} |
||||
|
||||
@override |
||||
void onClose() { |
||||
_connectivitySubscription.cancel(); |
||||
super.onClose(); |
||||
} |
||||
|
||||
Future<void> _initConnectivityListener() async { |
||||
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(( |
||||
results, |
||||
) { |
||||
final isConnected = results.any( |
||||
(result) => |
||||
result == ConnectivityResult.mobile || |
||||
result == ConnectivityResult.wifi || |
||||
result == ConnectivityResult.ethernet, |
||||
); |
||||
isOnline.value = isConnected; |
||||
if (isConnected) { |
||||
Get.snackbar( |
||||
'网络状态', |
||||
'已连接到网络', |
||||
colorText: Colors.white, |
||||
backgroundColor: Colors.green, |
||||
snackPosition: SnackPosition.TOP, |
||||
); |
||||
} else { |
||||
Get.snackbar( |
||||
'网络状态', |
||||
'已断开网络连接', |
||||
colorText: Colors.white, |
||||
backgroundColor: Colors.red, |
||||
snackPosition: SnackPosition.TOP, |
||||
); |
||||
} |
||||
}); |
||||
|
||||
final initialResults = await _connectivity.checkConnectivity(); |
||||
isOnline.value = initialResults.any( |
||||
(result) => |
||||
result == ConnectivityResult.mobile || |
||||
result == ConnectivityResult.wifi || |
||||
result == ConnectivityResult.ethernet, |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,46 @@
|
||||
// lib/data/providers/connectivity_provider.dart |
||||
import 'dart:async'; |
||||
import 'package:connectivity_plus/connectivity_plus.dart'; |
||||
import 'package:get/get.dart'; |
||||
|
||||
class NetworkStatusService extends GetxService { |
||||
final Connectivity _connectivity = Connectivity(); |
||||
final RxBool isOnline = false.obs; |
||||
StreamSubscription<List<ConnectivityResult>>? _connectivitySubscription; |
||||
|
||||
@override |
||||
void onInit() { |
||||
super.onInit(); |
||||
_initConnectivityListener(); |
||||
_checkInitialConnectivity(); |
||||
} |
||||
|
||||
@override |
||||
void onClose() { |
||||
_connectivitySubscription?.cancel(); |
||||
super.onClose(); |
||||
} |
||||
|
||||
void _initConnectivityListener() { |
||||
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(( |
||||
results, |
||||
) { |
||||
isOnline.value = _isConnected(results); |
||||
// UI 层监听 isOnline.value 变化后自行处理提示 |
||||
}); |
||||
} |
||||
|
||||
Future<void> _checkInitialConnectivity() async { |
||||
final initialResults = await _connectivity.checkConnectivity(); |
||||
isOnline.value = _isConnected(initialResults); |
||||
} |
||||
|
||||
bool _isConnected(List<ConnectivityResult> results) { |
||||
return results.any( |
||||
(result) => |
||||
result == ConnectivityResult.mobile || |
||||
result == ConnectivityResult.wifi || |
||||
result == ConnectivityResult.ethernet, |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,113 @@
|
||||
// lib/app/modules/enterprise_list/enterprise_list_controller.dart |
||||
|
||||
import 'package:get/get.dart'; |
||||
import 'package:problem_check_system/modules/enterprise_list/model/enterprise_model.dart'; |
||||
|
||||
class EnterpriseListController extends GetxController { |
||||
// 使用 .obs 使列表变为响应式,当数据变化时,UI会自动更新 |
||||
final enterpriseList = <Enterprise>[].obs; |
||||
|
||||
@override |
||||
void onInit() { |
||||
super.onInit(); |
||||
fetchEnterprises(); // 页面初始化时加载数据 |
||||
} |
||||
|
||||
// 模拟从API获取数据的过程 |
||||
void fetchEnterprises() { |
||||
// 模拟网络延迟 |
||||
Future.delayed(const Duration(milliseconds: 500), () { |
||||
// 创建一些示例数据 |
||||
var mockData = [ |
||||
Enterprise( |
||||
companyName: '山东汇丰石化集团有限公司', |
||||
companyType: '危险化学品生产', |
||||
totalIssues: 29, |
||||
creationTime: '2025-07-31 15:30:29', |
||||
uploaded: 15, |
||||
notUploaded: 14, |
||||
), |
||||
Enterprise( |
||||
companyName: '山东荣源石化集团有限公司', |
||||
companyType: '危险化学品生产', |
||||
totalIssues: 29, |
||||
creationTime: '2025-07-31 15:30:29', |
||||
uploaded: 15, |
||||
notUploaded: 14, |
||||
), |
||||
Enterprise( |
||||
companyName: '山东江潜石化集团有限公司', |
||||
companyType: '危险化学品生产', |
||||
totalIssues: 29, |
||||
creationTime: '2025-07-31 15:30:29', |
||||
uploaded: 15, |
||||
notUploaded: 14, |
||||
), |
||||
Enterprise( |
||||
companyName: '山东汇绿武化集团有限公司', |
||||
companyType: '危险化学品生产', |
||||
totalIssues: 29, |
||||
creationTime: '2025-07-31 15:30:29', |
||||
uploaded: 15, |
||||
notUploaded: 14, |
||||
), |
||||
Enterprise( |
||||
companyName: '山东汇绿武化集团有限公司', |
||||
companyType: '危险化学品生产', |
||||
totalIssues: 29, |
||||
creationTime: '2025-07-31 15:30:29', |
||||
uploaded: 15, |
||||
notUploaded: 14, |
||||
), |
||||
Enterprise( |
||||
companyName: '山东汇绿武化集团有限公司', |
||||
companyType: '危险化学品生产', |
||||
totalIssues: 29, |
||||
creationTime: '2025-07-31 15:30:29', |
||||
uploaded: 15, |
||||
notUploaded: 14, |
||||
), |
||||
Enterprise( |
||||
companyName: '山东汇绿武化集团有限公司', |
||||
companyType: '危险化学品生产', |
||||
totalIssues: 29, |
||||
creationTime: '2025-07-31 15:30:29', |
||||
uploaded: 15, |
||||
notUploaded: 14, |
||||
), |
||||
Enterprise( |
||||
companyName: '山东汇绿武化集团有限公司', |
||||
companyType: '危险化学品生产', |
||||
totalIssues: 29, |
||||
creationTime: '2025-07-31 15:30:29', |
||||
uploaded: 15, |
||||
notUploaded: 14, |
||||
), |
||||
Enterprise( |
||||
companyName: '山东汇绿武化集团有限公司', |
||||
companyType: '危险化学品生产', |
||||
totalIssues: 29, |
||||
creationTime: '2025-07-31 15:30:29', |
||||
uploaded: 15, |
||||
notUploaded: 14, |
||||
), |
||||
]; |
||||
enterpriseList.assignAll(mockData); // 更新列表 |
||||
}); |
||||
} |
||||
|
||||
// 可以在这里添加其他业务逻辑,例如: |
||||
void onSearch() { |
||||
// 处理搜索逻辑 |
||||
} |
||||
|
||||
void onAddNew() { |
||||
// 处理添加新条目的逻辑 |
||||
Get.snackbar('提示', '添加功能待实现'); |
||||
} |
||||
|
||||
void onUpload() { |
||||
// 处理上传逻辑 |
||||
Get.snackbar('提示', '上传功能待实现'); |
||||
} |
||||
} |
@ -0,0 +1,280 @@
|
||||
import 'package:flutter/material.dart'; |
||||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
||||
import 'package:get/get.dart'; |
||||
import 'package:problem_check_system/modules/enterprise_list/model/enterprise_model.dart'; |
||||
import 'package:problem_check_system/modules/enterprise_list/widgets/enterprise_card.dart'; |
||||
import 'enterprise_list_controller.dart'; |
||||
|
||||
class EnterpriseListPage extends GetView<EnterpriseListController> { |
||||
const EnterpriseListPage({super.key}); |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
return Scaffold( |
||||
appBar: AppBar( |
||||
title: Text( |
||||
'企业列表', |
||||
style: TextStyle( |
||||
fontWeight: FontWeight.bold, |
||||
fontFamily: 'MyFont', |
||||
fontSize: 18.sp, |
||||
color: Colors.white, |
||||
), |
||||
), |
||||
backgroundColor: const Color(0xFF3B82F6), |
||||
elevation: 0, |
||||
centerTitle: true, |
||||
actions: [ |
||||
IconButton( |
||||
icon: Icon(Icons.add, color: Colors.white), // 使用 .sp |
||||
onPressed: () { |
||||
// 处理筛选按钮点击事件 |
||||
}, |
||||
), |
||||
IconButton( |
||||
icon: Icon(Icons.upload, color: Colors.pink[300]), // 使用 .sp |
||||
onPressed: () { |
||||
// 处理筛选按钮点击事件 |
||||
}, |
||||
), |
||||
], |
||||
), |
||||
body: Stack( |
||||
children: [ |
||||
Column( |
||||
children: [ |
||||
_buildFilterBar(), |
||||
// 使用 Obx 包裹需要响应式更新的 Widget |
||||
Expanded( |
||||
child: Obx(() { |
||||
if (controller.enterpriseList.isEmpty) { |
||||
return const Center(child: CircularProgressIndicator()); |
||||
} |
||||
return ListView.builder( |
||||
padding: EdgeInsets.symmetric( |
||||
horizontal: 16.w, // 使用 .w |
||||
vertical: 8.h, // 使用 .h |
||||
), |
||||
itemCount: controller.enterpriseList.length, |
||||
itemBuilder: (context, index) { |
||||
final enterprise = controller.enterpriseList[index]; |
||||
return Padding( |
||||
padding: EdgeInsets.only(bottom: 12.h), // 使用 .h |
||||
// child: _EnterpriseCard(enterprise: enterprise), |
||||
child: EnterpriseCard(), |
||||
); |
||||
}, |
||||
); |
||||
}), |
||||
), |
||||
], |
||||
), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
Widget _buildFilterBar() { |
||||
return Container( |
||||
padding: EdgeInsets.fromLTRB(16.w, 12.h, 16.w, 12.h), // 使用 .w 和 .h |
||||
color: Colors.white, |
||||
child: Row( |
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
children: [ |
||||
Expanded( |
||||
child: Row( |
||||
children: [ |
||||
const Text('企业名称', style: TextStyle(color: Colors.black54)), |
||||
SizedBox(width: 4.w), // 使用 .w |
||||
Icon( |
||||
Icons.search, |
||||
size: 20.sp, |
||||
color: Colors.black54, |
||||
), // 使用 .sp |
||||
], |
||||
), |
||||
), |
||||
_buildDropdown('选择日期'), |
||||
_buildDropdown('近一周'), |
||||
_buildDropdown('类型'), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
Widget _buildDropdown(String text) { |
||||
return Padding( |
||||
padding: EdgeInsets.symmetric(horizontal: 8.w), // 使用 .w |
||||
child: Row( |
||||
children: [ |
||||
Text(text, style: const TextStyle(color: Colors.black54)), |
||||
const Icon(Icons.arrow_drop_down, color: Colors.black54), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
} |
||||
|
||||
class _EnterpriseCard extends StatelessWidget { |
||||
final Enterprise enterprise; |
||||
const _EnterpriseCard({required this.enterprise}); |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
return Container( |
||||
padding: EdgeInsets.all(16.r), // 使用 .r 保证四边 padding 比例一致 |
||||
decoration: BoxDecoration( |
||||
color: Colors.white, |
||||
borderRadius: BorderRadius.circular(8.r), // 使用 .r |
||||
), |
||||
child: Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Row( |
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Text( |
||||
'企业名称', |
||||
style: TextStyle(fontSize: 9.sp, color: Colors.grey), |
||||
), |
||||
SizedBox(height: 4.h), // 使用 .h |
||||
Text( |
||||
enterprise.companyName, |
||||
style: TextStyle( |
||||
fontSize: 12.5.sp, |
||||
fontWeight: FontWeight.w500, |
||||
), |
||||
maxLines: 1, |
||||
overflow: TextOverflow.ellipsis, |
||||
), |
||||
], |
||||
), |
||||
|
||||
Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Text( |
||||
'企业类型', |
||||
style: TextStyle(fontSize: 9.sp, color: Colors.grey), |
||||
), |
||||
SizedBox(height: 4.h), // 使用 .h |
||||
Text( |
||||
enterprise.companyType, |
||||
maxLines: 1, |
||||
overflow: TextOverflow.ellipsis, |
||||
style: TextStyle( |
||||
fontSize: 12.5.sp, |
||||
fontWeight: FontWeight.w500, |
||||
), |
||||
), |
||||
], |
||||
), |
||||
|
||||
Container( |
||||
padding: EdgeInsets.symmetric( |
||||
horizontal: 5.w, |
||||
vertical: 2.h, |
||||
), // 使用 .w 和 .h |
||||
decoration: BoxDecoration( |
||||
// color: Colors.orange[100], |
||||
borderRadius: BorderRadius.circular(10.r), // 使用 .r |
||||
border: Border.all(color: Colors.red), |
||||
), |
||||
child: Text( |
||||
'信息未上传', |
||||
maxLines: 1, |
||||
style: TextStyle(fontSize: 9.sp, color: Colors.red), // 使用 .sp |
||||
), |
||||
), |
||||
], |
||||
), |
||||
SizedBox(height: 16.h), // 使用 .h |
||||
Row( |
||||
children: [ |
||||
Icon( |
||||
Icons.description, |
||||
size: 16.sp, |
||||
color: Colors.grey, |
||||
), // 使用 .sp |
||||
SizedBox(width: 4.w), // 使用 .w |
||||
Text( |
||||
'问题总数: ${enterprise.totalIssues}', |
||||
style: TextStyle(fontSize: 12.sp, color: Colors.grey), // 使用 .sp |
||||
), |
||||
SizedBox(width: 16.w), // 使用 .w |
||||
Icon( |
||||
Icons.access_time, |
||||
size: 16.sp, |
||||
color: Colors.grey, |
||||
), // 使用 .sp |
||||
SizedBox(width: 4.w), // 使用 .w |
||||
Text( |
||||
'创建时间: ${enterprise.creationTime}', |
||||
style: TextStyle(fontSize: 12.sp, color: Colors.grey), // 使用 .sp |
||||
), |
||||
], |
||||
), |
||||
// SizedBox(height: 16.h), // 使用 .h |
||||
Row( |
||||
children: [ |
||||
_buildTag('已上传 ${enterprise.uploaded}', Colors.blue), |
||||
SizedBox(width: 8.w), // 使用 .w |
||||
_buildTag('未上传 ${enterprise.notUploaded}', Colors.red), |
||||
const Spacer(), |
||||
Icon(Icons.edit, size: 16.sp, color: Colors.grey), // 使用 .sp |
||||
SizedBox(width: 4.w), // 使用 .w |
||||
Text( |
||||
'修改信息', |
||||
style: TextStyle( |
||||
fontSize: 9.5.sp, |
||||
color: Colors.grey, |
||||
), // 使用 .sp |
||||
), |
||||
SizedBox(width: 16.w), // 使用 .w |
||||
ElevatedButton( |
||||
onPressed: () {}, |
||||
style: ElevatedButton.styleFrom( |
||||
backgroundColor: const Color(0xFF3B82F6), |
||||
|
||||
shape: RoundedRectangleBorder( |
||||
borderRadius: BorderRadius.circular(4.r), // 使用 .r |
||||
), |
||||
// padding: EdgeInsets.symmetric( |
||||
// horizontal: 16.w, // 使用 .w |
||||
// vertical: 8.h, // 使用 .h |
||||
// ), |
||||
// tapTargetSize: MaterialTapTargetSize.shrinkWrap, |
||||
), |
||||
child: const Text( |
||||
'查看问题', |
||||
style: TextStyle(color: Colors.white), |
||||
), |
||||
), |
||||
], |
||||
), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
Widget _buildTag(String text, Color color) { |
||||
return Container( |
||||
padding: EdgeInsets.symmetric( |
||||
horizontal: 6.w, |
||||
vertical: 2.h, |
||||
), // 使用 .w 和 .h |
||||
decoration: BoxDecoration( |
||||
color: color.withOpacity(0.1), |
||||
borderRadius: BorderRadius.circular(4.r), // 使用 .r |
||||
), |
||||
child: Text( |
||||
text, |
||||
style: TextStyle(color: color, fontSize: 8.5.sp), |
||||
), // 使用 .sp |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,19 @@
|
||||
// lib/app/modules/enterprise_list/models/enterprise_model.dart |
||||
|
||||
class Enterprise { |
||||
final String companyName; |
||||
final String companyType; |
||||
final int totalIssues; |
||||
final String creationTime; |
||||
final int uploaded; |
||||
final int notUploaded; |
||||
|
||||
Enterprise({ |
||||
required this.companyName, |
||||
required this.companyType, |
||||
required this.totalIssues, |
||||
required this.creationTime, |
||||
required this.uploaded, |
||||
required this.notUploaded, |
||||
}); |
||||
} |
@ -0,0 +1,272 @@
|
||||
import 'package:flutter/material.dart'; |
||||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
||||
|
||||
// 主卡片组件 |
||||
class EnterpriseCard extends StatelessWidget { |
||||
const EnterpriseCard({super.key}); |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
return Material( |
||||
color: Colors.transparent, |
||||
child: Container( |
||||
// 【核心修改 1】移除 Container 的 padding |
||||
// padding: EdgeInsets.only(...), // <--- 移除这一段 |
||||
decoration: BoxDecoration( |
||||
color: Colors.white, |
||||
borderRadius: BorderRadius.circular(12.r), |
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w), |
||||
), |
||||
// 【核心修改 2】使用 Clip.antiAlias 来剪裁溢出的按钮部分 |
||||
clipBehavior: Clip.antiAlias, |
||||
child: Stack( |
||||
// Stack 默认会填满父组件(Container) |
||||
children: [ |
||||
// --- 内容层 --- |
||||
// 【核心修改 3】使用 Padding 在 Stack 内部创建内边距 |
||||
Padding( |
||||
padding: EdgeInsets.only( |
||||
left: 16.w, |
||||
right: 16.w, |
||||
top: 16.h, |
||||
// 底部 padding 需要足够大,为按钮留出空间 |
||||
bottom: 0.h, |
||||
), |
||||
child: Column( |
||||
// 让 Column 包裹内容,避免不必要的高度 |
||||
mainAxisSize: MainAxisSize.min, |
||||
children: [ |
||||
_buildTopSection(), |
||||
SizedBox(height: 12.h), |
||||
_buildMiddleSection(), |
||||
SizedBox(height: 12.h), |
||||
_buildBottomSection(context), |
||||
], |
||||
), |
||||
), |
||||
|
||||
// --- 按钮层 --- |
||||
// 【核心修改 4】修改 Positioned 的定位 |
||||
Positioned( |
||||
bottom: 0, // 相对于卡片底部 |
||||
right: 0, // 相对于卡片右侧 |
||||
child: Row( |
||||
children: [ |
||||
// --- “修改信息” 按钮 --- |
||||
TextButton.icon( |
||||
onPressed: () { |
||||
print('修改信息按钮被点击'); |
||||
}, |
||||
icon: Icon(Icons.edit_outlined, size: 16.sp), |
||||
label: Text('修改信息', style: TextStyle(fontSize: 9.5.sp)), |
||||
style: TextButton.styleFrom( |
||||
foregroundColor: Colors.grey.shade600, |
||||
padding: EdgeInsets.symmetric( |
||||
horizontal: 16.w, |
||||
vertical: 8.h, |
||||
), |
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap, |
||||
minimumSize: const Size(0, 0), |
||||
), |
||||
), |
||||
// --- “查看问题” 按钮 --- |
||||
ElevatedButton( |
||||
onPressed: () { |
||||
print('查看问题按钮被点击'); |
||||
}, |
||||
style: ElevatedButton.styleFrom( |
||||
backgroundColor: const Color(0xFF42A5F5), |
||||
foregroundColor: Colors.white, |
||||
shape: RoundedRectangleBorder( |
||||
borderRadius: BorderRadius.only( |
||||
topLeft: Radius.circular(12.r), |
||||
// 注意:右下角因为贴边,不再需要圆角,否则会有缝隙 |
||||
// bottomRight: Radius.circular(12.r), |
||||
), |
||||
), |
||||
padding: EdgeInsets.symmetric( |
||||
horizontal: 16.w, |
||||
vertical: 8.h, |
||||
), |
||||
// elevation: 0, // 移除阴影,因为它已经被剪裁了 |
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap, |
||||
minimumSize: const Size(0, 0), |
||||
), |
||||
child: Text( |
||||
"查看问题", |
||||
style: TextStyle( |
||||
fontSize: 13.sp, |
||||
fontWeight: FontWeight.w500, |
||||
), |
||||
), |
||||
), |
||||
], |
||||
), |
||||
), |
||||
], |
||||
), |
||||
), |
||||
); |
||||
} |
||||
|
||||
/// 构建顶部区域:企业名称、类型和状态 |
||||
Widget _buildTopSection() { |
||||
return Row( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
children: [ |
||||
Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Text( |
||||
'企业名称', |
||||
style: TextStyle( |
||||
fontSize: 9.sp, |
||||
color: Colors.grey.shade500, |
||||
), // .sp 适配字体 |
||||
), |
||||
SizedBox(height: 4.h), // .h 适配垂直间距 |
||||
Text( |
||||
'山东汇丰石化集团有限公司', |
||||
style: TextStyle( |
||||
fontSize: 12.5.sp, |
||||
fontWeight: FontWeight.w500, |
||||
color: Colors.black87, |
||||
), |
||||
overflow: TextOverflow.ellipsis, |
||||
), |
||||
], |
||||
), |
||||
Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Text( |
||||
'企业类型', |
||||
style: TextStyle(fontSize: 9.sp, color: Colors.grey.shade500), |
||||
), |
||||
SizedBox(height: 4.h), |
||||
Text( |
||||
'危险化学品生产', |
||||
style: TextStyle( |
||||
fontSize: 12.5.sp, |
||||
fontWeight: FontWeight.w500, |
||||
color: Colors.black87, |
||||
), |
||||
overflow: TextOverflow.ellipsis, |
||||
), |
||||
], |
||||
), |
||||
// SizedBox(width: 8.w), |
||||
Container( |
||||
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 3.h), |
||||
decoration: BoxDecoration( |
||||
borderRadius: BorderRadius.circular(8.r), |
||||
border: Border.all(color: Colors.red.shade400, width: 1.w), |
||||
), |
||||
child: Text( |
||||
'信息未上传', |
||||
style: TextStyle(fontSize: 7.sp, color: Colors.red.shade400), |
||||
), |
||||
), |
||||
], |
||||
); |
||||
} |
||||
|
||||
/// 构建中间区域:问题总数和创建时间 |
||||
Widget _buildMiddleSection() { |
||||
return Row( |
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
children: [ |
||||
Row( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Icon(Icons.description_outlined, color: Colors.grey, size: 16.sp), |
||||
SizedBox(width: 4.w), |
||||
Text( |
||||
'问题总数: ', |
||||
style: TextStyle(fontSize: 12.sp, color: Colors.grey), |
||||
), |
||||
Text( |
||||
'29', |
||||
style: TextStyle( |
||||
fontSize: 12.5.sp, |
||||
color: Colors.black87, |
||||
fontWeight: FontWeight.w500, |
||||
), |
||||
), |
||||
], |
||||
), |
||||
Row( |
||||
crossAxisAlignment: CrossAxisAlignment.center, |
||||
children: [ |
||||
Icon(Icons.access_time, color: Colors.grey, size: 16.sp), |
||||
Text( |
||||
'创建时间: 2025-07-31 15:30:29', |
||||
style: TextStyle(fontSize: 12.sp, color: Colors.grey), |
||||
), |
||||
], |
||||
), |
||||
], |
||||
); |
||||
} |
||||
|
||||
/// 构建底部区域:标签和复选框 |
||||
Widget _buildBottomSection(BuildContext context) { |
||||
return Row( |
||||
children: [ |
||||
_buildTag( |
||||
text: '已上传 15', |
||||
textColor: Colors.blue.shade700, |
||||
backgroundColor: Colors.blue.shade50, |
||||
), |
||||
SizedBox(width: 8.w), |
||||
_buildTag( |
||||
text: '未上传 14', |
||||
textColor: Colors.red.shade600, |
||||
backgroundColor: Colors.red.shade50, |
||||
), |
||||
const Spacer(), |
||||
|
||||
Theme( |
||||
data: Theme.of(context).copyWith( |
||||
checkboxTheme: CheckboxThemeData( |
||||
// 将点击区域收缩,移除额外的 padding |
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, |
||||
// (可选) 进一步压缩视觉密度 |
||||
visualDensity: VisualDensity.compact, |
||||
), |
||||
), |
||||
child: Checkbox( |
||||
value: true, |
||||
onChanged: (bool? value) {}, |
||||
// 其他属性... |
||||
), |
||||
), |
||||
], |
||||
); |
||||
} |
||||
|
||||
/// 用于创建“已上传”和“未上传”标签的辅助方法 |
||||
Widget _buildTag({ |
||||
required String text, |
||||
required Color textColor, |
||||
required Color backgroundColor, |
||||
}) { |
||||
return Container( |
||||
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h), |
||||
decoration: BoxDecoration( |
||||
color: backgroundColor, |
||||
// borderRadius: BorderRadius.circular(4.r), |
||||
// border: Border.all(color: textColor.withAlpha(128), width: 1.w), |
||||
), |
||||
child: Text( |
||||
text, |
||||
style: TextStyle( |
||||
color: textColor, |
||||
fontSize: 10.sp, |
||||
fontWeight: FontWeight.w500, |
||||
), |
||||
), |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,468 @@
|
||||
import 'package:flutter/material.dart'; |
||||
|
||||
class HealthApp extends StatelessWidget { |
||||
const HealthApp({super.key}); |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
return HomePage1(); |
||||
} |
||||
} |
||||
|
||||
class HomePage1 extends StatefulWidget { |
||||
const HomePage1({super.key}); |
||||
|
||||
@override |
||||
State<HomePage1> createState() => _HomePageState(); |
||||
} |
||||
|
||||
class _HomePageState extends State<HomePage1> |
||||
with SingleTickerProviderStateMixin { |
||||
late TabController _tabController; |
||||
int _bottomNavIndex = 0; |
||||
|
||||
@override |
||||
void initState() { |
||||
super.initState(); |
||||
_tabController = TabController(length: 3, vsync: this); |
||||
} |
||||
|
||||
@override |
||||
void dispose() { |
||||
_tabController.dispose(); |
||||
super.dispose(); |
||||
} |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
return Scaffold( |
||||
appBar: _buildAppBar(), |
||||
body: SingleChildScrollView( |
||||
child: Column( |
||||
children: [ |
||||
// _buildSearchBar(), |
||||
// _buildServicesGrid(), |
||||
const SizedBox(height: 12), |
||||
// _buildHealthManagementCard(), |
||||
const SizedBox(height: 12), |
||||
// _buildDoctorBanner(), |
||||
const SizedBox(height: 12), |
||||
// _buildPopularServices(), |
||||
], |
||||
), |
||||
), |
||||
// bottomNavigationBar: _buildBottomNavigationBar(), |
||||
); |
||||
} |
||||
|
||||
// 构建顶部 AppBar |
||||
PreferredSizeWidget _buildAppBar() { |
||||
return AppBar( |
||||
backgroundColor: Colors.blue, |
||||
elevation: 0, |
||||
leading: SizedBox(), |
||||
title: const Center( |
||||
child: Text( |
||||
'企业列表', |
||||
style: TextStyle( |
||||
color: Colors.white, |
||||
fontWeight: FontWeight.bold, |
||||
fontFamily: 'MyFont', |
||||
), |
||||
), |
||||
), |
||||
actions: [ |
||||
// IconButton( |
||||
// onPressed: () {}, |
||||
// icon: const Icon(Icons.widgets_outlined, color: Colors.black), |
||||
// ), |
||||
IconButton( |
||||
onPressed: () {}, |
||||
icon: const Icon(Icons.add, color: Colors.white), |
||||
), |
||||
], |
||||
bottom: TabBar( |
||||
controller: _tabController, |
||||
isScrollable: true, |
||||
labelColor: Colors.black, |
||||
unselectedLabelColor: Colors.grey[700], |
||||
indicatorColor: Colors.blue, |
||||
indicatorWeight: 3, |
||||
tabs: const [ |
||||
Tab(text: '问题列表'), |
||||
Tab(text: '历史问题列表'), |
||||
Tab(text: '企业基本情况'), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
// 构建顶部 AppBar |
||||
PreferredSizeWidget _buildAppBar2() { |
||||
return AppBar( |
||||
backgroundColor: Colors.lightBlue[50], |
||||
elevation: 0, |
||||
leading: const Icon(Icons.arrow_back_ios, color: Colors.black), |
||||
title: const Center( |
||||
child: Text( |
||||
'企业列表', |
||||
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold), |
||||
), |
||||
), |
||||
actions: [ |
||||
// IconButton( |
||||
// onPressed: () {}, |
||||
// icon: const Icon(Icons.widgets_outlined, color: Colors.black), |
||||
// ), |
||||
IconButton( |
||||
onPressed: () {}, |
||||
icon: const Icon(Icons.add, color: Colors.black), |
||||
), |
||||
], |
||||
bottom: TabBar( |
||||
controller: _tabController, |
||||
isScrollable: true, |
||||
labelColor: Colors.black, |
||||
unselectedLabelColor: Colors.grey[700], |
||||
indicatorColor: Colors.blue, |
||||
indicatorWeight: 3, |
||||
tabs: const [ |
||||
Tab(text: '问题列表'), |
||||
Tab(text: '历史问题列表'), |
||||
Tab(text: '企业基本情况'), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
// 构建搜索框 |
||||
Widget _buildSearchBar() { |
||||
return Container( |
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), |
||||
color: Colors.lightBlue[50], |
||||
child: Row( |
||||
children: [ |
||||
const Text('淄博', style: TextStyle(fontSize: 16)), |
||||
const Icon(Icons.arrow_drop_down), |
||||
const SizedBox(width: 8), |
||||
Expanded( |
||||
child: Container( |
||||
height: 40, |
||||
decoration: BoxDecoration( |
||||
color: Colors.white, |
||||
borderRadius: BorderRadius.circular(20), |
||||
), |
||||
child: const TextField( |
||||
decoration: InputDecoration( |
||||
hintText: '家庭共济', |
||||
prefixIcon: Icon(Icons.search, color: Colors.grey), |
||||
border: InputBorder.none, |
||||
contentPadding: EdgeInsets.symmetric(vertical: 10), |
||||
), |
||||
), |
||||
), |
||||
), |
||||
TextButton( |
||||
onPressed: () {}, |
||||
child: const Text( |
||||
'搜索', |
||||
style: TextStyle(color: Colors.black, fontSize: 16), |
||||
), |
||||
), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
// 构建功能网格 |
||||
Widget _buildServicesGrid() { |
||||
// 辅助函数创建单个网格项 |
||||
Widget buildGridItem( |
||||
IconData icon, |
||||
String label, { |
||||
bool isSpecial = false, |
||||
}) { |
||||
return Column( |
||||
mainAxisAlignment: MainAxisAlignment.center, |
||||
children: [ |
||||
Stack( |
||||
alignment: Alignment.topRight, |
||||
children: [ |
||||
Icon(icon, size: 40, color: Colors.blue), |
||||
if (isSpecial) |
||||
Container( |
||||
padding: const EdgeInsets.symmetric( |
||||
horizontal: 4, |
||||
vertical: 1, |
||||
), |
||||
decoration: BoxDecoration( |
||||
color: Colors.red, |
||||
borderRadius: BorderRadius.circular(8), |
||||
), |
||||
child: const Text( |
||||
'正品药', |
||||
style: TextStyle(color: Colors.white, fontSize: 8), |
||||
), |
||||
), |
||||
], |
||||
), |
||||
const SizedBox(height: 8), |
||||
Text(label, style: const TextStyle(fontSize: 12)), |
||||
], |
||||
); |
||||
} |
||||
|
||||
return Container( |
||||
color: Colors.lightBlue[50], |
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 24), |
||||
child: GridView.count( |
||||
crossAxisCount: 5, |
||||
shrinkWrap: true, |
||||
physics: const NeverScrollableScrollPhysics(), |
||||
children: [ |
||||
buildGridItem(Icons.qr_code_scanner, '医保码'), |
||||
buildGridItem(Icons.add_box, '挂号'), |
||||
buildGridItem(Icons.person, '问诊'), |
||||
buildGridItem(Icons.medical_services, '买药', isSpecial: true), |
||||
buildGridItem(Icons.shield, '健康保障'), |
||||
buildGridItem(Icons.local_hospital, '好药源选'), |
||||
buildGridItem(Icons.health_and_safety, '体检'), |
||||
buildGridItem(Icons.mood, '口腔'), |
||||
buildGridItem(Icons.grass, '中医'), |
||||
buildGridItem(Icons.apps, '全部服务'), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
// 构建健康管理卡片 |
||||
Widget _buildHealthManagementCard() { |
||||
return Container( |
||||
margin: const EdgeInsets.symmetric(horizontal: 16), |
||||
padding: const EdgeInsets.all(16), |
||||
decoration: BoxDecoration( |
||||
color: Colors.white, |
||||
borderRadius: BorderRadius.circular(12), |
||||
), |
||||
child: Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
const Text( |
||||
'我的健康管理', |
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), |
||||
), |
||||
const SizedBox(height: 12), |
||||
Container( |
||||
padding: const EdgeInsets.all(12), |
||||
decoration: BoxDecoration( |
||||
color: Colors.blue.withOpacity(0.1), |
||||
borderRadius: BorderRadius.circular(8), |
||||
), |
||||
child: Row( |
||||
children: [ |
||||
const Icon(Icons.warning_amber_rounded, color: Colors.orange), |
||||
const SizedBox(width: 8), |
||||
const Expanded( |
||||
child: Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Text( |
||||
'你的补充医保待激活', |
||||
style: TextStyle(fontWeight: FontWeight.bold), |
||||
), |
||||
Text( |
||||
'√ 医保内外都能报 √ 带病投保首选', |
||||
style: TextStyle(color: Colors.grey, fontSize: 12), |
||||
), |
||||
], |
||||
), |
||||
), |
||||
ElevatedButton( |
||||
onPressed: () {}, |
||||
style: ElevatedButton.styleFrom( |
||||
backgroundColor: Colors.orange, |
||||
shape: RoundedRectangleBorder( |
||||
borderRadius: BorderRadius.circular(18.0), |
||||
), |
||||
), |
||||
child: const Text('去激活'), |
||||
), |
||||
], |
||||
), |
||||
), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
// 构建医生问诊横幅 |
||||
Widget _buildDoctorBanner() { |
||||
return Container( |
||||
margin: const EdgeInsets.symmetric(horizontal: 16), |
||||
padding: const EdgeInsets.all(16), |
||||
decoration: BoxDecoration( |
||||
color: Colors.white, |
||||
borderRadius: BorderRadius.circular(12), |
||||
image: const DecorationImage( |
||||
// 模拟医生背景图 |
||||
image: NetworkImage( |
||||
'https://img.tukuppt.com/png_preview/00/34/83/8fT4fFFaLg.jpg!/fw/780', |
||||
), // 使用一个示例医生图片 URL |
||||
fit: BoxFit.cover, |
||||
alignment: Alignment.centerRight, |
||||
opacity: 0.8, |
||||
), |
||||
), |
||||
child: const Row( |
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
children: [ |
||||
Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Text( |
||||
'三甲医生快速问诊', |
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), |
||||
), |
||||
SizedBox(height: 4), |
||||
Text('100%公立医生 不限制问诊次数', style: TextStyle(color: Colors.grey)), |
||||
], |
||||
), |
||||
// 医生图片通过 DecorationImage 实现 |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
// 构建热门服务部分 |
||||
Widget _buildPopularServices() { |
||||
return Container( |
||||
margin: const EdgeInsets.symmetric(horizontal: 16), |
||||
padding: const EdgeInsets.all(16), |
||||
decoration: BoxDecoration( |
||||
color: Colors.white, |
||||
borderRadius: BorderRadius.circular(12), |
||||
), |
||||
child: Column( |
||||
children: [ |
||||
const Row( |
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
children: [ |
||||
Text( |
||||
'热门服务', |
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), |
||||
), |
||||
Text('专病专科', style: TextStyle(color: Colors.grey)), |
||||
Text('健康百科', style: TextStyle(color: Colors.grey)), |
||||
Row( |
||||
children: [ |
||||
Text('更多', style: TextStyle(color: Colors.grey)), |
||||
Icon(Icons.arrow_forward_ios, size: 12, color: Colors.grey), |
||||
], |
||||
), |
||||
], |
||||
), |
||||
const SizedBox(height: 16), |
||||
Row( |
||||
children: [ |
||||
_buildServiceCard( |
||||
'百万医疗险', |
||||
'600万医疗保障', |
||||
Icons.add_to_photos, |
||||
Colors.blue, |
||||
), |
||||
const SizedBox(width: 12), |
||||
_buildServiceCard( |
||||
'百万住院保障', |
||||
'医保内外都可报', |
||||
Icons.security, |
||||
Colors.cyan, |
||||
), |
||||
], |
||||
), |
||||
const SizedBox(height: 12), |
||||
Row( |
||||
children: [ |
||||
_buildServiceCard( |
||||
'医保种牙', |
||||
'种植牙方案科普', |
||||
Icons.mood, |
||||
Colors.lightBlue, |
||||
), |
||||
const SizedBox(width: 12), |
||||
_buildServiceCard( |
||||
'我的家', |
||||
'家人健康我守护', |
||||
Icons.home, |
||||
Colors.purpleAccent, |
||||
), |
||||
], |
||||
), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
|
||||
// 服务卡片的小组件 |
||||
Widget _buildServiceCard( |
||||
String title, |
||||
String subtitle, |
||||
IconData icon, |
||||
Color color, |
||||
) { |
||||
return Expanded( |
||||
child: Container( |
||||
padding: const EdgeInsets.all(12), |
||||
decoration: BoxDecoration( |
||||
color: color.withOpacity(0.1), |
||||
borderRadius: BorderRadius.circular(8), |
||||
), |
||||
child: Row( |
||||
children: [ |
||||
Expanded( |
||||
child: Column( |
||||
crossAxisAlignment: CrossAxisAlignment.start, |
||||
children: [ |
||||
Text( |
||||
title, |
||||
style: const TextStyle(fontWeight: FontWeight.bold), |
||||
), |
||||
const SizedBox(height: 4), |
||||
Text( |
||||
subtitle, |
||||
style: const TextStyle(fontSize: 12, color: Colors.grey), |
||||
), |
||||
], |
||||
), |
||||
), |
||||
Icon(icon, color: color, size: 30), |
||||
], |
||||
), |
||||
), |
||||
); |
||||
} |
||||
|
||||
// 构建底部导航栏 |
||||
Widget _buildBottomNavigationBar() { |
||||
return BottomNavigationBar( |
||||
currentIndex: _bottomNavIndex, |
||||
onTap: (index) { |
||||
setState(() { |
||||
_bottomNavIndex = index; |
||||
}); |
||||
}, |
||||
type: BottomNavigationBarType.fixed, |
||||
selectedItemColor: Colors.blue, |
||||
unselectedItemColor: Colors.grey, |
||||
items: const [ |
||||
BottomNavigationBarItem(icon: Icon(Icons.home), label: '企业'), |
||||
BottomNavigationBarItem( |
||||
icon: Icon(Icons.card_membership), |
||||
label: '全部问题', |
||||
), |
||||
// BottomNavigationBarItem(icon: Icon(Icons.waves), label: 'AQ-健康管家'), |
||||
// BottomNavigationBarItem(icon: Icon(Icons.add_moderator), label: '医疗保障'), |
||||
BottomNavigationBarItem(icon: Icon(Icons.person_outline), label: '我的'), |
||||
], |
||||
); |
||||
} |
||||
} |
Loading…
Reference in new issue