集合是用于存储和组织数据的重要数据结构。Dart 提供了几种内置的集合类型,包括 List(列表)、Set(集合)和 Map(映射),它们各自适用于不同的数据存储和访问场景。
1. List (列表)
List 是一个有序的、可索引的元素集合,类似于其他编程语言中的数组。List 中的元素可以重复。
创建 List:
// 字面量创建
List<int> numbers = [1, 2, 3, 4, 5];
List<String> fruits = ["apple", "banana", "orange"];
// 使用 List 构造函数
List<double> prices = List.filled(3, 0.0); // 创建一个包含3个0.0的固定长度列表
List<dynamic> mixedList = [1, "hello", true]; // 动态类型列表
常用操作:
print(numbers[0]); // 输出: 1
print(fruits[1]); // 输出: banana
add()、addAll()、insert()。
fruits.add("grape"); // ["apple", "banana", "orange", "grape"]
fruits.addAll(["kiwi", "mango"]); // ["apple", ..., "mango"]
fruits.insert(1, "lemon"); // ["apple", "lemon", "banana", ...]
remove()、removeAt()、removeLast()、clear()。
fruits.remove("lemon");
fruits.removeAt(0); // 删除第一个元素
fruits.removeLast();
fruits.clear(); // 清空列表
numbers[0] = 10; // numbers 现在是 [10, 2, 3, 4, 5]
length 属性。
print(numbers.length); // 输出: 5
for 循环、for-in 循环、forEach()。
for (int i = 0; i < fruits.length; i++) {
print(fruits[i]);
}
for (String fruit in fruits) {
print(fruit);
}
fruits.forEach((fruit) => print(fruit));
2. Set (集合)
Set 是一个无序的、唯一的元素集合。Set 中的元素不能重复,如果尝试添加重复元素,Set 会忽略该操作。
创建 Set:
// 字面量创建
Set<int> uniqueNumbers = {1, 2, 3, 1}; // 实际存储为 {1, 2, 3}
Set<String> colors = {"red", "green", "blue"};
// 使用 Set 构造函数
Set<String> emptySet = {}; // 注意:{} 默认是 Map,要明确指定类型
Set<String> anotherEmptySet = Set<String>();
常用操作:
add()、addAll()。
uniqueNumbers.add(4); // {1, 2, 3, 4}
uniqueNumbers.add(2); // 不会添加,因为 2 已存在
colors.addAll({"yellow", "red"}); // "red" 不会重复添加
remove()、clear()。
colors.remove("green");
colors.clear();
contains()。
print(uniqueNumbers.contains(3)); // 输出: true
union() (并集)、intersection() (交集)、difference() (差集)。
Set<int> setA = {1, 2, 3};
Set<int> setB = {3, 4, 5};
print(setA.union(setB)); // {1, 2, 3, 4, 5}
print(setA.intersection(setB)); // {3}
print(setA.difference(setB)); // {1, 2}
3. Map (映射)
Map 是一个键值对的集合,其中每个键都是唯一的,并且映射到一个值。Map 类似于其他语言中的字典或哈希表。
创建 Map:
// 字面量创建
Map<String, String> capitals = {
"USA": "Washington D.C.",
"Japan": "Tokyo",
"China": "Beijing"
};
Map<String, dynamic> userInfo = {
"name": "Alice",
"age": 30,
"isActive": true
};
// 使用 Map 构造函数
Map<int, String> emptyMap = {}; // 注意:{} 默认是 Map,但最好明确指定类型
Map<int, String> anotherEmptyMap = Map<int, String>();
常用操作:
print(capitals["USA"]); // 输出: Washington D.C.
print(userInfo["age"]); // 输出: 30
capitals["France"] = "Paris"; // 添加新键值对
capitals["USA"] = "New York"; // 修改已有键的值
remove()。
capitals.remove("Japan");
containsKey()、containsValue()。
print(capitals.containsKey("China")); // 输出: true
print(capitals.containsValue("Tokyo")); // 输出: false (因为 Japan 已经被删除了)
forEach()、keys、values。
capitals.forEach((key, value) {
print("$key: $value");
});
for (String key in capitals.keys) {
print("Key: $key");
}
for (String value in capitals.values) {
print("Value: $value");
}
集合在 Flutter 开发中无处不在,它们是组织和管理 UI 数据、状态以及从后端获取数据的基础。无论是构建列表、管理用户偏好设置,还是处理复杂的 JSON 数据,都离不开对 List、Set 和 Map 的熟练运用。
案例:一个简单的购物清单应用
我们将创建一个简单的购物清单应用,演示如何使用 List 来存储商品,以及如何使用 Map 来表示商品的属性。这个案例将涵盖 List 的添加、删除、修改和遍历操作。
import 'package:flutter/material.dart';
// 定义一个商品类,使用 Map 来表示其属性
class ShoppingItem {
String name;
int quantity;
bool isPurchased;
ShoppingItem({
required this.name,
this.quantity = 1,
this.isPurchased = false,
});
// 将 ShoppingItem 转换为 Map,方便存储或传输
Map<String, dynamic> toMap() {
return {
'name': name,
'quantity': quantity,
'isPurchased': isPurchased,
};
}
// 从 Map 创建 ShoppingItem 对象
factory ShoppingItem.fromMap(Map<String, dynamic> map) {
return ShoppingItem(
name: map['name'] as String,
quantity: map['quantity'] as int,
isPurchased: map['isPurchased'] as bool,
);
}
}
class ShoppingListScreen extends StatefulWidget {
const ShoppingListScreen({super.key});
@override
State<ShoppingListScreen> createState() => _ShoppingListScreenState();
}
class _ShoppingListScreenState extends State<ShoppingListScreen> {
// 使用 List 来存储 ShoppingItem 对象
final List<ShoppingItem> _shoppingList = [
ShoppingItem(name: '牛奶', quantity: 2),
ShoppingItem(name: '面包', quantity: 1, isPurchased: true),
ShoppingItem(name: '鸡蛋', quantity: 12),
];
final TextEditingController _itemController = TextEditingController();
void _addItem() {
if (_itemController.text.isNotEmpty) {
setState(() {
_shoppingList.add(ShoppingItem(name: _itemController.text));
_itemController.clear();
});
}
}
void _togglePurchase(int index) {
setState(() {
_shoppingList[index].isPurchased = !_shoppingList[index].isPurchased;
});
}
void _deleteItem(int index) {
setState(() {
_shoppingList.removeAt(index);
});
}
@override
void dispose() {
_itemController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('购物清单'),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _itemController,
decoration: const InputDecoration(
labelText: '添加新商品',
border: OutlineInputBorder(),
),
onSubmitted: (_) => _addItem(), // 按回车键添加
),
),
const SizedBox(width: 8.0),
ElevatedButton(
onPressed: _addItem,
child: const Text('添加'),
),
],
),
),
Expanded(
child: ListView.builder(
itemCount: _shoppingList.length,
itemBuilder: (context, index) {
final item = _shoppingList[index];
return Card(
margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: ListTile(
leading: Checkbox(
value: item.isPurchased,
onChanged: (bool? newValue) {
_togglePurchase(index);
},
),
title: Text(
item.name,
style: TextStyle(
decoration: item.isPurchased ? TextDecoration.lineThrough : null,
color: item.isPurchased ? Colors.grey : Colors.black,
),
),
subtitle: Text('数量: ${item.quantity}'),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => _deleteItem(index),
),
),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 示例:将所有商品转换为 Map 列表
List<Map<String, dynamic>> itemsAsMaps = _shoppingList.map((item) => item.toMap()).toList();
print('所有商品 (Map 形式): $itemsAsMaps');
// 示例:从 Map 列表重新创建 ShoppingItem 对象
List<ShoppingItem> recreatedItems = itemsAsMaps.map((map) => ShoppingItem.fromMap(map)).toList();
print('重新创建的商品: ${recreatedItems.map((e) => e.name).toList()}');
},
child: const Icon(Icons.info_outline),
),
);
}
}
void main() {
runApp(const MaterialApp(home: ShoppingListScreen()));
}
案例分析:
final List<ShoppingItem> _shoppingList = [...]:我们使用 List<ShoppingItem> 来存储购物清单中的所有商品。这是一个泛型 List,明确指定了列表中存储的元素类型是 ShoppingItem 对象。ShoppingItem 类中的 toMap() 和 fromMap():为了演示 Map 的应用,ShoppingItem 类提供了 toMap() 方法将对象转换为 Map<String, dynamic>,以及 factory ShoppingItem.fromMap() 工厂构造函数从 Map 创建对象。这在处理 JSON 数据(通常是 Map 结构)时非常有用。_shoppingList.add(...):在 _addItem 方法中,我们使用 add() 方法向 _shoppingList 中添加新的商品。setState() 用于通知 Flutter 框架数据已更改,需要重新构建 UI。_shoppingList[index].isPurchased = ...:在 _togglePurchase 方法中,我们通过索引访问 List 中的元素,并修改其 isPurchased 属性。这展示了 List 元素的修改操作。_shoppingList.removeAt(index):在 _deleteItem 方法中,我们使用 removeAt() 方法根据索引删除 List 中的元素。ListView.builder:Flutter 中构建动态列表的常用 Widget。它通过 itemCount 属性获取列表长度,并通过 itemBuilder 回调函数为每个列表项构建 UI。itemBuilder 内部通过 _shoppingList[index] 访问当前项的数据。_shoppingList.map((item) => item.toMap()).toList():在 FloatingActionButton 的 onPressed 回调中,我们演示了如何使用 map() 方法将 ShoppingItem 对象的 List 转换为 Map 对象的 List。这在数据序列化(例如保存到本地存储或发送到服务器)时非常有用。这个案例全面展示了 Dart 中 List 和 Map 集合类型在 Flutter 应用中的实际应用。通过这些集合,我们可以高效地组织和管理数据,从而构建出功能丰富且交互性强的应用程序。