Browse Source

暂存:需要完善上传问题,创建DTO模型

dev
徐振升 1 day ago
parent
commit
d20b677719
  1. 12
      lib/app/core/domain/entities/upload_result.dart
  2. 14
      lib/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart
  3. 1
      lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart
  4. 1
      lib/app/features/enterprise/presentation/pages/widgets/upload_progress_dialog.dart
  5. 259
      lib/app/features/problem/data/model/problem_dto.dart
  6. 26
      lib/app/features/problem/data/repositories/problem_repository_impl.dart
  7. 2
      lib/app/features/problem/domain/repositories/problem_repository.dart
  8. 71
      lib/app/features/problem/domain/usecases/upload_problems_usecase.dart
  9. 2
      lib/app/features/problem/presentation/bindings/problem_upload_binding.dart
  10. 129
      lib/app/features/problem/presentation/controllers/problem_upload_controller.dart
  11. 149
      lib/app/features/problem/presentation/pages/problem_upload_page.dart

12
lib/app/core/domain/entities/upload_result.dart

@ -0,0 +1,12 @@
///
class UploadResult {
final int successCount;
final int failureCount;
final bool wasCancelled;
UploadResult({
this.successCount = 0,
this.failureCount = 0,
this.wasCancelled = false,
});
}

14
lib/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart

