mirror of https://github.com/flutter/samples.git
[Rolodex] Update fidelity of Contact list screens (#2588)
This PR: - Updates the fidelity of the home screen and contact list screens to near full fidelity - Renames "Contact Lists" to "Contact Groups". We have multiple collections of contacts, so having lists of lists felt confusing - Adds more functionality to the data models Comparison of the two screens against native: | Native | Flutter | | --- | --- | | <img width="418" alt="Screenshot 2025-02-19 at 2 59 58 PM" src="https://github.com/user-attachments/assets/1cc53d19-6bb5-4782-82fa-1a62cd75fd51" /> | <img width="397" alt="Screenshot 2025-02-19 at 2 58 54 PM" src="https://github.com/user-attachments/assets/ddab75f2-4aec-4239-a736-690a3185c196" /> | | <img width="409" alt="Screenshot 2025-02-19 at 2 59 41 PM" src="https://github.com/user-attachments/assets/ee0d81ee-ae60-47ad-b071-266f39ce9b70" /> | <img width="402" alt="Screenshot 2025-02-19 at 2 58 45 PM" src="https://github.com/user-attachments/assets/88bf22f9-d8bd-40d7-a9ca-f2ded439de6c" /> | Notably the container widget on the first screen is a placeholder, until a Cupertino collapsable widget is made.pull/2591/head
parent
864153a3ef
commit
c787dd01a7
@ -0,0 +1,118 @@
|
||||
// Copyright 2018 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import 'contact.dart';
|
||||
|
||||
void _sortContacts(List<Contact> contacts) {
|
||||
contacts.sort((Contact a, Contact b) {
|
||||
final int checkLastName = a.lastName.compareTo(b.lastName);
|
||||
if (checkLastName != 0) {
|
||||
return checkLastName;
|
||||
}
|
||||
final int checkFirstName = a.firstName.compareTo(b.firstName);
|
||||
if (checkFirstName != 0) {
|
||||
return checkFirstName;
|
||||
}
|
||||
if (a.middleName != null && b.middleName != null) {
|
||||
final int checkMiddleName = a.middleName!.compareTo(b.middleName!);
|
||||
if (checkMiddleName != 0) {
|
||||
return checkMiddleName;
|
||||
}
|
||||
} else if (a.middleName != null || b.middleName != null) {
|
||||
return a.middleName != null ? 1 : -1;
|
||||
}
|
||||
// If both contacts have the exact same name, order by first created.
|
||||
return a.id.compareTo(b.id);
|
||||
});
|
||||
}
|
||||
|
||||
typedef AlphabetizedContactMap = SplayTreeMap<String, List<Contact>>;
|
||||
|
||||
class ContactGroup {
|
||||
factory ContactGroup({
|
||||
required int id,
|
||||
required String label,
|
||||
bool permanent = false,
|
||||
String? title,
|
||||
List<Contact>? contacts,
|
||||
}) {
|
||||
final contactsCopy = contacts ?? <Contact>[];
|
||||
_sortContacts(contactsCopy);
|
||||
return ContactGroup._internal(
|
||||
id: id,
|
||||
label: label,
|
||||
permanent: permanent,
|
||||
title: title,
|
||||
contacts: contactsCopy,
|
||||
);
|
||||
}
|
||||
|
||||
ContactGroup._internal({
|
||||
required this.id,
|
||||
required this.label,
|
||||
this.permanent = false,
|
||||
String? title,
|
||||
List<Contact>? contacts,
|
||||
}) : title = title ?? label,
|
||||
_contacts = contacts ?? const <Contact>[];
|
||||
|
||||
final int id;
|
||||
|
||||
final bool permanent;
|
||||
|
||||
final String label;
|
||||
|
||||
final String title;
|
||||
|
||||
final List<Contact> _contacts;
|
||||
|
||||
List<Contact> get contacts => _contacts;
|
||||
|
||||
AlphabetizedContactMap get alphabetizedContacts {
|
||||
final AlphabetizedContactMap contactsMap = AlphabetizedContactMap();
|
||||
for (Contact contact in _contacts) {
|
||||
final String lastInitial = contact.lastName[0].toUpperCase();
|
||||
if (contactsMap.containsKey(lastInitial)) {
|
||||
contactsMap[lastInitial]!.add(contact);
|
||||
} else {
|
||||
contactsMap[lastInitial] = [contact];
|
||||
}
|
||||
}
|
||||
return contactsMap;
|
||||
}
|
||||
}
|
||||
|
||||
class ContactGroupsModel extends ChangeNotifier {
|
||||
final List<ContactGroup> _lists = generateSeedData();
|
||||
|
||||
List<ContactGroup> get lists => _lists;
|
||||
|
||||
ContactGroup findContactList(int id) {
|
||||
return lists[id];
|
||||
}
|
||||
}
|
||||
|
||||
final allPhone = ContactGroup(
|
||||
id: 0,
|
||||
permanent: true,
|
||||
label: 'All iPhone',
|
||||
title: 'iPhone',
|
||||
contacts: allContacts.toList(),
|
||||
);
|
||||
|
||||
final friends = ContactGroup(
|
||||
id: 1,
|
||||
label: 'Friends',
|
||||
contacts: [allContacts.elementAt(3)],
|
||||
);
|
||||
|
||||
final work = ContactGroup(id: 2, label: 'Work');
|
||||
|
||||
List<ContactGroup> generateSeedData() {
|
||||
return [allPhone, friends, work];
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// Copyright 2018 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import 'contact.dart';
|
||||
|
||||
class ContactList {
|
||||
ContactList({
|
||||
required this.id,
|
||||
required this.label,
|
||||
this.permanent = false,
|
||||
String? title,
|
||||
}) : title = title ?? label;
|
||||
|
||||
final int id;
|
||||
|
||||
final bool permanent;
|
||||
|
||||
final String label;
|
||||
|
||||
final String title;
|
||||
|
||||
final List<Contact> contacts = [];
|
||||
}
|
||||
|
||||
class ContactListsModel extends ChangeNotifier {
|
||||
final List<ContactList> _lists = generateSeedData();
|
||||
|
||||
List<ContactList> get lists => _lists;
|
||||
|
||||
ContactList findContactList(int id) {
|
||||
return lists[id];
|
||||
}
|
||||
}
|
||||
|
||||
List<ContactList> generateSeedData() {
|
||||
return [
|
||||
ContactList(id: 0, permanent: true, label: 'All iPhone', title: 'iPhone'),
|
||||
ContactList(id: 1, label: 'Friends'),
|
||||
ContactList(id: 2, label: 'Work'),
|
||||
];
|
||||
}
|
@ -1,18 +1,19 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:rolodex/main.dart';
|
||||
import 'package:rolodex/screens/contacts.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Opens on all contacts page', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const RolodexApp());
|
||||
|
||||
expect(find.text('Contacts page'), findsOneWidget);
|
||||
expect(find.byType(ContactListsPage), findsOneWidget);
|
||||
expect(find.text('Add List'), findsNothing);
|
||||
|
||||
await tester.tap(find.text('Lists'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Add List'), findsOneWidget);
|
||||
expect(find.text('Contacts page'), findsNothing);
|
||||
expect(find.byType(ContactListsPage), findsNothing);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in new issue