Skip to content

Commit f0e1a16

Browse files
ZahraErfaniZahraErfani
ZahraErfani
authored and
ZahraErfani
committed
add image and show it
1 parent 7af4b56 commit f0e1a16

File tree

12 files changed

+306
-7
lines changed

12 files changed

+306
-7
lines changed

android/app/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package="com.example.todo">
33
<uses-permission android:name="android.permission.RECORD_AUDIO" />
44
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
5+
<uses-permission android:name="android.permission.CAMERA"/>
56
<application
67
android:label="todo"
78
android:name="${applicationName}"

lib/Utils/upload_image.dart

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'package:flutter/foundation.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:image_picker/image_picker.dart';
4+
import 'package:todo/Utils/utils.dart';
5+
6+
class UploadImage {
7+
UploadImage._();
8+
Future<bool> requestPermission(type) async {
9+
if (type == ImageSource.gallery) {
10+
return await Utils.getStoragePermission();
11+
} else {
12+
return await Utils.getCameraPermission();
13+
}
14+
}
15+
16+
static Future<XFile?> openImagePicker({
17+
required ImageSource source,
18+
required BuildContext context,
19+
}) async {
20+
Uint8List? imageBytes;
21+
XFile? choosePhoto;
22+
bool permission = false;
23+
if (!kIsWeb) {
24+
if (source == ImageSource.gallery) {
25+
permission = await Utils.getStoragePermission();
26+
} else {
27+
permission = await Utils.getCameraPermission();
28+
}
29+
30+
if (permission) {
31+
ImagePicker picker = ImagePicker();
32+
XFile? photo = await picker.pickImage(
33+
source: source, maxWidth: 1500, maxHeight: 1500, imageQuality: 99);
34+
if (photo != null) {
35+
choosePhoto = photo;
36+
imageBytes = await photo.readAsBytes();
37+
}
38+
return choosePhoto;
39+
} else {
40+
if (context.mounted) {
41+
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
42+
content: Text('Permission'),
43+
duration: Duration(seconds: 1),
44+
));
45+
}
46+
}
47+
}
48+
return null;
49+
}
50+
}

lib/Utils/utils.dart

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import 'package:permission_handler/permission_handler.dart';
2+
3+
class Utils {
4+
Utils._();
5+
static Future<bool> getStoragePermission() async {
6+
final status = await Permission.storage.request();
7+
if (status == PermissionStatus.granted) {
8+
return true;
9+
} else {
10+
return false;
11+
}
12+
}
13+
14+
static Future<bool> getCameraPermission() async {
15+
final status = await Permission.camera.request();
16+
if (status.isGranted) {
17+
return true;
18+
} else {
19+
return false;
20+
}
21+
}
22+
}

lib/config/route/const.dart

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ class RouteName {
33
static const String init = '/';
44
static const String home = '/home';
55
static const String addTask = '/addTask';
6+
static const String showImage = '/showImage';
67
}

lib/config/route/generate.dart

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'package:flutter/material.dart';
2+
import 'package:image_picker/image_picker.dart';
23
import 'package:todo/screen/add_task/add_task.dart';
34
import 'package:todo/screen/home/home.dart';
5+
import 'package:todo/screen/show_image/show_image.dart';
46
import 'package:todo/screen/splash/splash.dart';
57

68
import 'const.dart';
@@ -17,6 +19,8 @@ class RouteGenerator {
1719
return const Home();
1820
case RouteName.addTask:
1921
return const AddTask();
22+
case RouteName.showImage:
23+
return ShowImage(image: args as XFile);
2024
default:
2125
return _errorRoute(settings.name);
2226
}

