add open camera files and add packages
This commit is contained in:
parent
660e30f937
commit
7f3b71a40b
|
|
@ -7,7 +7,7 @@ plugins {
|
|||
|
||||
android {
|
||||
namespace = "com.example.smart_vision_assist"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
compileSdk = 36
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:name="${applicationName}"
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pluginManagement {
|
|||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "8.1.0" apply false
|
||||
id "com.android.application" version "8.2.1" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:camera/camera.dart';
|
||||
|
||||
class CameraScreen extends StatefulWidget {
|
||||
@override
|
||||
_CameraScreenState createState() => _CameraScreenState();
|
||||
}
|
||||
|
||||
class _CameraScreenState extends State<CameraScreen> {
|
||||
CameraController? controller;
|
||||
List<CameraDescription>? cameras;
|
||||
|
||||
bool isRecording = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initCamera();
|
||||
}
|
||||
|
||||
Future<void> initCamera() async {
|
||||
cameras = await availableCameras();
|
||||
controller = CameraController(
|
||||
cameras![0],
|
||||
ResolutionPreset.high,
|
||||
);
|
||||
|
||||
await controller!.initialize();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
// 🔴 START RECORD
|
||||
Future<void> startRecording() async {
|
||||
if (!controller!.value.isRecordingVideo) {
|
||||
await controller!.startVideoRecording();
|
||||
setState(() {
|
||||
isRecording = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ⏹ STOP RECORD
|
||||
Future<void> stopRecording() async {
|
||||
if (controller!.value.isRecordingVideo) {
|
||||
await controller!.stopVideoRecording();
|
||||
setState(() {
|
||||
isRecording = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (controller == null || !controller!.value.isInitialized) {
|
||||
return Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
// 📷 PREVIEW KAMERA (FULL SCREEN)
|
||||
Positioned.fill(
|
||||
child: CameraPreview(controller!),
|
||||
),
|
||||
|
||||
// 🔙 TOMBOL KEMBALI (KANAN BAWAH)
|
||||
Positioned(
|
||||
bottom: 40,
|
||||
right: 30,
|
||||
child: Semantics(
|
||||
label: "Kembali",
|
||||
button: true,
|
||||
child: FloatingActionButton(
|
||||
backgroundColor: Colors.black,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Icon(Icons.arrow_back),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 🔘 TOMBOL RECORD (TENGAH BAWAH)
|
||||
Positioned(
|
||||
bottom: 30,
|
||||
left: MediaQuery.of(context).size.width / 2 - 35,
|
||||
child: Semantics(
|
||||
label: isRecording ? "Stop rekam" : "Mulai rekam",
|
||||
button: true,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (isRecording) {
|
||||
stopRecording();
|
||||
} else {
|
||||
startRecording();
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 70,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: isRecording ? Colors.red : Colors.white,
|
||||
border: Border.all(color: Colors.grey, width: 4),
|
||||
),
|
||||
child: Icon(
|
||||
isRecording ? Icons.stop : Icons.circle,
|
||||
color: isRecording ? Colors.white : Colors.red,
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import '../utils/colors.dart';
|
|||
import 'home_screen.dart';
|
||||
import 'signup_screen.dart';
|
||||
import 'forgot_password_screen.dart';
|
||||
import 'camera_screen.dart';
|
||||
|
||||
class LoginScreen extends StatelessWidget {
|
||||
final TextEditingController emailController = TextEditingController();
|
||||
|
|
@ -95,8 +96,13 @@ class LoginScreen extends StatelessWidget {
|
|||
|
||||
// 🔘 LOGIN BUTTON
|
||||
CustomButton(
|
||||
text: "Login",
|
||||
onPressed: () => handleLogin(context),
|
||||
text: "Buka Kamera",
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => CameraScreen()),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
SizedBox(height: 15),
|
||||
|
|
|
|||
|
|
@ -4,14 +4,35 @@ import '../widgets/custom_button.dart';
|
|||
import '../services/tts_service.dart';
|
||||
import '../utils/colors.dart';
|
||||
|
||||
class SignUpScreen extends StatelessWidget {
|
||||
class SignUpScreen extends StatefulWidget {
|
||||
const SignUpScreen({super.key});
|
||||
|
||||
@override
|
||||
State<SignUpScreen> createState() => _SignUpScreenState();
|
||||
}
|
||||
|
||||
class _SignUpScreenState extends State<SignUpScreen> {
|
||||
final TextEditingController emailController = TextEditingController();
|
||||
final TextEditingController passwordController = TextEditingController();
|
||||
final TextEditingController confirmPasswordController = TextEditingController();
|
||||
|
||||
final TTSService tts = TTSService();
|
||||
String? selectedGender;
|
||||
|
||||
SignUpScreen({super.key});
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
tts.speak("Halaman daftar akun");
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
emailController.dispose();
|
||||
passwordController.dispose();
|
||||
confirmPasswordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void handleSignUp(BuildContext context) {
|
||||
String email = emailController.text;
|
||||
|
|
@ -28,31 +49,63 @@ class SignUpScreen extends StatelessWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
if (selectedGender == null) {
|
||||
tts.speak("Jenis kelamin wajib dipilih");
|
||||
return;
|
||||
}
|
||||
|
||||
tts.speak("Pendaftaran berhasil");
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
Widget buildGenderOption(String gender) {
|
||||
return RadioListTile<String>(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
value: gender,
|
||||
groupValue: selectedGender,
|
||||
activeColor: AppColors.primary,
|
||||
fillColor: MaterialStateProperty.resolveWith((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return AppColors.primary;
|
||||
}
|
||||
return AppColors.text;
|
||||
}),
|
||||
title: Text(
|
||||
gender,
|
||||
style: TextStyle(color: AppColors.text),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
selectedGender = value;
|
||||
});
|
||||
|
||||
if (value != null) {
|
||||
tts.speak("Jenis kelamin $value dipilih");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
tts.speak("Halaman daftar akun");
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
appBar: AppBar(
|
||||
title: Text("Sign Up"),
|
||||
title: const Text("Sign Up"),
|
||||
),
|
||||
body: Padding(
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
CustomTextField(
|
||||
label: "Email",
|
||||
controller: emailController,
|
||||
),
|
||||
|
||||
SizedBox(height: 20),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
CustomTextField(
|
||||
label: "Password",
|
||||
|
|
@ -60,7 +113,7 @@ class SignUpScreen extends StatelessWidget {
|
|||
obscure: true,
|
||||
),
|
||||
|
||||
SizedBox(height: 20),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
CustomTextField(
|
||||
label: "Konfirmasi Password",
|
||||
|
|
@ -68,7 +121,36 @@ class SignUpScreen extends StatelessWidget {
|
|||
obscure: true,
|
||||
),
|
||||
|
||||
SizedBox(height: 30),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
Semantics(
|
||||
label: "Pilih jenis kelamin",
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: AppColors.text),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
"Jenis Kelamin",
|
||||
style: TextStyle(
|
||||
color: AppColors.text,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
buildGenderOption("Laki-laki"),
|
||||
const Divider(color: Colors.white24, height: 1),
|
||||
buildGenderOption("Perempuan"),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
|
||||
CustomButton(
|
||||
text: "Daftar",
|
||||
|
|
|
|||
82
pubspec.lock
82
pubspec.lock
|
|
@ -33,6 +33,46 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
camera:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: camera
|
||||
sha256: dfa8fc5a1adaeb95e7a54d86a5bd56f4bb0e035515354c8ac6d262e35cec2ec8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.6"
|
||||
camera_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_android
|
||||
sha256: f19ba60b61fdc7b79128191f3509455978eb945b98748fcf93e38921df502176
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.10+5"
|
||||
camera_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_avfoundation
|
||||
sha256: e4aca5bccaf897b70cac87e5fdd789393310985202442837922fd40325e2733b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.21+1"
|
||||
camera_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_platform_interface
|
||||
sha256: "2f757024a48696ff4814a789b0bd90f5660c0fb25f393ab4564fb483327930e2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.10.0"
|
||||
camera_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_web
|
||||
sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.5"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -73,6 +113,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.0"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.4+2"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -110,6 +158,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: "6382ce712ff69b0f719640ce957559dde459e55ecd433c767e06d139ddf16cab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.29"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
|
@ -216,6 +272,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
posix:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -253,6 +317,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_transform
|
||||
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -293,6 +365,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.3.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -311,4 +391,4 @@ packages:
|
|||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.6.1 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
flutter: ">=3.27.0"
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ environment:
|
|||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
|
||||
camera: ^0.10.5+5
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_tts: ^3.5.2
|
||||
|
|
|
|||
Loading…
Reference in New Issue