25-9-1-1
This commit is contained in:
@@ -28,13 +28,13 @@ class AboutPage extends StatelessWidget {
|
||||
|
||||
// Company Name and Introduction
|
||||
Text(
|
||||
'Tripz LLC',
|
||||
'Intaleq LLC',
|
||||
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Egypt\'s pioneering ride-sharing service, proudly developed by Arabian and local owners. We prioritize being near you – both our valued passengers and our dedicated captains.'
|
||||
"Syria's pioneering ride-sharing service, proudly developed by Arabian and local owners. We prioritize being near you – both our valued passengers and our dedicated captains."
|
||||
.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
textAlign: TextAlign.center,
|
||||
@@ -43,7 +43,7 @@ class AboutPage extends StatelessWidget {
|
||||
|
||||
// Key Features Section
|
||||
Text(
|
||||
'Why Choose Tripz?'.tr,
|
||||
'Why Choose Intaleq?'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
||||
@@ -432,8 +432,7 @@ class ShareAppPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
controller.formatPhoneNumber(
|
||||
contact['phones'][0].toString()),
|
||||
(contact['phones'][0].toString()),
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.secondaryLabel,
|
||||
fontSize: 15,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'package:Intaleq/views/widgets/my_scafold.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
@@ -8,205 +6,258 @@ import 'package:share_plus/share_plus.dart';
|
||||
import '../../../controller/functions/audio_record1.dart';
|
||||
|
||||
class TripsRecordedPage extends StatelessWidget {
|
||||
const TripsRecordedPage({
|
||||
super.key,
|
||||
});
|
||||
const TripsRecordedPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(
|
||||
title: 'Trips recorded'.tr,
|
||||
body: [
|
||||
GetBuilder<AudioRecorderController>(builder: (audio) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
FutureBuilder<List<String>>(
|
||||
future: audio.getRecordedFiles(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(
|
||||
child: CupertinoActivityIndicator());
|
||||
} else if (snapshot.hasData) {
|
||||
final recordedFiles = snapshot.data!;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () async {
|
||||
String? selectedFile =
|
||||
await showCupertinoModalPopup<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CupertinoActionSheet(
|
||||
title: Text('Select a File'.tr),
|
||||
actions: recordedFiles
|
||||
.map(
|
||||
(file) => CupertinoActionSheetAction(
|
||||
child: Text(path.basename(file)),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(file);
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
if (selectedFile != null) {
|
||||
audio.selectedFilePath = selectedFile;
|
||||
audio.playRecordedFile(selectedFile);
|
||||
audio.update();
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
audio.selectedFilePath != null
|
||||
? path.basename(audio.selectedFilePath!)
|
||||
: 'Select a File'.tr,
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.actionTextStyle
|
||||
.copyWith(color: CupertinoColors.activeBlue),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text('Error: ${snapshot.error}'),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
// Ensure the controller is available.
|
||||
// If it's not initialized elsewhere, you can use Get.put() or Get.lazyPut() here.
|
||||
// Get.lazyPut(() => AudioRecorderController());
|
||||
|
||||
// Cupertino-style slider for seeking audio
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: CupertinoSlider(
|
||||
value: audio.totalDuration > 0
|
||||
? audio.currentPosition / audio.totalDuration
|
||||
: 0.0, // Normalize to a value between 0.0 and 1.0
|
||||
min: 0.0,
|
||||
max: 1.0, // Maximum value is now 1.0
|
||||
activeColor: CupertinoColors.activeBlue,
|
||||
onChanged: (value) {
|
||||
final newPosition = value * audio.totalDuration;
|
||||
audio.currentPosition = newPosition;
|
||||
audio.audioPlayer
|
||||
.seek(Duration(seconds: newPosition.toInt()));
|
||||
audio.update();
|
||||
},
|
||||
),
|
||||
),
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Trips recorded'.tr),
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 1,
|
||||
actions: [
|
||||
GetBuilder<AudioRecorderController>(
|
||||
builder: (controller) => IconButton(
|
||||
tooltip: 'Delete All'.tr,
|
||||
icon: const Icon(Icons.delete_sweep_outlined),
|
||||
onPressed: () {
|
||||
_showDeleteConfirmation(context, controller, isDeleteAll: true);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: GetBuilder<AudioRecorderController>(
|
||||
builder: (controller) {
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildRecordingsList(controller),
|
||||
),
|
||||
// Show player controls only when a file is selected
|
||||
if (controller.selectedFilePath != null)
|
||||
_buildPlayerControls(context, controller),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// iOS-style playback controls
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16.0, horizontal: 16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Icon(
|
||||
audio.isPlaying
|
||||
? CupertinoIcons.pause
|
||||
: CupertinoIcons.play_arrow,
|
||||
color: CupertinoColors.activeBlue,
|
||||
),
|
||||
onPressed: () {
|
||||
if (audio.isPlaying) {
|
||||
audio.pausePlayback();
|
||||
} else {
|
||||
audio.resumePlayback();
|
||||
}
|
||||
audio.update();
|
||||
},
|
||||
),
|
||||
CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
child: const Icon(CupertinoIcons.stop,
|
||||
color: CupertinoColors.destructiveRed),
|
||||
onPressed: () {
|
||||
audio.stopPlayback();
|
||||
audio.update();
|
||||
},
|
||||
),
|
||||
CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
child: const Icon(CupertinoIcons.delete,
|
||||
color: CupertinoColors.destructiveRed),
|
||||
onPressed: () async {
|
||||
showCupertinoModalPopup(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CupertinoActionSheet(
|
||||
title: Text('Are you sure?'.tr),
|
||||
message: Text(
|
||||
'This will delete all recorded files from your device.'
|
||||
.tr,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
actions: [
|
||||
CupertinoActionSheetAction(
|
||||
isDestructiveAction: true,
|
||||
onPressed: () async {
|
||||
await audio.deleteAllRecordedFiles();
|
||||
Navigator.pop(context);
|
||||
audio.update();
|
||||
},
|
||||
child: Text('Delete'.tr),
|
||||
),
|
||||
],
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text('Cancel'.tr),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
/// Builds the list of recorded audio files.
|
||||
Widget _buildRecordingsList(AudioRecorderController controller) {
|
||||
return FutureBuilder<List<String>>(
|
||||
future: controller.getRecordedFiles(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
if (snapshot.hasError) {
|
||||
return Center(child: Text('Error: ${snapshot.error}'.tr));
|
||||
}
|
||||
if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.mic_off_outlined, size: 80, color: Colors.grey[400]),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'No Recordings Found'.tr,
|
||||
style: TextStyle(fontSize: 18, color: Colors.grey[600]),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
||||
child: Text(
|
||||
'Record your trips to see them here.'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.grey[500]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// File selection and sharing
|
||||
if (audio.selectedFilePath != null)
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
color: CupertinoColors.systemGrey6,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Selected file: ${path.basename(audio.selectedFilePath!)}',
|
||||
style: CupertinoTheme.of(context)
|
||||
.textTheme
|
||||
.textStyle,
|
||||
),
|
||||
CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
child: const Icon(CupertinoIcons.share),
|
||||
onPressed: () {
|
||||
Share.shareXFiles(
|
||||
[XFile(audio.selectedFilePath!)]);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
final recordedFiles = snapshot.data!;
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 8),
|
||||
itemCount: recordedFiles.length,
|
||||
itemBuilder: (context, index) {
|
||||
final file = recordedFiles[index];
|
||||
final fileName = path.basename(file);
|
||||
final isSelected = controller.selectedFilePath == file;
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||
elevation: isSelected ? 4 : 1,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
isSelected && controller.isPlaying
|
||||
? Icons.pause_circle_filled
|
||||
: Icons.play_circle_fill,
|
||||
color:
|
||||
isSelected ? Theme.of(context).primaryColor : Colors.grey,
|
||||
size: 40,
|
||||
),
|
||||
title: Text(fileName,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
subtitle: Text("Audio Recording".tr),
|
||||
onTap: () {
|
||||
if (isSelected) {
|
||||
controller.isPlaying
|
||||
? controller.pausePlayback()
|
||||
: controller.resumePlayback();
|
||||
} else {
|
||||
controller.playRecordedFile(file);
|
||||
}
|
||||
},
|
||||
selected: isSelected,
|
||||
selectedTileColor:
|
||||
Theme.of(context).primaryColor.withOpacity(0.1),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
);
|
||||
})
|
||||
],
|
||||
isleading: true);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Builds the player UI at the bottom of the screen.
|
||||
Widget _buildPlayerControls(
|
||||
BuildContext context, AudioRecorderController controller) {
|
||||
final fileName = path.basename(controller.selectedFilePath!);
|
||||
final positionText = Duration(seconds: controller.currentPosition.toInt())
|
||||
.toString()
|
||||
.split('.')
|
||||
.first
|
||||
.padLeft(8, '0')
|
||||
.substring(3);
|
||||
final durationText = Duration(seconds: controller.totalDuration.toInt())
|
||||
.toString()
|
||||
.split('.')
|
||||
.first
|
||||
.padLeft(8, '0')
|
||||
.substring(3);
|
||||
|
||||
return Material(
|
||||
elevation: 10,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(fileName,
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
||||
textAlign: TextAlign.center),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Text(positionText),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: (controller.totalDuration > 0)
|
||||
? controller.currentPosition / controller.totalDuration
|
||||
: 0.0,
|
||||
onChanged: (value) {
|
||||
final newPosition = value * controller.totalDuration;
|
||||
controller.audioPlayer
|
||||
.seek(Duration(seconds: newPosition.toInt()));
|
||||
},
|
||||
),
|
||||
),
|
||||
Text(durationText),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.share_outlined),
|
||||
tooltip: 'Share'.tr,
|
||||
onPressed: () {
|
||||
Share.shareXFiles([XFile(controller.selectedFilePath!)]);
|
||||
},
|
||||
iconSize: 28,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(controller.isPlaying
|
||||
? Icons.pause_circle_filled
|
||||
: Icons.play_circle_filled),
|
||||
onPressed: () {
|
||||
controller.isPlaying
|
||||
? controller.pausePlayback()
|
||||
: controller.resumePlayback();
|
||||
},
|
||||
iconSize: 56,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
IconButton(
|
||||
icon:
|
||||
const Icon(Icons.delete_outline, color: Colors.redAccent),
|
||||
tooltip: 'Delete'.tr,
|
||||
onPressed: () {
|
||||
_showDeleteConfirmation(context, controller,
|
||||
isDeleteAll: false);
|
||||
},
|
||||
iconSize: 28,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Shows a confirmation dialog for deleting one or all files.
|
||||
void _showDeleteConfirmation(
|
||||
BuildContext context,
|
||||
AudioRecorderController controller, {
|
||||
required bool isDeleteAll,
|
||||
}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(isDeleteAll
|
||||
? 'Delete All Recordings?'.tr
|
||||
: 'Delete Recording?'.tr),
|
||||
content: Text(isDeleteAll
|
||||
? 'This action cannot be undone.'.tr
|
||||
: 'Are you sure you want to delete this file?'.tr),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('Cancel'.tr),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
TextButton(
|
||||
child:
|
||||
Text('Delete'.tr, style: const TextStyle(color: Colors.red)),
|
||||
onPressed: () async {
|
||||
if (isDeleteAll) {
|
||||
await controller.deleteAllRecordedFiles();
|
||||
} else {
|
||||
// NOTE: You may need to add this method to your controller
|
||||
// if it doesn't exist.
|
||||
// await controller.deleteFile(controller.selectedFilePath!);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user