lib/data/hive/models/task.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Task extends HiveObject {
1515
@HiveField(4)
1616
final String? note;
1717
@HiveField(5)
18-
final String? image;
18+
final List<String?>? image;
1919
@HiveField(6)
2020
final List<SubTask>? subTask;
2121
Task(

lib/data/hive/models/task.g.dart

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/screen/add_task/add_task.dart

+107-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1+
import 'dart:io';
2+
13
import 'package:flutter/material.dart';
4+
import 'package:image_picker/image_picker.dart';
25
import 'package:record/record.dart';
6+
import 'package:todo/Utils/upload_image.dart';
7+
import 'package:todo/config/route/const.dart';
38
import 'package:todo/config/themes/my_drawing.dart';
9+
import 'package:todo/data/hive/models/task.dart';
10+
import 'package:todo/data/hive/requests/task_request.dart';
411
import 'package:todo/data/media_query/media_query.dart';
512
import 'package:todo/data/media_query/space_between.dart';
613
import 'package:todo/data/model/front/header_model.dart';
@@ -25,6 +32,7 @@ class _AddTaskState extends State<AddTask> {
2532
final record = Record();
2633
bool recordAudio = false;
2734
String? audioPath;
35+
List<String?> choosePhoto = [];
2836

2937
@override
3038
void initState() {
@@ -63,7 +71,7 @@ class _AddTaskState extends State<AddTask> {
6371
name = text;
6472
});
6573
},
66-
labelText: "Task Name",
74+
labelText: "Task name",
6775
validation: "required",
6876
preIcon: Icons.task,
6977
),
@@ -79,7 +87,6 @@ class _AddTaskState extends State<AddTask> {
7987
title: "record",
8088
child: AudioRecorder(
8189
onStop: (path) {
82-
print('Recorded file path: $path');
8390
setState(() {
8491
audioPath = path;
8592
recordAudio = true;
@@ -107,7 +114,7 @@ class _AddTaskState extends State<AddTask> {
107114
note = text;
108115
});
109116
},
110-
labelText: "Add a Note",
117+
labelText: "Add a note",
111118
validation: "required",
112119
preIcon: Icons.note,
113120
),
@@ -117,9 +124,65 @@ class _AddTaskState extends State<AddTask> {
117124
loading: false,
118125
showBoxShadow: false,
119126
icon: Icons.image,
120-
onTab: () {},
127+
onTab: () => chooseImageSource(),
121128
title: "Add an image"),
122129
intermediate(20),
130+
choosePhoto.isNotEmpty
131+
? Wrap(
132+
children: [
133+
for (int index = 0;
134+
index < choosePhoto.length;
135+
index++)
136+
Padding(
137+
padding: const EdgeInsets.symmetric(
138+
vertical: 10.0, horizontal: 10.0),
139+
child: InkWell(
140+
onTap: () => Navigator.of(context).pushNamed(
141+
RouteName.showImage,
142+
arguments: choosePhoto[index]!),
143+
child: Stack(
144+
alignment: Alignment.topLeft,
145+
children: [
146+
ClipRRect(
147+
borderRadius: BorderRadius.circular(4.0),
148+
child: Image.file(
149+
File(choosePhoto[index]!),
150+
width: 100,
151+
height: 100,
152+
fit: BoxFit.cover,
153+
),
154+
),
155+
InkWell(
156+
onTap: () {
157+
setState(() {
158+
choosePhoto.removeAt(index);
159+
});
160+
},
161+
child: Padding(
162+
padding: const EdgeInsets.symmetric(
163+
vertical: 3, horizontal: 3),
164+
child: ClipOval(
165+
child: Container(
166+
alignment: Alignment.center,
167+
width: 25,
168+
height: 25,
169+
color: MyColors.white,
170+
child: const Icon(
171+
Icons.clear,
172+
color: MyColors.red,
173+
size: 20,
174+
),
175+
),
176+
),
177+
))
178+
],
179+
),
180+
),
181+
),
182+
],
183+
)
184+
: Container(),
185+
intermediate(20),
123186
ButtonLoading(
124187
btnColor: MyColors.primaryDark,
125188
btnWidth: context.width - 20,
@@ -135,9 +198,48 @@ class _AddTaskState extends State<AddTask> {
135198
);
136199
}
137200

201+
chooseImageSource() async {
202+
final res = await ModalClass.showModalBottomCustomSheet(
203+
context: context,
204+
child: Container(
205+
color: MyColors.white,
206+
width: context.width,
207+
height: 200,
208+
child: Column(
209+
children: [
210+
intermediate(20),
211+
ButtonLoading(
212+
loading: false,
213+
showBoxShadow: false,
214+
onTab: () => Navigator.of(context).pop("camera"),
215+
title: "Camera"),
216+
intermediate(20),
217+
ButtonLoading(
218+
loading: false,
219+
showBoxShadow: false,
220+
onTab: () => Navigator.of(context).pop("gallery"),
221+
title: "Gallery"),
222+
],
223+
)));
224+
if (res != null && context.mounted) {
225+
final result = await UploadImage.openImagePicker(
226+
source: res == "camera" ? ImageSource.camera : ImageSource.gallery,
227+
context: context);
228+
if (result != null) {
229+
setState(() {
230+
choosePhoto.add(result.path);
231+
});
232+
}
233+
}
234+
}
235+
138236
save() {
139-
if (formKey.currentState!.validate()) {
237+
if (formKey.currentState!.validate() &&
238+
audioPath != null &&
239+
choosePhoto.isNotEmpty) {
140240
print("1111111111111111111");
241+
TaskHiveRequest.addTask(Task(
242+
taskName: name!, record: audioPath, note: note!, image: choosePhoto));
141243
} else {
142244
print("ajsdhgkjd");
143245
}

lib/screen/show_image/show_image.dart

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import 'dart:io';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:image_picker/image_picker.dart';
5+
import 'package:todo/data/media_query/media_query.dart';
6+
import 'package:todo/data/model/front/header_model.dart';
7+
import 'package:todo/widgets/appbar/my_custom_appbar.dart';
8+
9+
class ShowImage extends StatelessWidget {
10+
final XFile image;
11+
const ShowImage({Key? key, required this.image}) : super(key: key);
12+
13+
@override
14+
Widget build(BuildContext context) {
15+
return Scaffold(
16+
appBar: MyCustomAppBar(
17+
item: HeaderModel(title: "Image", icon: []),
18+
backButton: true,
19+
),
20+
body: Center(
21+
child: ClipRRect(
22+
borderRadius: BorderRadius.circular(4.0),
23+
child: Image.file(
24+
File(image.path),
25+
width: context.width - 100,
26+
height: context.width - 100,
27+
fit: BoxFit.cover,
28+
),
29+
),
30+
),
31+
);
32+
}
33+
}

lib/widgets/modal/modal.dart

+20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'package:flutter/material.dart';
22

33
class ModalClass {
4+
ModalClass._();
5+
46
static Future showModalBottomPage({
57
required BuildContext context,
68
required Widget child,
@@ -42,4 +44,22 @@ class ModalClass {
4244
},
4345
);
4446
}
47+
48+
static Future showModalBottomCustomSheet({
49+
required BuildContext context,
50+
required Widget child,
51+
}) async {
52+
final res = await showModalBottomSheet(
53+
context: context,
54+
shape: const RoundedRectangleBorder(
55+
borderRadius: BorderRadius.only(
56+
topLeft: Radius.circular(30.0), topRight: Radius.circular(30.0)),
57+
),
58+
builder: (context) {
59+
return DraggableScrollableSheet(
60+
builder: (_, controller) => Container(child: child),
61+
);
62+
});
63+
return res;
64+
}
4565
}

0 commit comments

Comments
 (0)