Skip to content

Commit

Permalink
Merge branch 'master' into firebase-integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashish Sharma committed Feb 12, 2024
2 parents a5d9684 + 6e207cc commit 1331189
Show file tree
Hide file tree
Showing 12 changed files with 369 additions and 60 deletions.
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion flutter.compileSdkVersion
compileSdkVersion 33
ndkVersion flutter.ndkVersion

compileOptions {
Expand Down
2 changes: 2 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.kilvish">
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<application
android:label="kilvish"
android:name="${applicationName}"
Expand Down
2 changes: 2 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>NSContactsUsageDescription</key>
<string>This app requires contacts access to function properly.</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
Expand Down
264 changes: 264 additions & 0 deletions lib/contact_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
import 'package:contacts_service/contacts_service.dart';
import 'package:flutter/material.dart';
import 'package:kilvish/models.dart';
import 'package:kilvish/provider/search_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:collection/collection.dart';

enum ContactSelection { singleSelect, multiSelect }

class ContactScreen extends StatefulWidget {
final ContactSelection contactSelection;

const ContactScreen(
{Key? key, this.contactSelection = ContactSelection.singleSelect})
: super(key: key);

@override
State<ContactScreen> createState() => _ContactScreenState();
}

class _ContactScreenState extends State<ContactScreen> {
final List<ContactModel> _selectedContactsList = [];
final List<ContactModel> _kilvishContactsList = [
ContactModel(
kilvishId: "Kelvish ID 1",
name: 'Kilvish User 1',
phoneNumber: "65656-52452"),
];
bool _isLoading = true;

final SearchNotifier _searchNotifier = SearchNotifier();
final TextEditingController _searchController = TextEditingController();
final String _hintText = "Enter Name, Contact";
bool _permissionDenied = false;

@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await getContactPermission();
});
}

@override
void dispose() {
_searchController.dispose();
super.dispose();
}

/// Ask Contact permission
Future<void> getContactPermission() async {
if (await Permission.contacts.request().isGranted) {
setState(() {
_permissionDenied = false;
});
// Permission is granted, fetch contacts
searchFromContactList();
} else {
final status = await Permission.contacts.request();
if (status.isGranted) {
setState(() {
_permissionDenied = false;
});
// Permission is granted, fetch contacts
searchFromContactList();
} else {
setState(() {
_permissionDenied = true;
});
}
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: _permissionDenied
? SizedBox(
height: 72,
child: Card(
child: Container(
margin: const EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: Text(
"Contact permission isn't provided so contacts are not fetched followed by a button to provide permission.")),
const SizedBox(width: 8),
InkWell(
onTap: () {
getContactPermission();
},
child: const Text("Grant"))
],
),
),
),
)
: const SizedBox(),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
Navigator.pop(context, _selectedContactsList);
},
label: const Text('Done'),
icon: const Icon(Icons.check, color: Colors.green),
backgroundColor: Colors.pink,
),
appBar: PreferredSize(
preferredSize: const Size(double.infinity, kToolbarHeight),
child: appBarForSearch(),
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: ValueListenableBuilder<List<ContactModel>>(
valueListenable: _searchNotifier.contactNotifier,
builder: (context, filterList, child) {
return ListView.builder(
itemCount: filterList.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
if (widget.contactSelection ==
ContactSelection.singleSelect) {
Navigator.pop(context, filterList[index]);
} else {
final localContact = _selectedContactsList
.firstWhereOrNull((element) =>
element.name == filterList[index].name);
if (localContact == null) {
_selectedContactsList.add(filterList[index]);
} else {
_selectedContactsList.remove(localContact);
}
setState(() {});
}
},
child: Column(
children: [
(index > 0 &&
filterList[index].kilvishId == null &&
filterList[index - 1].kilvishId != null)
? const Divider(height: 1, color: Colors.grey)
: const SizedBox(),
ListTile(
leading: Stack(
children: [
CircleAvatar(
child: Text(filterList[index].name.isNotEmpty
? filterList[index].name[0]
: ""),
),
if (_selectedContactsList.firstWhereOrNull(
(element) =>
element.name ==
filterList[index].name) !=
null)
const CircleAvatar(
child: Center(
child: Icon(Icons.check,
color: Colors.green)),
)
],
),
title: Text(filterList[index].name),
subtitle: Text(
filterList[index].phoneNumber.isNotEmpty
? filterList[index].phoneNumber
: 'No phone number',
),
trailing: Text((filterList[index].kilvishId ?? "")),
),
],
),
);
},
);
},
),
);
}

/// AppBar for Search bhajan
AppBar appBarForSearch() {
return AppBar(
centerTitle: true,
automaticallyImplyLeading: false,
leading: Container(
margin: const EdgeInsets.only(left: 8),
child: IconButton(
icon: Icon(Icons.arrow_back_ios,
color: Theme.of(context).textSelectionTheme.selectionColor),
onPressed: () {
Navigator.pop(context);
},
)),
title: titleSearchWidget(),
);
}

/// Search Contact from Kilvish Contact & Phone Contact List
Future<void> searchFromContactList() async {
setState(() {
_isLoading = true;
});
final kilvishSearchResult = _kilvishContactsList.where((element) {
return element.name
.toLowerCase()
.contains(_searchController.text.toLowerCase()) ||
element.phoneNumber
.toLowerCase()
.contains(_searchController.text.toLowerCase()) ||
(element.kilvishId != null &&
element.kilvishId!
.toLowerCase()
.contains(_searchController.text.toLowerCase()));
}).toList();

List<Contact> contactSearchResult =
await ContactsService.getContacts(query: _searchController.text);
if (contactSearchResult.length > 5) {
contactSearchResult = contactSearchResult.take(5).toList();
}
contactSearchResult.forEach((contactsElement) {
final localContact = kilvishSearchResult.firstWhereOrNull((element) {
if (contactsElement.phones != null &&
contactsElement.phones?[0].value != null) {
return (element.phoneNumber == contactsElement.phones![0].value);
} else {
return false;
}
});
if (localContact == null) {
kilvishSearchResult.add(ContactModel(
name: (contactsElement.displayName ?? ""),
phoneNumber: (contactsElement.phones != null &&
contactsElement.phones!.isNotEmpty)
? (contactsElement.phones![0].value ?? "")
: ""));
}
});
_searchNotifier.updateSearchValue(kilvishSearchResult);
setState(() {
_isLoading = false;
});
}

/// Showing title widget for app bar Search title
Widget titleSearchWidget() {
return TextField(
controller: _searchController,
onChanged: (value) {
searchFromContactList();
},
decoration: InputDecoration(
prefixIcon: Icon(Icons.search,
color: Theme.of(context).textSelectionTheme.selectionColor),
hintText: _hintText,
hintStyle: TextStyle(
color: Theme.of(context).textSelectionTheme.selectionColor)),
);
}
}
Loading

0 comments on commit 1331189

Please sign in to comment.