Skip to content
Snippets Groups Projects
Commit bb9826e8 authored by Benoît Harrault's avatar Benoît Harrault
Browse files

Add take and save pictures from camera feature

parent 2c5ab76e
No related branches found
No related tags found
1 merge request!50Resolve "Add "take and save pictures" feature"
Pipeline #4942 passed
...@@ -44,7 +44,7 @@ android { ...@@ -44,7 +44,7 @@ android {
defaultConfig { defaultConfig {
applicationId "org.benoitharrault.random" applicationId "org.benoitharrault.random"
minSdkVersion 19 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode appVersionCode.toInteger() versionCode appVersionCode.toInteger()
versionName appVersionName versionName appVersionName
......
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
app.versionName=1.0.43 app.versionName=1.0.44
app.versionCode=44 app.versionCode=45
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
"app_name": "Sandbox App", "app_name": "Sandbox App",
"bottom_nav_sample": "Sample", "bottom_nav_sample": "Sample",
"bottom_nav_camera": "Camera",
"bottom_nav_api": "API", "bottom_nav_api": "API",
"bottom_nav_chart": "Graph", "bottom_nav_chart": "Graph",
"bottom_nav_game": "Game", "bottom_nav_game": "Game",
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
"app_name": "App de test", "app_name": "App de test",
"bottom_nav_sample": "Démo", "bottom_nav_sample": "Démo",
"bottom_nav_camera": "Caméra",
"bottom_nav_api": "API", "bottom_nav_api": "API",
"bottom_nav_chart": "Graph", "bottom_nav_chart": "Graph",
"bottom_nav_game": "Jeu", "bottom_nav_game": "Jeu",
......
...@@ -3,7 +3,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; ...@@ -3,7 +3,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
class BottomNavCubit extends HydratedCubit<int> { class BottomNavCubit extends HydratedCubit<int> {
BottomNavCubit() : super(0); BottomNavCubit() : super(0);
int pagesCount = 5; int pagesCount = 6;
void updateIndex(int index) { void updateIndex(int index) {
if (isIndexAllowed(index)) { if (isIndexAllowed(index)) {
......
import 'package:flutter/material.dart';
import 'package:unicons/unicons.dart';
import 'package:random/ui/widgets/take_picture_widget.dart';
class CameraPage extends StatelessWidget {
const CameraPage({super.key});
static Icon navBarIcon = const Icon(UniconsLine.camera);
static String navBarText = 'bottom_nav_camera';
@override
Widget build(BuildContext context) {
return Material(
color: Theme.of(context).colorScheme.background,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const SizedBox(height: 8),
const TakePictureWidget(),
],
),
);
}
}
...@@ -5,6 +5,7 @@ import 'package:flutter_swipe/flutter_swipe.dart'; ...@@ -5,6 +5,7 @@ import 'package:flutter_swipe/flutter_swipe.dart';
import 'package:random/cubit/bottom_nav_cubit.dart'; import 'package:random/cubit/bottom_nav_cubit.dart';
import 'package:random/ui/screens/about_page.dart'; import 'package:random/ui/screens/about_page.dart';
import 'package:random/ui/screens/api_page.dart'; import 'package:random/ui/screens/api_page.dart';
import 'package:random/ui/screens/camera_page.dart';
import 'package:random/ui/screens/demo_page.dart'; import 'package:random/ui/screens/demo_page.dart';
import 'package:random/ui/screens/game_page.dart'; import 'package:random/ui/screens/game_page.dart';
import 'package:random/ui/screens/graph_page.dart'; import 'package:random/ui/screens/graph_page.dart';
...@@ -25,6 +26,7 @@ class _SkeletonScreenState extends State<SkeletonScreen> { ...@@ -25,6 +26,7 @@ class _SkeletonScreenState extends State<SkeletonScreen> {
const List<Widget> pageNavigation = <Widget>[ const List<Widget> pageNavigation = <Widget>[
DemoPage(), DemoPage(),
ApiPage(), ApiPage(),
CameraPage(),
GraphPage(), GraphPage(),
GamePage(), GamePage(),
SettingsPage(), SettingsPage(),
......
...@@ -50,6 +50,10 @@ class BottomNavBar extends StatelessWidget { ...@@ -50,6 +50,10 @@ class BottomNavBar extends StatelessWidget {
icon: const Icon(UniconsLine.globe), icon: const Icon(UniconsLine.globe),
label: tr('bottom_nav_api'), label: tr('bottom_nav_api'),
), ),
BottomNavigationBarItem(
icon: const Icon(UniconsLine.camera),
label: tr('bottom_nav_camera'),
),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: const Icon(UniconsLine.pen), icon: const Icon(UniconsLine.pen),
label: tr('bottom_nav_chart'), label: tr('bottom_nav_chart'),
......
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:unicons/unicons.dart';
import 'package:random/utils/picture_storage.dart';
class TakePictureWidget extends StatefulWidget {
const TakePictureWidget({super.key});
@override
TakePictureWidgetState createState() => TakePictureWidgetState();
}
class TakePictureWidgetState extends State<TakePictureWidget> {
CameraController? controller;
PictureStorage? storage;
List<String> previousImages = [];
List<String> debug = [];
@override
void initState() {
loadCamera();
storage = PictureStorage();
super.initState();
}
loadCamera() async {
final List<CameraDescription>? cameras = await availableCameras();
if (cameras != null) {
controller = CameraController(
cameras.first,
ResolutionPreset.max,
enableAudio: false,
);
controller!.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
} else {
print("No camera found.");
}
}
@override
void dispose() {
controller!.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 400,
child: controller == null
? Center(child: Text("Loading camera..."))
: !controller!.value.isInitialized
? Center(child: CircularProgressIndicator())
: CameraPreview(controller!),
),
ElevatedButton.icon(
label: Text("Take picture"),
icon: Icon(UniconsLine.camera),
onPressed: () async {
try {
if ((controller != null) && (controller!.value.isInitialized)) {
final XFile image = await controller!.takePicture();
print('image.path: ' + image.path);
debug.add('image.path: ' + image.path);
File savedFile = await storage!.writeCounter(File(image.path));
debug.add('image.path: ' + image.path);
String imagePath = savedFile.path;
print('imagePath: ' + imagePath);
debug.add('imagePath: ' + imagePath);
previousImages.add(imagePath);
setState(() {});
}
} catch (e) {
debug.add('error: ' + e.toString());
setState(() {});
print(e);
}
},
),
Text('debug: '),
Column(
children: debug.map((String line) {
return Text(line);
}).toList(),
),
previousImages.length == 0
? Text('no previous images')
: Column(
children: previousImages.map((String imagePath) {
return Row(
children: [
// Image.file(File(imagePath)),
Text(imagePath),
],
);
}).toList(),
),
],
),
);
}
}
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
class PictureStorage {
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<String> _localFilePath(String name) async {
final path = await _localPath;
return path + '/' + name;
}
Future<File> moveFile(File sourceFile, String newPath) async {
try {
return await sourceFile.rename(newPath);
} on FileSystemException catch (e) {
print('Found exception while moving file: ' + e.toString());
final newFile = await sourceFile.copy(newPath);
await sourceFile.delete();
return newFile;
}
}
Future<File> writeCounter(File sourceFile) async {
final targetFile = await _localFilePath(basename(sourceFile.path));
return moveFile(sourceFile, targetFile);
}
}
...@@ -25,6 +25,46 @@ packages: ...@@ -25,6 +25,46 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.1.2" version: "8.1.2"
camera:
dependency: "direct main"
description:
name: camera
sha256: "9499cbc2e51d8eb0beadc158b288380037618ce4e30c9acbc4fae1ac3ecb5797"
url: "https://pub.dev"
source: hosted
version: "0.10.5+9"
camera_android:
dependency: transitive
description:
name: camera_android
sha256: "351429510121d179b9aac5a2e8cb525c3cd6c39f4d709c5f72dfb21726e52371"
url: "https://pub.dev"
source: hosted
version: "0.10.8+16"
camera_avfoundation:
dependency: transitive
description:
name: camera_avfoundation
sha256: "7d0763dfcbf060f56aa254a68c103210280bee9e97bbe4fdef23e257a4f70ab9"
url: "https://pub.dev"
source: hosted
version: "0.9.14"
camera_platform_interface:
dependency: transitive
description:
name: camera_platform_interface
sha256: e971ebca970f7cfee396f76ef02070b5e441b4aa04942da9c108d725f57bbd32
url: "https://pub.dev"
source: hosted
version: "2.7.2"
camera_web:
dependency: transitive
description:
name: camera_web
sha256: f18ccfb33b2a7c49a52ad5aa3f07330b7422faaecbdfd9b9fe8e51182f6ad67d
url: "https://pub.dev"
source: hosted
version: "0.3.2+4"
characters: characters:
dependency: transitive dependency: transitive
description: description:
...@@ -49,6 +89,14 @@ packages: ...@@ -49,6 +89,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.18.0" version: "1.18.0"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
url: "https://pub.dev"
source: hosted
version: "0.3.3+8"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
...@@ -123,6 +171,14 @@ packages: ...@@ -123,6 +171,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
url: "https://pub.dev"
source: hosted
version: "2.0.17"
flutter_swipe: flutter_swipe:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -365,6 +421,14 @@ packages: ...@@ -365,6 +421,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.10.0"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
...@@ -438,5 +502,5 @@ packages: ...@@ -438,5 +502,5 @@ packages:
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
sdks: sdks:
dart: ">=3.2.0 <4.0.0" dart: ">=3.2.3 <4.0.0"
flutter: ">=3.16.0" flutter: ">=3.16.6"
...@@ -3,7 +3,7 @@ description: A random application, for testing purpose only. ...@@ -3,7 +3,7 @@ description: A random application, for testing purpose only.
publish_to: 'none' publish_to: 'none'
version: 1.0.43+44 version: 1.0.44+45
environment: environment:
sdk: '^3.0.0' sdk: '^3.0.0'
...@@ -12,6 +12,7 @@ dependencies: ...@@ -12,6 +12,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
camera: ^0.10.5+8
easy_localization: ^3.0.1 easy_localization: ^3.0.1
equatable: ^2.0.5 equatable: ^2.0.5
flutter_bloc: ^8.1.1 flutter_bloc: ^8.1.1
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment