feat: Add Voicemail feature with playback functionality
All checks were successful
/ mirror (push) Successful in 4s

This commit is contained in:
AlexisDanlos 2025-01-15 17:34:54 +01:00
parent 8b1eb1d709
commit 9177b5a42b
3 changed files with 206 additions and 2 deletions

View File

@ -5,6 +5,7 @@ import 'package:dialer/features/history/history_page.dart';
import 'package:dialer/features/composition/composition.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:dialer/features/settings/settings.dart';
import 'package:dialer/features/voicemail/voicemail_page.dart';
import '../../widgets/contact_service.dart';
class _MyHomePageState extends State<MyHomePage>
@ -18,8 +19,8 @@ class _MyHomePageState extends State<MyHomePage>
@override
void initState() {
super.initState();
// Set the TabController length to 3
_tabController = TabController(length: 3, vsync: this, initialIndex: 1);
// Set the TabController length to 4
_tabController = TabController(length: 4, vsync: this, initialIndex: 1);
_tabController.addListener(_handleTabIndex);
_fetchContacts();
}
@ -173,6 +174,7 @@ class _MyHomePageState extends State<MyHomePage>
FavoritesPage(),
HistoryPage(),
ContactPage(),
VoicemailPage(),
],
),
Positioned(
@ -216,6 +218,11 @@ class _MyHomePageState extends State<MyHomePage>
icon: Icon(_tabController.index == 2
? Icons.contacts
: Icons.contacts_outlined)),
Tab(
icon: Icon(_tabController.index == 3
? Icons.voicemail
: Icons.voicemail_outlined),
),
],
labelColor: Colors.white,
unselectedLabelColor: const Color.fromARGB(255, 158, 158, 158),

View File

@ -0,0 +1,196 @@
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
// ...existing code...
class VoicemailPage extends StatefulWidget {
const VoicemailPage({Key? key}) : super(key: key);
@override
State<VoicemailPage> createState() => _VoicemailPageState();
}
class _VoicemailPageState extends State<VoicemailPage> {
bool _expanded = false;
bool _isPlaying = false;
Duration _duration = Duration.zero;
Duration _position = Duration.zero;
late AudioPlayer _audioPlayer;
@override
void initState() {
super.initState();
_audioPlayer = AudioPlayer();
_audioPlayer.onDurationChanged.listen((Duration d) {
setState(() => _duration = d);
});
_audioPlayer.onPositionChanged.listen((Duration p) {
setState(() => _position = p);
});
_audioPlayer.onPlayerComplete.listen((event) {
setState(() {
_isPlaying = false;
_position = Duration.zero;
});
});
}
Future<void> _togglePlayPause() async {
if (_isPlaying) {
await _audioPlayer.pause();
} else {
await _audioPlayer.play(UrlSource('voicemail.mp3'));
}
setState(() => _isPlaying = !_isPlaying);
}
@override
void dispose() {
_audioPlayer.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
// appBar: AppBar(
// // title: const Text('Voicemail'),
// backgroundColor: Colors.black,
// ),
body: ListView(
children: [
GestureDetector(
onTap: () {
setState(() {
_expanded = !_expanded;
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color.fromARGB(255, 30, 30, 30),
borderRadius: BorderRadius.circular(12.0),
),
padding: const EdgeInsets.all(16),
child: _expanded
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const CircleAvatar(
backgroundImage: AssetImage(
'assets/default_contact_picture.png'),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'John Doe',
style: TextStyle(color: Colors.white),
),
Text(
'Wed 3:00 PM - 1:20 min',
style: TextStyle(color: Colors.grey),
),
],
),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(
_isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
),
onPressed: _togglePlayPause,
),
SizedBox(
width: 200,
child: Slider(
min: 0,
max: _duration.inSeconds.toDouble(),
value: _position.inSeconds.toDouble(),
onChanged: (value) async {
final newPos = Duration(seconds: value.toInt());
await _audioPlayer.seek(newPos);
},
activeColor: Colors.blue,
inactiveColor: Colors.grey,
),
),
],
),
const SizedBox(height: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: const [
Icon(Icons.call, color: Colors.green),
SizedBox(width: 8),
Text('Call', style: TextStyle(color: Colors.white)),
],
),
const SizedBox(height: 12),
Row(
children: const [
Icon(Icons.message, color: Colors.blue),
SizedBox(width: 8),
Text('Text', style: TextStyle(color: Colors.white)),
],
),
const SizedBox(height: 12),
Row(
children: const [
Icon(Icons.block, color: Colors.red),
SizedBox(width: 8),
Text('Block', style: TextStyle(color: Colors.white)),
],
),
const SizedBox(height: 12),
Row(
children: const [
Icon(Icons.share, color: Colors.white),
SizedBox(width: 8),
Text('Share', style: TextStyle(color: Colors.white)),
],
),
],
),
],
)
: Row(
children: [
const CircleAvatar(
backgroundImage:
AssetImage('assets/default_contact_picture.png'),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'John Doe',
style: TextStyle(color: Colors.white),
),
Text(
'Wed 3:00 PM - 1:20 min',
style: TextStyle(color: Colors.grey),
),
],
),
],
),
),
),
],
),
);
}
}

View File

@ -50,6 +50,7 @@ dependencies:
intl_utils: ^2.0.7
url_launcher: ^6.3.1
flutter_secure_storage: ^9.0.0
audioplayers: ^6.1.0
mobile_number:
path: packages/mobile_number