引言
在这篇博客中,我们将详细介绍如何设计和实现联系人列表功能。这个功能是我们应用的重要组成部分,主要包括从数据库加载联系人信息,展示联系人信息,以及实现联系人信息的增删改功能。通过具体的代码示例,我们将一步步展示这个功能是如何实现的。
数据库设计
首先,我们需要设计一个数据库表来存储联系人信息。在本例中,我们设计一个名为digital_persons
的表,包含以下字段:
-
id
:联系人ID,主键,自增。 -
name
:联系人姓名。 -
avatarUrl
:联系人头像的URL。
以下是SQLite数据库的表结构设计:
CREATE TABLE digital_persons ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, avatarUrl TEXT );
我们使用sqflite
库来管理SQLite数据库,并实现数据的增删改查操作。
数据模型
为了表示联系人数据,我们创建一个DigitalPerson
类,包含联系人信息的各个字段。
class DigitalPerson { final int? id; final String name; final String avatarUrl; DigitalPerson({ this.id, required this.name, required this.avatarUrl, }); Map<String, dynamic> toMap() { return { 'id': id, 'name': name, 'avatarUrl': avatarUrl, }; } factory DigitalPerson.fromMap(Map<String, dynamic> map) { return DigitalPerson( id: map['id'], name: map['name'], avatarUrl: map['avatarUrl'], ); } }
数据库助手类
为了简化数据库操作,我们创建一个DatabaseHelper
类,管理数据库的初始化和数据操作。
import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; import '../models/digital_person.dart'; class DatabaseHelper { static final DatabaseHelper _instance = DatabaseHelper._internal(); factory DatabaseHelper() => _instance; DatabaseHelper._internal(); static Database? _database; Future<Database> get database async { if (_database != null) return _database!; _database = await _initDatabase(); return _database!; } Future<Database> _initDatabase() async { final path = join(await getDatabasesPath(), 'contacts.db'); return openDatabase( path, onCreate: (db, version) { return db.execute( 'CREATE TABLE digital_persons(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, avatarUrl TEXT)', ); }, version: 1, ); } Future<void> insertDigitalPerson(DigitalPerson person) async { final db = await database; await db.insert('digital_persons', person.toMap()); } Future<void> updateDigitalPerson(DigitalPerson person) async { final db = await database; await db.update('digital_persons', person.toMap(), where: 'id = ?', whereArgs: [person.id]); } Future<void> deleteDigitalPerson(int id) async { final db = await database; await db.delete('digital_persons', where: 'id = ?', whereArgs: [id]); } Future<List<DigitalPerson>> getDigitalPersons() async { final db = await database; final maps = await db.query('digital_persons'); return List.generate(maps.length, (i) { return DigitalPerson.fromMap(maps[i]); }); } Future<List<DigitalPerson>> getSortedDigitalPersons() async { final db = await database; final maps = await db.query('digital_persons', orderBy: 'name'); return List.generate(maps.length, (i) { return DigitalPerson.fromMap(maps[i]); }); } }
在这个类中,我们实现了数据库的初始化、联系人信息的插入、更新、删除和查询操作。
实现联系人列表页面
我们从实现联系人列表页面的基本结构开始。首先,创建一个ContactsScreen
类,继承自StatefulWidget
,并在其中定义一个_ContactsScreenState
类。
import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import '../db/database_helper.dart'; import '../models/digital_person.dart'; import 'package:lpinyin/lpinyin.dart'; import 'chat_screen.dart'; class ContactsScreen extends StatefulWidget { @override _ContactsScreenState createState() => _ContactsScreenState(); } class _ContactsScreenState extends State<ContactsScreen> { List<DigitalPerson> digitalPersons = []; // 用于存储从数据库加载的数字人信息 final DatabaseHelper _dbHelper = DatabaseHelper.instance; Map<String, List<DigitalPerson>> groupedContacts = {}; // 分组联系人 @override void initState() { super.initState(); loadDigitalPersons(); // 加载并分组数字人信息 } void loadDigitalPersons() async { List<DigitalPerson> persons = await _dbHelper.getSortedDigitalPersons(); setState(() { digitalPersons = persons; groupedContacts = _groupPersonsByFirstLetter(persons); }); } Map<String, List<DigitalPerson>> _groupPersonsByFirstLetter(List<DigitalPerson> persons) { Map<String, List<DigitalPerson>> map = {}; for (DigitalPerson person in persons) { // 获取名字的首字母 String pinyin = PinyinHelper.getPinyinE(person.name.substring(0, 1)); String firstLetter = pinyin.substring(0, 1).toUpperCase(); if (!RegExp(r'[A-Z]').hasMatch(firstLetter)) { firstLetter = '#'; // 非字母开头的名字归类到其他 } if (!map.containsKey(firstLetter)) { map[firstLetter] = []; } map[firstLetter]!.add(person); } return map; } void _deleteDigitalPerson(DigitalPerson person) async { await _dbHelper.deleteDigitalPerson(person.id!); loadDigitalPersons(); } void _showDeleteConfirmationDialog(DigitalPerson person) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('确认删除'), content: Text('您确定要删除 ${person.name} 吗?'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text('取消'), ), TextButton( onPressed: () { _deleteDigitalPerson(person); Navigator.of(context).pop(); }, child: Text('删除'), ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('通讯录'), ), body: CustomScrollView( slivers: groupedContacts.entries.map((entry) { return SliverStickyHeader( header: Container( height: 50.0, color: Colors.lightBlue, padding: const EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.centerLeft, child: Text( entry.key, style: const TextStyle(color: Colors.white), ), ), sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, index) { DigitalPerson person = entry.value[index]; return Slidable( actionPane: SlidableDrawerActionPane(), actionExtentRatio: 0.25, child: ListTile( leading: CircleAvatar( backgroundImage: AssetImage(person.avatarUrl), ), title: Text(person.name), onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ChatScreen(digitalPerson: person), ), ); }, ), secondaryActions: <Widget>[ IconSlideAction( caption: '删除', color: Colors.red, icon: Icons.delete, onTap: () => _showDeleteConfirmationDialog(person), ), ], ); }, childCount: entry.value.length, ), ), ); }).toList(), ), ); } }
在上面的代码中,我们实现了联系人列表的显示、添加、编辑和删除功能。每个联系人显示在列表中,点击可以编辑联系人,右侧的删除按钮可以删除联系人。我们使用对话框来实现联系人信息的输入和编辑。
联系人信息的分组显示
为了使联系人列表更加清晰,我们按照联系人姓名的首字母对联系人进行分组显示。我们使用flutter_sticky_header
库实现分组显示。
Map<String, List<DigitalPerson>> _groupPersonsByFirstLetter(List<DigitalPerson> persons) { Map<String, List<DigitalPerson>> map = {}; for (DigitalPerson person in persons) { // 获取名字的首字母 String pinyin = PinyinHelper.getPinyinE(person.name.substring(0, 1)); String firstLetter = pinyin.substring(0, 1).toUpperCase(); if (!RegExp(r'[A-Z]').hasMatch(firstLetter)) { firstLetter = '#'; // 非字母开头的名字归类到其他 } if (!map.containsKey(firstLetter)) { map[firstLetter] = []; } map[firstLetter]!.add(person); } return map; }
结论
通过上述步骤,我们实现了联系人列表的基本功能,包括从数据库加载联系人信息,显示联系人信息,以及实现联系人信息的添加、编辑和删除功能。我们还通过分组显示使联系人列表更加清晰和易于使用。希望通过本篇博客,能帮助大家更好地理解和实现联系人列表功能。如果有任何问题或建议,欢迎在评论区交流。