@ -1,22 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'package:get/get.dart'; // GetX import 'package:get/get.dart'; // GetX
import 'package:problem_check_system/app/core/domain/entities/upload_result.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart'; import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart'; import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
///
class UploadResult {
final int successCount;
final int failureCount;
final bool wasCancelled;
UploadResult({
this.successCount = 0,
this.failureCount = 0,
this.wasCancelled = false,
});
}
/// ///
class UploadEnterprisesUseCase { class UploadEnterprisesUseCase {
final EnterpriseRepository repository; final EnterpriseRepository repository;

1
lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart

@ -1,6 +1,7 @@
// lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart // lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:problem_check_system/app/core/domain/entities/upload_result.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart'; import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/get_enterprise_list_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart';

1
lib/app/features/enterprise/presentation/pages/widgets/upload_progress_dialog.dart

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:problem_check_system/app/core/domain/entities/upload_result.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart'; import 'package:problem_check_system/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart'; import 'package:problem_check_system/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart';

259
lib/app/features/problem/data/model/problem_dto.dart

@ -1,144 +1,115 @@
// import 'dart:convert'; import 'dart:convert';
// import 'package:problem_check_system/app/core/extensions/map_extensions.dart'; import 'package:problem_check_system/app/core/extensions/map_extensions.dart';
// import 'package:problem_check_system/app/core/models/image_metadata_model.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
// import 'package:problem_check_system/app/core/models/problem_sync_status.dart'; import 'package:problem_check_system/app/features/enterprise/data/model/enterprise_model.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
// ///
// /// /// ProblemDto (Data Transfer Object)
// class ProblemRemoteDto { ///
// /// /// API
// final String id; /// API JSON
class ProblemDto {
// /// id final String? id;
// final String? enterpriseId; final String? title;
final String? location;
// /// final String? censorTaskId;
// final String description; final String? rowId;
final String? bindData;
// /// final List<String?>? imageUrls;
// final String location; final String? creationTime;
final String? companyId;
// ///
// final List<String> imageUrls; ProblemDto({
this.id,
// /// this.title,
// final DateTime creationTime; this.location,
this.censorTaskId,
// /// id this.rowId,
// final String creatorId; this.bindData,
this.imageUrls,
// /// this.creationTime,
// final ProblemSyncStatus syncStatus; this.companyId,
});
// ///
// final DateTime lastModifiedTime;
factory ProblemDto.fromModel(ProblemModel model) {
// /// ID return ProblemDto(
// final String? censorTaskId; id: model.id,
title: model.description,
// /// location: model.location,
// final String? bindData; bindData: model.bindData,
imageUrls: List<String>.from(jsonDecode(model.imageUrls)),
// /// false creationTime: model.creationTime,
// final bool isChecked; companyId: model.enterpriseId,
);
// ProblemRemoteDto({ }
// required this.id, ProblemModel toModel() {
// required this.description, return ProblemModel(
// required this.location, id:
// required this.imageUrls,
// required this.creationTime, );
// required this.creatorId, }
// required this.lastModifiedTime,
// this.syncStatus = ProblemSyncStatus.pendingCreate, // =======================================================================
// this.censorTaskId, //
// this.bindData, // =======================================================================
// this.isChecked = false,
// this.enterpriseId, /// [] fromJson JSON Map EnterpriseDto
// }); ///
/// JSON Dart
// /// copyWith factory ProblemDto.fromJson(Map<String, dynamic> json) {
// ProblemRemoteDto copyWith({ final creationTime = DateTime.parse(json['creationTime'] as String);
// String? id, final creatorId = json['creatorId'] as String;
// String? enterpriseId, final lastModTimeStr = json['lastModificationTime'] as String?;
// String? description, return ProblemDto(
// String? location, //
// List<ImageMetadata>? imageUrls, id: json['id'] as String,
// DateTime? creationTime, creationTime: creationTime,
// String? creatorId, creatorId: creatorId,
// DateTime? lastModifiedTime, lastModificationTime: lastModTimeStr != null
// ProblemSyncStatus? syncStatus, ? DateTime.parse(lastModTimeStr)
// bool? isDeleted, : creationTime,
// String? censorTaskId, lastModifierId: json['lastModifierId'] as String? ?? creatorId,
// String? bindData, companyName: json['companyName'] as String,
// bool? isChecked, companyType: json['companyType'] as String? ?? "生产",
// }) {
// return ProblemRemoteDto( //
// id: id ?? this.id, companyScope: json['companyScope'] as String?,
// enterpriseId: enterpriseId ?? this.enterpriseId, mainPrincipalName: json['mainPrincipalName'] as String?,
// description: description ?? this.description, mainPrincipalPhone: json['mainPrincipalPhone'] as String?,
// location: location ?? this.location, securityPrincipalName: json['securityPrincipalName'] as String?,
// imageUrls: imageUrls ?? this.imageUrls, securityPrincipalPhone: json['securityPrincipalPhone'] as String?,
// creationTime: creationTime ?? this.creationTime, companyAddress: json['companyAddress'] as String?,
// creatorId: creatorId ?? this.creatorId, majorHazard: json['majorHazard'] as String?,
// lastModifiedTime: lastModifiedTime ?? this.lastModifiedTime, );
// syncStatus: syncStatus ?? this.syncStatus, }
// censorTaskId: censorTaskId ?? this.censorTaskId,
// bindData: bindData ?? this.bindData, /// [] toJson EnterpriseDto JSON Map
// isChecked: isChecked ?? this.isChecked, ///
// ); /// JSON
// } Map<String, dynamic> toJson() {
final jsonMap = {
// /// JSON字符串 'id': id,
// Map<String, dynamic> toJson() { // 使 toIso8601String() DateTime
// return { 'creationTime': creationTime.toIso8601String(),
// 'id': id, 'creatorId': creatorId,
// 'enterpriseId': enterpriseId, 'lastModificationTime': lastModificationTime?.toIso8601String(),
// 'description': description, 'lastModifierId': lastModifierId,
// 'location': location, 'companyName': companyName,
// 'imageUrls': json.encode(imageUrls.map((e) => e.toMap()).toList()), 'companyType': companyType,
// 'creationTime': creationTime.toIso8601String(), 'companyScope': companyScope,
// 'creatorId': creatorId, 'mainPrincipalName': mainPrincipalName,
// 'lastModifiedTime': lastModifiedTime.toIso8601String(), 'mainPrincipalPhone': mainPrincipalPhone,
// 'censorTaskId': censorTaskId, 'securityPrincipalName': securityPrincipalName,
// 'bindData': bindData, 'securityPrincipalPhone': securityPrincipalPhone,
// }.withoutNullOrEmptyValues; 'companyAddress': companyAddress,
// } 'majorHazard': majorHazard,
};
// /// Map创建对象SQLite读取 return jsonMap.withoutNullOrEmptyValues;
// factory ProblemRemoteDto.fromJson(Map<String, dynamic> map) { }
// // imageUrls的转换
// List<ImageMetadata> imageUrlsList = []; /// [] DTO () Model (/)
// if (map['imageUrls'] != null) { ///
// try { ///
// final List<dynamic> imageList = json.decode(map['imageUrls']); }
// imageUrlsList = imageList.map((e) => ImageMetadata.fromMap(e)).toList();
// } catch (e) {
// //
// imageUrlsList = [];
// }
// }
// return ProblemRemoteDto(
// id: map['id'],
// enterpriseId: map['companyId'],
// description: map['description'],
// location: map['location'],
// imageUrls: imageUrlsList,
// creationTime: DateTime.fromMillisecondsSinceEpoch(
// map['creationTime'],
// isUtc: true,
// ),
// creatorId: map['creatorId'],
// lastModifiedTime: DateTime.fromMillisecondsSinceEpoch(
// map['lastModifiedTime'],
// isUtc: true,
// ),
// syncStatus: ProblemSyncStatus.values[map['syncStatus']],
// censorTaskId: map['censorTaskId'],
// bindData: map['bindData'],
// isChecked: map['isChecked'] == 1,
// );
// }
// }

26
lib/app/features/problem/data/repositories/problem_repository_impl.dart

@ -2,7 +2,6 @@ import 'dart:convert';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart'; import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_data_source.dart'; import 'package:problem_check_system/app/features/problem/data/datasources/problem_local_data_source.dart';
import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart'; import 'package:problem_check_system/app/features/problem/data/model/problem_model.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_bind_status.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_entity.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_filter_params.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart'; import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart';
@ -117,4 +116,29 @@ class ProblemRepository implements IProblemRepository {
// 3. // 3.
return problem; return problem;
} }
// TODO:
@override
Future<ProblemEntity> syncProblemToServer(ProblemEntity problem) {
// Domain Data Model
final problemModel = ProblemModel.fromEntity(problem);
EnterpriseDto syncedDto;
// ****
// Repository DataSource
switch (enterprise.syncStatus) {
case SyncStatus.pendingCreate:
syncedDto = await remoteDataSource.createEnterprise(enterpriseModel);
break;
case SyncStatus.pendingUpdate:
syncedDto = await remoteDataSource.updateEnterprise(enterpriseModel);
break;
case SyncStatus.pendingDelete:
await remoteDataSource.deleteEnterprise(enterprise.id);
return enterprise;
//
case SyncStatus.synced:
case SyncStatus.untracked:
return enterprise;
}
return syncedDto.toModel().toEntity();
}
} }

