Feat: Calling page, can call and message in composition page

This commit is contained in:
Florian Griffon 2024-11-29 01:12:12 +01:00
parent 4359057c1d
commit 17b2301d16
6 changed files with 173 additions and 16 deletions

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
<application <application
android:label="com.example.dialer" android:label="com.example.dialer"
android:name="${applicationName}" android:name="${applicationName}"

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.

View File

@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'dart:async';
class CallPage extends StatefulWidget {
final String phoneNumber;
const CallPage({Key? key, required this.phoneNumber}) : super(key: key);
@override
_CallPageState createState() => _CallPageState();
}
class _CallPageState extends State<CallPage> {
bool isCalling = true;
int callDuration = 0;
late final Timer _timer;
@override
void initState() {
super.initState();
_startCallTimer();
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
void _startCallTimer() {
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
if (isCalling) {
setState(() {
callDuration++;
});
}
});
}
void _endCall() {
setState(() {
isCalling = false;
});
Navigator.pop(context);
}
String _formatDuration(int seconds) {
final minutes = seconds ~/ 60;
final remainingSeconds = seconds % 60;
return '${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Center( // Center widget to center all the content vertically and horizontally
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, // Ensure the text and button are centered horizontally
children: [
Text(
"Calling ${widget.phoneNumber}",
style: TextStyle(color: Colors.white, fontSize: 24),
),
const SizedBox(height: 20),
Text(
isCalling ? _formatDuration(callDuration) : "Call Ended",
style: TextStyle(color: Colors.greenAccent, fontSize: 20),
),
const SizedBox(height: 40),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
shape: CircleBorder(),
padding: const EdgeInsets.all(20),
),
onPressed: _endCall,
child: Icon(Icons.call_end, color: Colors.white),
),
],
),
),
);
}
}

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:url_launcher/url_launcher.dart';
import '../call/call.dart';
class CompositionPage extends StatefulWidget { class CompositionPage extends StatefulWidget {
const CompositionPage({super.key}); const CompositionPage({super.key});
@ -40,6 +42,35 @@ class _CompositionPageState extends State<CompositionPage> {
}); });
} }
void _onCallPress(String phoneNumber) async {
final uri = Uri(scheme: 'tel', path: phoneNumber);
if (await canLaunchUrl(uri)) {
// Launch the system dialer in the background
launchUrl(uri);
// Navigate to your custom call page
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CallPage(phoneNumber: phoneNumber),
),
);
} else {
print('Could not launch $uri');
}
}
// Function to send an SMS
void _onTextPress(String phoneNumber) async {
final uri = Uri(scheme: 'sms', path: phoneNumber);
if (await canLaunchUrl(uri)) {
// Launch the SMS app with the given phone number
launchUrl(uri);
} else {
print('Could not launch SMS to $phoneNumber');
}
}
void _onNumberPress(String number) { void _onNumberPress(String number) {
setState(() { setState(() {
dialedNumber += number; dialedNumber += number;
@ -79,9 +110,9 @@ class _CompositionPageState extends State<CompositionPage> {
// Top half: Display contacts matching dialed number // Top half: Display contacts matching dialed number
Expanded( Expanded(
flex: 2, flex: 2,
child: child: Container(
Container( padding: const EdgeInsets.only(
padding: const EdgeInsets.only(top: 42.0, left: 16.0, right: 16.0, bottom: 16.0), top: 42.0, left: 16.0, right: 16.0, bottom: 16.0),
color: Colors.black, color: Colors.black,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -93,12 +124,14 @@ class _CompositionPageState extends State<CompositionPage> {
return ListTile( return ListTile(
title: Text( title: Text(
contact.displayName, contact.displayName,
style: const TextStyle(color: Colors.white), style:
const TextStyle(color: Colors.white),
), ),
subtitle: contact.phones.isNotEmpty subtitle: contact.phones.isNotEmpty
? Text( ? Text(
contact.phones.first.number, contact.phones.first.number,
style: const TextStyle(color: Colors.grey), style: const TextStyle(
color: Colors.grey),
) )
: null, : null,
trailing: Row( trailing: Row(
@ -106,16 +139,39 @@ class _CompositionPageState extends State<CompositionPage> {
children: [ children: [
// Call button // Call button
IconButton( IconButton(
icon: Icon(Icons.phone, color: Colors.green[300], size: 20), icon: Icon(Icons.phone,
color: Colors.green[300],
size: 20),
onPressed: () { onPressed: () {
print('Calling ${contact.displayName}'); final phoneNumber = contact
.phones.isNotEmpty
? contact.phones.first.number
: null;
if (phoneNumber != null) {
_onCallPress(phoneNumber);
} else {
print(
'${contact.displayName} has no phone number.');
}
}, },
), ),
// Text button // Text button
IconButton( IconButton(
icon: Icon(Icons.message, color: Colors.blue[300], size: 20), icon: Icon(Icons.message,
color: Colors.blue[300],
size: 20),
onPressed: () { onPressed: () {
print('Texting ${contact.displayName}'); final phoneNumber = contact
.phones.isNotEmpty
? contact.phones.first.number
: null;
if (phoneNumber != null) {
_onTextPress(phoneNumber); // Text functionality
} else {
print(
'${contact.displayName} has no phone number.');
}
}, },
), ),
], ],
@ -125,7 +181,12 @@ class _CompositionPageState extends State<CompositionPage> {
}, },
); );
}).toList() }).toList()
: [Center(child: Text('No contacts found', style: TextStyle(color: Colors.white)))], : [
Center(
child: Text('No contacts found',
style:
TextStyle(color: Colors.white)))
],
), ),
), ),
], ],
@ -150,14 +211,16 @@ class _CompositionPageState extends State<CompositionPage> {
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
dialedNumber, dialedNumber,
style: const TextStyle(fontSize: 24, color: Colors.white), style: const TextStyle(
fontSize: 24, color: Colors.white),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
), ),
IconButton( IconButton(
onPressed: _onClearPress, onPressed: _onClearPress,
icon: const Icon(Icons.backspace, color: Colors.white), icon: const Icon(Icons.backspace,
color: Colors.white),
), ),
], ],
), ),
@ -170,7 +233,8 @@ class _CompositionPageState extends State<CompositionPage> {
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildDialButton('1'), _buildDialButton('1'),
_buildDialButton('2'), _buildDialButton('2'),
@ -178,7 +242,8 @@ class _CompositionPageState extends State<CompositionPage> {
], ],
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildDialButton('4'), _buildDialButton('4'),
_buildDialButton('5'), _buildDialButton('5'),
@ -186,7 +251,8 @@ class _CompositionPageState extends State<CompositionPage> {
], ],
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildDialButton('7'), _buildDialButton('7'),
_buildDialButton('8'), _buildDialButton('8'),
@ -194,7 +260,8 @@ class _CompositionPageState extends State<CompositionPage> {
], ],
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildDialButton('*'), _buildDialButton('*'),
_buildDialButton('0'), _buildDialButton('0'),

View File

@ -38,6 +38,7 @@ dependencies:
flutter_contacts: ^1.1.9+2 flutter_contacts: ^1.1.9+2
permission_handler: ^10.2.0 # For handling permissions permission_handler: ^10.2.0 # For handling permissions
cached_network_image: ^3.2.3 # For caching contact images cached_network_image: ^3.2.3 # For caching contact images
url_launcher: ^6.1.8 # For calling
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: