From f0426b0246d90ec581698c89539b527809bd1218 Mon Sep 17 00:00:00 2001
From: Florian Griffon <florian.griffon@epitech.eu>
Date: Thu, 31 Oct 2024 16:15:00 +0100
Subject: [PATCH 1/3] feat: searchbar permanent

---
 dialer/lib/features/home/home_page.dart | 68 +++++++++++++++++--------
 1 file changed, 46 insertions(+), 22 deletions(-)

diff --git a/dialer/lib/features/home/home_page.dart b/dialer/lib/features/home/home_page.dart
index 26d2f7f..09ded0c 100644
--- a/dialer/lib/features/home/home_page.dart
+++ b/dialer/lib/features/home/home_page.dart
@@ -3,17 +3,43 @@ import 'package:dialer/features/contacts/contact_page.dart';
 import 'package:dialer/features/favorites/favorites_page.dart';
 import 'package:dialer/features/history/history_page.dart';
 import 'package:dialer/features/composition/composition.dart';
+import 'package:flutter_contacts/flutter_contacts.dart';
 
 class _MyHomePageState extends State<MyHomePage>
     with SingleTickerProviderStateMixin {
   late TabController _tabController;
-  final SearchController _searchController = SearchController();
+  List<Contact> _allContacts = [];
+  List<Contact> _contactSuggestions = [];
 
   @override
   void initState() {
     super.initState();
     _tabController = TabController(length: 3, vsync: this, initialIndex: 1);
     _tabController.addListener(_handleTabIndex);
+    _fetchContacts();
+  }
+
+  void _fetchContacts() async {
+    if (await FlutterContacts.requestPermission()) {
+      _allContacts = await FlutterContacts.getContacts(withProperties: true);
+      setState(() {});
+    }
+  }
+
+  void _onSearchChanged(String query) {
+    print("Search query: $query");
+
+    setState(() {
+      if (query.isEmpty) {
+        _contactSuggestions = List.from(_allContacts);
+      } else {
+        _contactSuggestions = _allContacts.where((contact) {
+          return contact.displayName
+              .toLowerCase()
+              .contains(query.toLowerCase());
+        }).toList();
+      }
+    });
   }
 
   @override
@@ -27,10 +53,6 @@ class _MyHomePageState extends State<MyHomePage>
     setState(() {});
   }
 
-  void _onSearchChanged(String query) {
-    // Use this method to manage search actions if needed
-  }
-
   @override
   Widget build(BuildContext context) {
     return Scaffold(
@@ -40,15 +62,14 @@ class _MyHomePageState extends State<MyHomePage>
           // Persistent Search Bar
           Padding(
             padding: const EdgeInsets.only(
-              top: 24.0, // Outside top padding
-              bottom: 10.0, // Outside bottom padding
-              left: 16.0, // Outside left padding
-              right: 16.0, // Outside right padding
+              top: 24.0,
+              bottom: 10.0,
+              left: 16.0,
+              right: 16.0,
             ),
             child: Container(
               decoration: BoxDecoration(
-                color: const Color.fromARGB(
-                    255, 30, 30, 30), // Background of the SearchBar
+                color: const Color.fromARGB(255, 30, 30, 30),
                 borderRadius: BorderRadius.circular(12.0),
                 border: Border(
                   top: BorderSide(color: Colors.grey.shade800, width: 1),
@@ -63,15 +84,15 @@ class _MyHomePageState extends State<MyHomePage>
                     controller: controller,
                     padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
                       EdgeInsets.only(
-                        top: 10.0, // Inside top padding
-                        bottom: 10.0, // Inside bottom padding
-                        left: 16.0, // Inside left padding
-                        right: 16.0, // Inside right padding
+                        top: 6.0,
+                        bottom: 6.0,
+                        left: 16.0,
+                        right: 16.0,
                       ),
                     ),
-                    onChanged: _onSearchChanged,
                     onTap: () {
                       controller.openView();
+                      _onSearchChanged('');
                     },
                     backgroundColor: MaterialStateProperty.all(
                         const Color.fromARGB(255, 30, 30, 30)),
@@ -91,18 +112,21 @@ class _MyHomePageState extends State<MyHomePage>
                     ),
                   );
                 },
+                viewOnChanged: (query) {
+                  _onSearchChanged(query);
+                },
                 suggestionsBuilder:
                     (BuildContext context, SearchController controller) {
-                  return List<ListTile>.generate(5, (int index) {
-                    final String item = 'Suggestion $index';
+                  return _contactSuggestions.map((contact) {
                     return ListTile(
-                      title: Text(item),
+                      key: ValueKey(contact.id),
+                      title: Text(contact.displayName,
+                          style: const TextStyle(color: Colors.white)),
                       onTap: () {
-                        // Close the search view and select suggestion
-                        controller.closeView(item);
+                        controller.closeView(contact.displayName);
                       },
                     );
-                  });
+                  }).toList();
                 },
               ),
             ),

From c0ec11098d249ced37bcca907604530839eac3f4 Mon Sep 17 00:00:00 2001
From: Florian Griffon <florian.griffon@epitech.eu>
Date: Thu, 31 Oct 2024 18:54:38 +0100
Subject: [PATCH 2/3] feat: Page de composition - dialpad fini

---
 .../lib/features/composition/composition.dart | 181 +++++++++++++-----
 1 file changed, 136 insertions(+), 45 deletions(-)

diff --git a/dialer/lib/features/composition/composition.dart b/dialer/lib/features/composition/composition.dart
index 774cc49..3fb4743 100644
--- a/dialer/lib/features/composition/composition.dart
+++ b/dialer/lib/features/composition/composition.dart
@@ -9,7 +9,18 @@ class CompositionPage extends StatefulWidget {
 
 class _CompositionPageState extends State<CompositionPage> {
   String dialedNumber = "";
-
+  final List<String> contacts = [
+    'Alice Johnson',
+    'Bob Smith',
+    'Carol White',
+    'David Brown',
+    'Eve Black',
+    'Frank Grey',
+    'Grace Green',
+    'Heidi Gold',
+    'Ivan Silver',
+    'Judy Blue'
+  ];
 
   void _onNumberPress(String number) {
     setState(() {
@@ -17,7 +28,6 @@ class _CompositionPageState extends State<CompositionPage> {
     });
   }
 
-
   void _onDeletePress() {
     setState(() {
       if (dialedNumber.isNotEmpty) {
@@ -26,61 +36,142 @@ class _CompositionPageState extends State<CompositionPage> {
     });
   }
 
+  void _onClearPress() {
+    setState(() {
+      dialedNumber = "";
+    });
+  }
+
+  List<String> _getFilteredContacts() {
+    return contacts
+        .where((contact) =>
+            contact.toLowerCase().contains(dialedNumber.toLowerCase()))
+        .toList();
+  }
+
   @override
   Widget build(BuildContext context) {
     return Scaffold(
       backgroundColor: Colors.black,
       body: Column(
         children: [
+          // Top half: Display contacts matching dialed number
           Expanded(
-            flex: 1,
-            child: Center(
-              child: Text(
-                dialedNumber,
-                style: const TextStyle(
-                  fontSize: 32,
-                  color: Colors.white,
-                  letterSpacing: 2.0,
-                ),
-              ),
-            ),
-          ),
-          Expanded(
-            flex: 3,
-            child: Padding(
+            flex: 2,
+            child: Container(
               padding: const EdgeInsets.all(16.0),
-              child: GridView.builder(
-                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
-                  crossAxisCount: 3,
-                  mainAxisSpacing: 10,
-                  crossAxisSpacing: 10,
-                  childAspectRatio: 1,
-                ),
-                itemCount: 12,
-                itemBuilder: (context, index) {
-                  if (index < 9) {
-                  
-                    return _buildDialButton((index + 1).toString());
-                  } else if (index == 9) {
-                  
-                    return const SizedBox.shrink();
-                  } else if (index == 10) {
-                  
-                    return _buildDialButton('0');
-                  } else {
-                  
-                    return _buildDeleteButton();
-                  }
-                },
+              color: Colors.black,
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  Expanded(
+                    child: ListView(
+                      children: _getFilteredContacts().map((contact) {
+                        return ListTile(
+                          title: Text(contact,
+                              style: const TextStyle(color: Colors.white)),
+                          onTap: () {
+                            // Handle contact selection if needed
+                          },
+                        );
+                      }).toList(),
+                    ),
+                  ),
+                ],
               ),
             ),
           ),
+
+          // Bottom half: Dialpad and Dialed number display with erase button
+          Expanded(
+            flex: 2,
+            child: Container(
+              padding: const EdgeInsets.symmetric(horizontal: 16.0),
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.start,
+                children: [
+                  // Display dialed number with erase button
+                  Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: [
+                      Expanded(
+                        child: Align(
+                          alignment: Alignment
+                              .center, // Aligns text to the center of the screen
+                          child: Text(
+                            dialedNumber,
+                            style: const TextStyle(
+                                fontSize: 24, color: Colors.white),
+                            overflow: TextOverflow.ellipsis,
+                          ),
+                        ),
+                      ),
+                      IconButton(
+                        onPressed: _onClearPress,
+                        icon: const Icon(Icons.backspace, color: Colors.white),
+                      ),
+                    ],
+                  ),
+                  const SizedBox(height: 10),
+
+                  // Wrapping the dialpad in a SingleChildScrollView to prevent overflow
+                  Expanded(
+                    child: SingleChildScrollView(
+                      child: Column(
+                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                        children: [
+                          // First Row (1, 2, 3)
+                          Row(
+                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                            children: [
+                              _buildDialButton('1'),
+                              _buildDialButton('2'),
+                              _buildDialButton('3'),
+                            ],
+                          ),
+                          // Second Row (4, 5, 6)
+                          Row(
+                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                            children: [
+                              _buildDialButton('4'),
+                              _buildDialButton('5'),
+                              _buildDialButton('6'),
+                            ],
+                          ),
+                          // Third Row (7, 8, 9)
+                          Row(
+                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                            children: [
+                              _buildDialButton('7'),
+                              _buildDialButton('8'),
+                              _buildDialButton('9'),
+                            ],
+                          ),
+                          // Fourth Row (*, 0, #)
+                          Row(
+                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                            children: [
+                              _buildDialButton('*'),
+                              _buildDialButton('0'),
+                              _buildDialButton('#'),
+                            ],
+                          ),
+                        ],
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          ),
+
+          // Bottom action: Call button with padding
           Padding(
             padding: const EdgeInsets.only(bottom: 20.0),
             child: FloatingActionButton(
               backgroundColor: Colors.green,
               onPressed: () {
-              
+                // Handle call action
               },
               child: const Icon(Icons.phone, color: Colors.white),
             ),
@@ -90,14 +181,14 @@ class _CompositionPageState extends State<CompositionPage> {
     );
   }
 
-
   Widget _buildDialButton(String number) {
     return ElevatedButton(
       onPressed: () => _onNumberPress(number),
       style: ElevatedButton.styleFrom(
         backgroundColor: Colors.black,
         shape: const CircleBorder(),
-        padding: const EdgeInsets.all(20),
+        padding:
+            const EdgeInsets.all(16), // Adjusted padding to prevent overflow
       ),
       child: Text(
         number,
@@ -109,14 +200,14 @@ class _CompositionPageState extends State<CompositionPage> {
     );
   }
 
-
   Widget _buildDeleteButton() {
     return ElevatedButton(
       onPressed: _onDeletePress,
       style: ElevatedButton.styleFrom(
         backgroundColor: Colors.black,
         shape: const CircleBorder(),
-        padding: const EdgeInsets.all(20),
+        padding:
+            const EdgeInsets.all(16), // Adjusted padding to prevent overflow
       ),
       child: const Icon(
         Icons.backspace,

From 64595a1755f90a487b0f2b05789365789dacfa31 Mon Sep 17 00:00:00 2001
From: Florian Griffon <florian.griffon@epitech.eu>
Date: Tue, 5 Nov 2024 23:39:48 +0100
Subject: [PATCH 3/3] feat: composition page finished, can search

---
 .../lib/features/composition/composition.dart | 322 ++++++++++--------
 1 file changed, 181 insertions(+), 141 deletions(-)

diff --git a/dialer/lib/features/composition/composition.dart b/dialer/lib/features/composition/composition.dart
index 3fb4743..bdaf048 100644
--- a/dialer/lib/features/composition/composition.dart
+++ b/dialer/lib/features/composition/composition.dart
@@ -1,4 +1,5 @@
 import 'package:flutter/material.dart';
+import 'package:flutter_contacts/flutter_contacts.dart';
 
 class CompositionPage extends StatefulWidget {
   const CompositionPage({super.key});
@@ -9,22 +10,40 @@ class CompositionPage extends StatefulWidget {
 
 class _CompositionPageState extends State<CompositionPage> {
   String dialedNumber = "";
-  final List<String> contacts = [
-    'Alice Johnson',
-    'Bob Smith',
-    'Carol White',
-    'David Brown',
-    'Eve Black',
-    'Frank Grey',
-    'Grace Green',
-    'Heidi Gold',
-    'Ivan Silver',
-    'Judy Blue'
-  ];
+  List<Contact> _allContacts = [];
+  List<Contact> _filteredContacts = [];
+
+  @override
+  void initState() {
+    super.initState();
+    _fetchContacts();
+  }
+
+  Future<void> _fetchContacts() async {
+    if (await FlutterContacts.requestPermission()) {
+      _allContacts = await FlutterContacts.getContacts(withProperties: true);
+      _filteredContacts = _allContacts;
+      setState(() {});
+    }
+  }
+
+  void _filterContacts() {
+    setState(() {
+      _filteredContacts = _allContacts.where((contact) {
+        final phoneMatch = contact.phones.any((phone) =>
+            phone.number.replaceAll(RegExp(r'\D'), '').contains(dialedNumber));
+        final nameMatch = contact.displayName
+            .toLowerCase()
+            .contains(dialedNumber.toLowerCase());
+        return phoneMatch || nameMatch;
+      }).toList();
+    });
+  }
 
   void _onNumberPress(String number) {
     setState(() {
       dialedNumber += number;
+      _filterContacts();
     });
   }
 
@@ -32,6 +51,7 @@ class _CompositionPageState extends State<CompositionPage> {
     setState(() {
       if (dialedNumber.isNotEmpty) {
         dialedNumber = dialedNumber.substring(0, dialedNumber.length - 1);
+        _filterContacts();
       }
     });
   }
@@ -39,141 +59,179 @@ class _CompositionPageState extends State<CompositionPage> {
   void _onClearPress() {
     setState(() {
       dialedNumber = "";
+      _filteredContacts = _allContacts;
     });
   }
 
-  List<String> _getFilteredContacts() {
-    return contacts
-        .where((contact) =>
-            contact.toLowerCase().contains(dialedNumber.toLowerCase()))
-        .toList();
+  // Placeholder function for adding contact
+  void addContact(String number) {
+    // This function is empty for now
   }
 
   @override
   Widget build(BuildContext context) {
     return Scaffold(
       backgroundColor: Colors.black,
-      body: Column(
+      body: Stack(
         children: [
-          // Top half: Display contacts matching dialed number
-          Expanded(
-            flex: 2,
-            child: Container(
-              padding: const EdgeInsets.all(16.0),
-              color: Colors.black,
-              child: Column(
-                crossAxisAlignment: CrossAxisAlignment.start,
-                children: [
-                  Expanded(
-                    child: ListView(
-                      children: _getFilteredContacts().map((contact) {
-                        return ListTile(
-                          title: Text(contact,
-                              style: const TextStyle(color: Colors.white)),
-                          onTap: () {
-                            // Handle contact selection if needed
-                          },
-                        );
-                      }).toList(),
-                    ),
-                  ),
-                ],
-              ),
-            ),
-          ),
-
-          // Bottom half: Dialpad and Dialed number display with erase button
-          Expanded(
-            flex: 2,
-            child: Container(
-              padding: const EdgeInsets.symmetric(horizontal: 16.0),
-              child: Column(
-                mainAxisAlignment: MainAxisAlignment.start,
-                children: [
-                  // Display dialed number with erase button
-                  Row(
-                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          Column(
+            children: [
+              // Top half: Display contacts matching dialed number
+              Expanded(
+                flex: 2,
+                child:
+                Container(
+                  padding: const EdgeInsets.only(top: 42.0, left: 16.0, right: 16.0, bottom: 16.0),
+                  color: Colors.black,
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
                     children: [
                       Expanded(
-                        child: Align(
-                          alignment: Alignment
-                              .center, // Aligns text to the center of the screen
-                          child: Text(
-                            dialedNumber,
-                            style: const TextStyle(
-                                fontSize: 24, color: Colors.white),
-                            overflow: TextOverflow.ellipsis,
-                          ),
+                        child: ListView(
+                          children: _filteredContacts.isNotEmpty
+                              ? _filteredContacts.map((contact) {
+                                  return ListTile(
+                                    title: Text(
+                                      contact.displayName,
+                                      style: const TextStyle(color: Colors.white),
+                                    ),
+                                    subtitle: contact.phones.isNotEmpty
+                                        ? Text(
+                                            contact.phones.first.number,
+                                            style: const TextStyle(color: Colors.grey),
+                                          )
+                                        : null,
+                                    trailing: Row(
+                                      mainAxisSize: MainAxisSize.min,
+                                      children: [
+                                        // Call button
+                                        IconButton(
+                                          icon: Icon(Icons.phone, color: Colors.green[300], size: 20),
+                                          onPressed: () {
+                                            print('Calling ${contact.displayName}');
+                                          },
+                                        ),
+                                        // Text button
+                                        IconButton(
+                                          icon: Icon(Icons.message, color: Colors.blue[300], size: 20),
+                                          onPressed: () {
+                                            print('Texting ${contact.displayName}');
+                                          },
+                                        ),
+                                      ],
+                                    ),
+                                    onTap: () {
+                                      // Handle contact selection if needed
+                                    },
+                                  );
+                                }).toList()
+                              : [Center(child: Text('No contacts found', style: TextStyle(color: Colors.white)))],
                         ),
                       ),
-                      IconButton(
-                        onPressed: _onClearPress,
-                        icon: const Icon(Icons.backspace, color: Colors.white),
-                      ),
                     ],
                   ),
-                  const SizedBox(height: 10),
+                ),
+              ),
 
-                  // Wrapping the dialpad in a SingleChildScrollView to prevent overflow
-                  Expanded(
-                    child: SingleChildScrollView(
-                      child: Column(
-                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+              // Bottom half: Dialpad and Dialed number display with erase button
+              Expanded(
+                flex: 2,
+                child: Container(
+                  padding: const EdgeInsets.symmetric(horizontal: 16.0),
+                  child: Column(
+                    mainAxisAlignment: MainAxisAlignment.start,
+                    children: [
+                      // Display dialed number with erase button
+                      Row(
+                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                         children: [
-                          // First Row (1, 2, 3)
-                          Row(
-                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
-                            children: [
-                              _buildDialButton('1'),
-                              _buildDialButton('2'),
-                              _buildDialButton('3'),
-                            ],
+                          Expanded(
+                            child: Align(
+                              alignment: Alignment.center,
+                              child: Text(
+                                dialedNumber,
+                                style: const TextStyle(fontSize: 24, color: Colors.white),
+                                overflow: TextOverflow.ellipsis,
+                              ),
+                            ),
                           ),
-                          // Second Row (4, 5, 6)
-                          Row(
-                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
-                            children: [
-                              _buildDialButton('4'),
-                              _buildDialButton('5'),
-                              _buildDialButton('6'),
-                            ],
-                          ),
-                          // Third Row (7, 8, 9)
-                          Row(
-                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
-                            children: [
-                              _buildDialButton('7'),
-                              _buildDialButton('8'),
-                              _buildDialButton('9'),
-                            ],
-                          ),
-                          // Fourth Row (*, 0, #)
-                          Row(
-                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
-                            children: [
-                              _buildDialButton('*'),
-                              _buildDialButton('0'),
-                              _buildDialButton('#'),
-                            ],
+                          IconButton(
+                            onPressed: _onClearPress,
+                            icon: const Icon(Icons.backspace, color: Colors.white),
                           ),
                         ],
                       ),
-                    ),
-                  ),
-                ],
-              ),
-            ),
-          ),
+                      const SizedBox(height: 10),
 
-          // Bottom action: Call button with padding
-          Padding(
-            padding: const EdgeInsets.only(bottom: 20.0),
-            child: FloatingActionButton(
-              backgroundColor: Colors.green,
+                      // Dialpad
+                      Expanded(
+                        child: SingleChildScrollView(
+                          child: Column(
+                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                            children: [
+                              Row(
+                                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                                children: [
+                                  _buildDialButton('1'),
+                                  _buildDialButton('2'),
+                                  _buildDialButton('3'),
+                                ],
+                              ),
+                              Row(
+                                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                                children: [
+                                  _buildDialButton('4'),
+                                  _buildDialButton('5'),
+                                  _buildDialButton('6'),
+                                ],
+                              ),
+                              Row(
+                                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                                children: [
+                                  _buildDialButton('7'),
+                                  _buildDialButton('8'),
+                                  _buildDialButton('9'),
+                                ],
+                              ),
+                              Row(
+                                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+                                children: [
+                                  _buildDialButton('*'),
+                                  _buildDialButton('0'),
+                                  _buildDialButton('#'),
+                                ],
+                              ),
+                            ],
+                          ),
+                        ),
+                      ),
+                    ],
+                  ),
+                ),
+              ),
+
+              // Add Contact Button with empty function call
+              Padding(
+                padding: const EdgeInsets.only(bottom: 20.0),
+                child: FloatingActionButton(
+                  backgroundColor: Colors.blue,
+                  onPressed: () {
+                    addContact(dialedNumber);
+                  },
+                  child: const Icon(Icons.person_add, color: Colors.white),
+                ),
+              ),
+            ],
+          ),
+          // Top Row with Back Arrow
+          Positioned(
+            top: 40.0,
+            left: 16.0,
+            child: IconButton(
+              icon: const Icon(Icons.arrow_back, color: Colors.white),
               onPressed: () {
-                // Handle call action
+                Navigator.pop(context);
               },
-              child: const Icon(Icons.phone, color: Colors.white),
             ),
           ),
         ],
@@ -187,8 +245,7 @@ class _CompositionPageState extends State<CompositionPage> {
       style: ElevatedButton.styleFrom(
         backgroundColor: Colors.black,
         shape: const CircleBorder(),
-        padding:
-            const EdgeInsets.all(16), // Adjusted padding to prevent overflow
+        padding: const EdgeInsets.all(16),
       ),
       child: Text(
         number,
@@ -199,21 +256,4 @@ class _CompositionPageState extends State<CompositionPage> {
       ),
     );
   }
-
-  Widget _buildDeleteButton() {
-    return ElevatedButton(
-      onPressed: _onDeletePress,
-      style: ElevatedButton.styleFrom(
-        backgroundColor: Colors.black,
-        shape: const CircleBorder(),
-        padding:
-            const EdgeInsets.all(16), // Adjusted padding to prevent overflow
-      ),
-      child: const Icon(
-        Icons.backspace,
-        color: Colors.white,
-        size: 24,
-      ),
-    );
-  }
 }