2
lib/app/features/problem/domain/repositories/problem_repository.dart

@ -12,4 +12,6 @@ abstract class IProblemRepository {
Future<ProblemEntity> addProblem(ProblemEntity problem); Future<ProblemEntity> addProblem(ProblemEntity problem);
Future<ProblemEntity> updateProblem(ProblemEntity problem); Future<ProblemEntity> updateProblem(ProblemEntity problem);
Future<void> deleteProblem(String id); Future<void> deleteProblem(String id);
//
Future<ProblemEntity> syncProblemToServer(ProblemEntity problem);
} }

71
lib/app/features/problem/domain/usecases/upload_problems_usecase.dart

@ -0,0 +1,71 @@
import 'dart:async';
import 'package:get/get.dart'; // GetX
import 'package:problem_check_system/app/core/domain/entities/upload_result.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/repositories/enterprise_repository.dart';
import 'package:problem_check_system/app/core/domain/entities/sync_status.dart';
import 'package:problem_check_system/app/features/problem/data/repositories/problem_repository_impl.dart';
import 'package:problem_check_system/app/features/problem/domain/entities/problem_list_item_entity.dart';
///
class UploadProblemsUsecase {
final ProblemRepository repository;
UploadProblemsUsecase({required this.repository});
// 使 GetX RxBool
// Controller UseCase
final RxBool _isCancelled = false.obs;
/// []
void cancel() {
_isCancelled.value = true;
}
///
/// [onProgress] (Controller)
Future<UploadResult> call({
required List<ProblemListItemEntity> problemsToUpload,
required void Function(int uploaded, int total) onProgress,
}) async {
// 便
_isCancelled.value = false;
int successCount = 0;
int failureCount = 0;
final total = problemsToUpload.length;
for (int i = 0; i < total; i++) {
//
if (_isCancelled.value) {
//
return UploadResult(
successCount: successCount,
failureCount: failureCount,
wasCancelled: true,
);
}
final problem = problemsToUpload[i].problemEntity;
try {
// 1.
final syncedProblem = await repository.syncProblemToServer(problem);
// 2.
if (problem.syncStatus == SyncStatus.pendingDelete) {
// repository.deleteLocalEnterprise(enterprise.id);
} else {
await repository.updateProblem(syncedProblem);
}
successCount++;
} catch (e) {
//
failureCount++;
Get.log('上传失败: ${problem.description}, 错误: $e');
}
// 3.
onProgress(i + 1, total);
}
//
return UploadResult(successCount: successCount, failureCount: failureCount);
}
}

