You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
349 lines
11 KiB
349 lines
11 KiB
3 weeks ago
|
import 'dart:io';
|
||
|
|
||
|
import 'package:flutter/material.dart';
|
||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
|
import 'package:get/get.dart';
|
||
|
import 'package:image_picker/image_picker.dart';
|
||
|
import 'package:problem_check_system/controllers/add_problem_controller.dart';
|
||
|
|
||
|
class AddProblemPage extends StatelessWidget {
|
||
|
AddProblemPage({super.key});
|
||
|
|
||
|
final AddProblemController controller = Get.put(AddProblemController());
|
||
|
|
||
|
// 显示底部选择器的方法
|
||
|
void _showImageSourceBottomSheet(BuildContext context) {
|
||
|
showModalBottomSheet(
|
||
|
context: context,
|
||
|
shape: RoundedRectangleBorder(
|
||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16.r)),
|
||
|
),
|
||
|
builder: (BuildContext context) {
|
||
|
return SafeArea(
|
||
|
child: Column(
|
||
|
mainAxisSize: MainAxisSize.min,
|
||
|
children: [
|
||
|
SizedBox(height: 16.h),
|
||
|
Text(
|
||
|
'选择图片来源',
|
||
|
style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold),
|
||
|
),
|
||
|
SizedBox(height: 16.h),
|
||
|
Divider(height: 1.h),
|
||
|
ListTile(
|
||
|
leading: Icon(Icons.camera_alt, color: Colors.blue),
|
||
|
title: Text('拍照'),
|
||
|
onTap: () {
|
||
|
Navigator.pop(context);
|
||
|
controller.pickImage(ImageSource.camera);
|
||
|
},
|
||
|
),
|
||
|
Divider(height: 1.h),
|
||
|
ListTile(
|
||
|
leading: Icon(Icons.photo_library, color: Colors.blue),
|
||
|
title: Text('从相册选择'),
|
||
|
onTap: () {
|
||
|
Navigator.pop(context);
|
||
|
controller.pickImage(ImageSource.gallery);
|
||
|
},
|
||
|
),
|
||
|
Divider(height: 1.h),
|
||
|
ListTile(
|
||
|
leading: Icon(Icons.cancel, color: Colors.grey),
|
||
|
title: Text('取消', style: TextStyle(color: Colors.grey)),
|
||
|
onTap: () => Navigator.pop(context),
|
||
|
),
|
||
|
SizedBox(height: 8.h),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
appBar: AppBar(
|
||
|
flexibleSpace: Container(
|
||
|
decoration: const BoxDecoration(
|
||
|
gradient: LinearGradient(
|
||
|
colors: [Color(0xFF418CFC), Color(0xFF3DBFFC)],
|
||
|
begin: Alignment.centerLeft,
|
||
|
end: Alignment.centerRight,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
leading: IconButton(
|
||
|
icon: const Icon(Icons.arrow_back_ios_new_rounded),
|
||
|
color: Colors.white,
|
||
|
onPressed: () {
|
||
|
Navigator.pop(context);
|
||
|
},
|
||
|
),
|
||
|
title: const Text('新增问题', style: TextStyle(color: Colors.white)),
|
||
|
centerTitle: true,
|
||
|
backgroundColor: Colors.transparent,
|
||
|
),
|
||
|
body: Column(
|
||
|
children: [
|
||
|
Expanded(
|
||
|
child: SingleChildScrollView(
|
||
|
child: Column(
|
||
|
children: [
|
||
|
Card(
|
||
|
margin: EdgeInsets.all(17.w),
|
||
|
child: Column(
|
||
|
children: [
|
||
|
ListTile(
|
||
|
title: const Text(
|
||
|
'问题描述',
|
||
|
style: TextStyle(
|
||
|
fontSize: 16,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
),
|
||
|
),
|
||
|
subtitle: TextField(
|
||
|
maxLines: null,
|
||
|
controller: controller.descriptionController,
|
||
|
decoration: const InputDecoration(
|
||
|
hintText: '请输入问题描述',
|
||
|
border: InputBorder.none,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
Card(
|
||
|
margin: EdgeInsets.all(17.w),
|
||
|
child: Column(
|
||
|
children: [
|
||
|
ListTile(
|
||
|
title: const Text(
|
||
|
'所在位置',
|
||
|
style: TextStyle(
|
||
|
fontSize: 16,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
),
|
||
|
),
|
||
|
subtitle: TextField(
|
||
|
maxLines: null,
|
||
|
controller: controller.locationController,
|
||
|
decoration: const InputDecoration(
|
||
|
hintText: '请输入问题所在位置',
|
||
|
border: InputBorder.none,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
Card(
|
||
|
margin: EdgeInsets.all(17.w),
|
||
|
child: Column(
|
||
|
children: [
|
||
|
ListTile(
|
||
|
title: const Text(
|
||
|
'问题图片',
|
||
|
style: TextStyle(
|
||
|
fontSize: 16,
|
||
|
fontWeight: FontWeight.bold,
|
||
|
),
|
||
|
),
|
||
|
subtitle: Column(
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
children: [
|
||
|
SizedBox(height: 8.h),
|
||
|
_buildImageGridWithAddButton(context),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
_bottomButton(),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Widget _buildImageGridWithAddButton(BuildContext context) {
|
||
|
return Obx(() {
|
||
|
// 计算总项目数(图片数 + 添加按钮)
|
||
|
int totalItems = controller.selectedImages.length + 1;
|
||
|
|
||
|
return GridView.builder(
|
||
|
shrinkWrap: true,
|
||
|
physics: const NeverScrollableScrollPhysics(),
|
||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||
|
crossAxisCount: 3,
|
||
|
crossAxisSpacing: 8.w,
|
||
|
mainAxisSpacing: 8.h,
|
||
|
childAspectRatio: 1,
|
||
|
),
|
||
|
itemCount: totalItems,
|
||
|
itemBuilder: (context, index) {
|
||
|
// 如果是最后一个项目,显示添加按钮
|
||
|
if (index == controller.selectedImages.length) {
|
||
|
return _buildAddImageButton(context);
|
||
|
}
|
||
|
|
||
|
// 否则显示图片
|
||
|
return _buildImageItem(index);
|
||
|
},
|
||
|
);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
Widget _buildAddImageButton(BuildContext context) {
|
||
|
return InkWell(
|
||
|
onTap: () => _showImageSourceBottomSheet(context),
|
||
|
child: Container(
|
||
|
decoration: BoxDecoration(
|
||
|
color: Colors.grey.shade100,
|
||
|
borderRadius: BorderRadius.circular(8),
|
||
|
border: Border.all(color: Colors.grey.shade300, width: 1),
|
||
|
),
|
||
|
child: Column(
|
||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
children: [
|
||
|
Icon(
|
||
|
Icons.add_photo_alternate,
|
||
|
size: 24.sp,
|
||
|
color: Colors.grey.shade600,
|
||
|
),
|
||
|
SizedBox(height: 4.h),
|
||
|
Text(
|
||
|
'添加图片',
|
||
|
style: TextStyle(color: Colors.grey.shade600, fontSize: 12.sp),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Widget _buildImageItem(int index) {
|
||
|
return Container(
|
||
|
decoration: BoxDecoration(
|
||
|
borderRadius: BorderRadius.circular(8),
|
||
|
border: Border.all(color: Colors.grey.shade300),
|
||
|
),
|
||
|
child: Stack(
|
||
|
children: [
|
||
|
ClipRRect(
|
||
|
borderRadius: BorderRadius.circular(8),
|
||
|
child: Image.file(
|
||
|
File(controller.selectedImages[index].path),
|
||
|
width: double.infinity,
|
||
|
height: double.infinity,
|
||
|
fit: BoxFit.cover,
|
||
|
),
|
||
|
),
|
||
|
Positioned(
|
||
|
top: 0,
|
||
|
right: 0,
|
||
|
child: GestureDetector(
|
||
|
onTap: () => controller.removeImage(index),
|
||
|
child: Container(
|
||
|
decoration: const BoxDecoration(
|
||
|
color: Colors.black54,
|
||
|
shape: BoxShape.circle,
|
||
|
),
|
||
|
padding: EdgeInsets.all(4.w),
|
||
|
child: Icon(Icons.close, color: Colors.white, size: 16.sp),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Widget _bottomButton() {
|
||
|
return Container(
|
||
|
width: 375.w,
|
||
|
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
|
||
|
decoration: BoxDecoration(
|
||
|
color: Colors.grey[200],
|
||
|
borderRadius: BorderRadius.only(
|
||
|
topLeft: Radius.circular(12.r),
|
||
|
topRight: Radius.circular(12.r),
|
||
|
),
|
||
|
),
|
||
|
child: Row(
|
||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
|
children: [
|
||
|
Expanded(
|
||
|
child: ElevatedButton(
|
||
|
style: ElevatedButton.styleFrom(
|
||
|
backgroundColor: Colors.white,
|
||
|
shape: RoundedRectangleBorder(
|
||
|
borderRadius: BorderRadius.circular(8.r),
|
||
|
),
|
||
|
padding: EdgeInsets.symmetric(vertical: 12.h),
|
||
|
),
|
||
|
onPressed: () {
|
||
|
Get.back();
|
||
|
},
|
||
|
child: Text(
|
||
|
'取消',
|
||
|
style: TextStyle(color: Colors.grey, fontSize: 16.sp),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
SizedBox(width: 10.w),
|
||
|
Expanded(
|
||
|
child: Obx(
|
||
|
() => ElevatedButton(
|
||
|
style: ElevatedButton.styleFrom(
|
||
|
backgroundColor: const Color(0xFF418CFC),
|
||
|
shape: RoundedRectangleBorder(
|
||
|
borderRadius: BorderRadius.circular(8.r),
|
||
|
),
|
||
|
padding: EdgeInsets.symmetric(vertical: 12.h),
|
||
|
),
|
||
|
onPressed: controller.isLoading.value
|
||
|
? null
|
||
|
: () {
|
||
|
if (controller.descriptionController.text.isEmpty) {
|
||
|
Get.snackbar(
|
||
|
'提示',
|
||
|
'问题描述不能为空',
|
||
|
snackPosition: SnackPosition.TOP,
|
||
|
backgroundColor: Colors.black87,
|
||
|
colorText: Colors.white,
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
controller.saveProblem();
|
||
|
},
|
||
|
child: controller.isLoading.value
|
||
|
? SizedBox(
|
||
|
width: 20.w,
|
||
|
height: 20.h,
|
||
|
child: CircularProgressIndicator(
|
||
|
strokeWidth: 2,
|
||
|
valueColor: const AlwaysStoppedAnimation<Color>(
|
||
|
Colors.white,
|
||
|
),
|
||
|
),
|
||
|
)
|
||
|
: Text(
|
||
|
'确定',
|
||
|
style: TextStyle(color: Colors.white, fontSize: 16.sp),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|