2
lib/app/features/problem/presentation/bindings/problem_upload_binding.dart

@ -25,6 +25,6 @@ class ProblemUploadBinding extends BaseBindings {
@override @override
void register5Controllers() { void register5Controllers() {
Get.lazyPut(() => ProblemUploadController()); // Get.lazyPut(() => ProblemUploadController());
} }
} }

129
lib/app/features/problem/presentation/controllers/problem_upload_controller.dart

@ -1,13 +1,128 @@
import 'dart:ui'; // lib/app/features/enterprise/presentation/controllers/enterprise_upload_controller.dart
class ProblemUploadController { import 'package:get/get.dart';
int get selectedCount => 10; import 'package:problem_check_system/app/core/domain/entities/upload_result.dart';
import 'package:problem_check_system/app/features/enterprise/domain/entities/enterprise_list_item.dart';
import 'package:problem_check_system/app/features/enterprise/domain/usecases/upload_enterprises_usecase.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/upload_progress_dialog.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/get_all_problems_usecase.dart';
import 'package:problem_check_system/app/features/problem/domain/usecases/upload_problems_usecase.dart';
get allSelected => null; class ProblemUploadController extends GetxController {
final GetAllProblemsUsecase getAllProblemsUsecase;
final UploadProblemsUsecase uploadProblemsUsecase;
get unUploadedProblems => null; ProblemUploadController({
required this.getAllProblemsUsecase,
required this.uploadProblemsUsecase,
});
VoidCallback? get selectAll => null; // --- ---
final isLoading = false.obs;
final enterprises = <EnterpriseListItem>[].obs;
final selectedEnterprises = <EnterpriseListItem>{}.obs;
// --- [] ---
final isUploading = false.obs;
final uploadProgress = 0.0.obs; // 0.0 1.0
final uploadedCount = 0.obs;
final totalToUpload = 0.obs;
final uploadResult = Rxn<UploadResult>(); //
get handleUpload => null; bool get allSelected =>
enterprises.isNotEmpty &&
enterprises.length == selectedEnterprises.length;
@override
void onInit() {
super.onInit();
fetchPendingUploads();
}
void onSelectionChanged(EnterpriseListItem enterprise) {
if (selectedEnterprises.contains(enterprise)) {
selectedEnterprises.remove(enterprise);
} else {
selectedEnterprises.add(enterprise);
}
}
void toggleSelectAll() {
if (allSelected) {
selectedEnterprises.clear();
} else {
//
selectedEnterprises.addAll(enterprises);
}
}
///
Future<void> fetchPendingUploads() async {
isLoading.value = true;
try {
final data = await getEnterpriseListUsecase(isUploaded: false);
enterprises.assignAll(data);
} catch (e) {
Get.snackbar('错误', '加载待上传列表失败: $e');
} finally {
isLoading.value = false;
}
}
void confirmUpload() {
if (selectedEnterprises.isEmpty) {
Get.snackbar('提示', '请至少选择一个企业进行上传');
return;
}
// 1.
Get.dialog(
const UploadProgressDialog(),
barrierDismissible: false, //
);
// 2.
_startUpload();
}
/// []
Future<void> _startUpload() async {
//
isUploading.value = true;
uploadProgress.value = 0.0;
uploadResult.value = null;
totalToUpload.value = selectedEnterprises.length;
uploadedCount.value = 0;
// UseCase onProgress
final result = await uploadEnterprisesUseCase.call(
enterprisesToUpload: selectedEnterprises.toList(),
onProgress: (uploaded, total) {
//
uploadedCount.value = uploaded;
uploadProgress.value = uploaded / total;
},
);
//
isUploading.value = false;
uploadResult.value = result;
}
/// []
void cancelUpload() {
uploadEnterprisesUseCase.cancel();
}
/// []
void closeUploadDialog() {
Get.back(); //
// true
if (uploadResult.value != null &&
!uploadResult.value!.wasCancelled &&
uploadResult.value!.successCount > 0) {
Get.back(result: true); //
} else {
//
//
fetchPendingUploads();
}
}
} }

149
lib/app/features/problem/presentation/pages/problem_upload_page.dart

@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
import 'package:get/get_state_manager/src/simple/get_view.dart';
import 'package:problem_check_system/app/core/pages/widgets/upload_app_bar.dart'; import 'package:problem_check_system/app/core/pages/widgets/upload_app_bar.dart';
import 'package:problem_check_system/app/features/enterprise/presentation/pages/widgets/unified_enterprise_card.dart';
import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_upload_controller.dart'; import 'package:problem_check_system/app/features/problem/presentation/controllers/problem_upload_controller.dart';
class ProblemUploadPage extends GetView<ProblemUploadController> { class ProblemUploadPage extends GetView<ProblemUploadController> {
@ -10,57 +12,110 @@ class ProblemUploadPage extends GetView<ProblemUploadController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: UploadAppBar( appBar: PreferredSize(
selectedCount: controller.selectedCount, preferredSize: const Size.fromHeight(kToolbarHeight),
allSelected: controller.allSelected.value, child: Obx(
buttonVisible: controller.unUploadedProblems.isNotEmpty, () => UploadAppBar(
onButtonPressed: controller.selectAll, selectedCount: controller.selectedEnterprises.length,
), allSelected: controller.allSelected,
body: _buildBody(), buttonVisible: controller.enterprises.isNotEmpty,
bottomNavigationBar: _buildBottomBar(), onButtonPressed: () => controller.toggleSelectAll(),
); ),
} ),
Widget _buildBody() {
return Center(
child: Text('暂无未上传的问题', style: TextStyle(fontSize: 16.sp)),
);
// return Obx(() {
// if (controller.unUploadedProblems.isEmpty) {
// return Center(
// child: Text('暂无未上传的问题', style: TextStyle(fontSize: 16.sp)),
// );
// }
// return ProblemListPage(
// problemsToShow: controller.unUploadedProblems,
// viewType: ProblemCardViewType.checkbox,
// );
// });
}
Widget _buildBottomBar() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
border: Border(top: BorderSide(color: Colors.grey.shade300)),
), ),
child: Obx( body: _buildProblemList(),
() => ElevatedButton( bottomNavigationBar: Container(
onPressed: controller.selectedCount > 0 padding: EdgeInsets.all(16.w),
? controller.handleUpload decoration: BoxDecoration(
: null, color: Colors.white,
style: ElevatedButton.styleFrom( border: Border(top: BorderSide(color: Colors.grey.shade300)),
backgroundColor: Colors.blue, ),
foregroundColor: Colors.white, child: Obx(
shape: RoundedRectangleBorder( () => ElevatedButton(
borderRadius: BorderRadius.circular(8.r), onPressed: controller.selectedEnterprises.isNotEmpty
? controller.confirmUpload
: null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
minimumSize: Size(double.infinity, 48.h),
), ),
minimumSize: Size(double.infinity, 48.h), child: Text('确认上传 (${controller.selectedEnterprises.length})'),
), ),
child: Text('点击上传 (${controller.selectedCount})'),
), ),
), ),
); );
} }
Widget _buildProblemList() {
// 使 Obx controller Rx
return Obx(() {
//
if (controller.isLoading.value && controller.enterprises.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
//
if (controller.enterprises.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.folder_off_outlined,
size: 60.sp,
color: Colors.grey[400],
),
SizedBox(height: 16.h),
Text(
'没有找到相关问题',
style: TextStyle(fontSize: 16.sp, color: Colors.grey[600]),
),
],
),
);
}
// 使
return RefreshIndicator(
onRefresh: () async => controller.fetchPendingUploads(),
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
itemCount: controller.enterprises.length,
itemBuilder: (context, index) {
final item = controller.enterprises[index];
// : base controller
return Obx(() {
final isSelected = controller.selectedEnterprises.contains(item);
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: UnifiedEnterpriseCard(
enterpriseListItem: item,
isSelected: isSelected,
// itemMode mode
// --- [] controller ---
onTap: () => controller.onSelectionChanged(item),
actions: Padding(
// Checkbox
padding: EdgeInsets.only(right: 8.w),
child: Checkbox(
value: isSelected,
activeColor: Theme.of(context).primaryColor,
onChanged: (value) {
controller.onSelectionChanged(item);
},
),
),
),
);
});
},
),
);
});
}
} }

Loading…
Cancel
Save