入门案例
简介
flutter_picker_plus 是一个在 Flutter 中用于替代或增强原生选择器(Picker)的第三方库,它基于早期的 flutter_picker 做了许多改进与优化。
主要特性包括:
- 多种Picker类型:NumberPicker、DateTimePicker、ArrayPicker、Linkage / 级联 Picker 等。
- 国际化/多语言支持(支持 20+ 语言)
- 多种展示模式:模态底部弹出(modal)、对话框(dialog)、嵌入式(embedded)等。
- 高度可定制:可以自定义样式、颜色、布局、分隔符、头部、确认/取消按钮样式等等。
- 支持联动(Linkage):可以创建多个列之间有关联的数据(例如省市区三级联动)
- 支持大数据集:据称在性能方面进行了优化以处理较大数据集。
安装
flutter pub add flutter_picker_plus
案例
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_picker_plus/flutter_picker_plus.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: PickerDemoPage(),
);
}
}
class PickerDemoPage extends StatefulWidget {
const PickerDemoPage({super.key});
@override
State<PickerDemoPage> createState() => _PickerDemoPageState();
}
class _PickerDemoPageState extends State<PickerDemoPage> {
String _selectedValue = "未选择";
void _showDateTimePicker() {
Picker(
adapter: DateTimePickerAdapter(
type: PickerDateTimeType.kYMDHM, // 年月日时分
isNumberMonth: true, // 月份数字显示
),
title: const Text("请选择日期时间"),
onConfirm: (Picker picker, List value) {
DateTime dateTime = (picker.adapter as DateTimePickerAdapter).value!;
setState(() {
_selectedValue = dateTime.toString();
});
},
).showDialog(context); // 弹出对话框
}
void _showCityPicker() {
final picker = Picker(
adapter: PickerDataAdapter<String>(
pickerData: JsonDecoder().convert('''
[
{"北京": ["东城", "西城", "朝阳"]},
{"上海": ["黄浦", "徐汇", "浦东"]},
{"广东": ["广州", "深圳", "珠海"]}
]
'''),
),
title: const Text("请选择省市区"),
onConfirm: (picker, value) {
setState(() {
_selectedValue = picker.getSelectedValues().join(" - ");
});
},
);
// 使用 makePicker() 生成 Widget 并展示在对话框里
showDialog(
context: context,
builder: (context) {
return Dialog(
child: SizedBox(
height: 300,
child: picker.makePicker(), // 直接作为 Widget 使用
),
);
},
);
}
void _showSinglePicker() {
Picker(
adapter: PickerDataAdapter<String>(
pickerData: ["苹果", "香蕉", "橙子", "葡萄", "西瓜"],
),
title: Text("请选择水果"),
onConfirm: (Picker picker, List value) {
setState(() {
_selectedValue = picker.getSelectedValues().join(" - ");
});
},
cancelText: "返回", // 默认是 "Cancel",改成中文
confirmText: "确定", // 默认是 "Confirm",改成中文
).showModal(context); // 底部弹出选择器
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("flutter_picker_plus Demo")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("选择结果:$_selectedValue", style: const TextStyle(fontSize: 18)),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _showSinglePicker,
child: const Text("选择水果"),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _showDateTimePicker,
child: const Text("选择日期时间"),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _showCityPicker,
child: const Text("选择省市区"),
),
],
),
),
);
}
}
Picker
基础属性
adapter:必填。决定选择器的数据来源和表现形式。PickerDataAdapter:普通数据或多级联动数据。DateTimePickerAdapter:日期时间选择器。- 还可以自定义 Adapter。
title:顶部标题区域的 Widget,一般是Text("请选择...")。selecteds:初始选中的索引,比如[0, 2]代表第一列选中第0个,第二列选中第2个。onConfirm
用户点击确定按钮时回调,参数:onConfirm: (picker, values) { print(values); // 选中的索引 print(picker.getSelectedValues()); // 选中的值 }onCancel
用户点击取消时回调。onSelect
滚动选择时实时触发(值变化时)。
UI 控制
height
整个 Picker 的高度,默认 200。itemExtent
每一行的高度,默认 36。hideHeader
是否隐藏头部(标题 + 按钮),默认false。backgroundColor
背景颜色。cancel/confirm:自定义取消和确认按钮的 Widget,例如:// 用 Text cancel: Text("返回", style: TextStyle(color: Colors.red)), confirm: Text("确定", style: TextStyle(color: Colors.blue)), // 用按钮 cancel: TextButton( onPressed: () => Navigator.pop(context), child: const Text("返回", style: TextStyle(color: Colors.red)), ), confirm: TextButton( onPressed: () { picker.doConfirmAction(); // ✅ 可以访问外部定义的 picker }, child: const Text("确定", style: TextStyle(color: Colors.blue)), ),注意:一旦你自定义了
cancel/confirm(无论是Text、Container、TextButton),库就 不会自动触发onConfirm,所以点击“确定”按钮后不会执行回调。cancelText和confirmTextcancelText→ 设置左侧按钮文字confirmText→ 设置右侧按钮文字
cancelText: "返回", // 默认是 "Cancel",改成中文 confirmText: "确定", // 默认是 "Confirm",改成中文textStyle:普通项的文字样式。selectedTextStyle:选中项的文字样式。
展示方式
showDialog(context):以弹窗方式显示。showModal(context):从底部滑出的方式显示。makePicker():返回一个 Widget,你可以放到Dialog、BottomSheet或页面里。
特殊属性(针对日期时间)
当 adapter 是 DateTimePickerAdapter 时,还可以用:
type:控制时间显示的粒度,比如:PickerDateTimeType.kYMD(年月日)PickerDateTimeType.kYMDHM(年月日时分)PickerDateTimeType.kYMDHMS(年月日时分秒)
isNumberMonth:月份是否显示为数字(true)还是文字(false)。yearBegin / yearEnd:限定年份范围。
PickerDataAdapter
是什么
PickerDataAdapter 是 flutter_picker_plus 中用来把原始数据(数组或多级数据)适配成 Picker 可识别的数据结构的适配器。
它本质上负责把普通的 List 或 List<PickerItem> 转换成 Picker 内部可以渲染的树形结构,并提供一些额外的选项,比如是否多级联动、是否是数组模式等。
主要构造函数
PickerDataAdapter<T>({
List? pickerData, // 原始数据:可以是 List、List<Map> 等
List<PickerItem<T>>? data, // 已经构造好的 PickerItem 列表
this.isArray = false, // 是否是数组模式(没有多级联动)
})
pickerData
- 支持原始数组、嵌套 Map、List 等格式。
- 适合从 JSON 直接生成 Picker。
- 内部会被
_parseData转换成PickerItem结构。
List data = [
{"北京": ["东城", "西城"]},
{"上海": ["浦东", "徐汇"]}
];
PickerDataAdapter(pickerData: data);
data
- 如果你已经手动创建了
PickerItem对象列表,也可以直接传给data。 - 例如:
List<PickerItem<String>> items = [
PickerItem(text: const Text("北京"), value: "北京"),
PickerItem(text: const Text("上海"), value: "上海"),
];
PickerDataAdapter(data: items);
isArray
- 布尔值,表示数据是否是数组模式。
true:数据按平级数组处理,不考虑多级联动。false(默认):数据会按多级树状处理。
PickerItem
PickerItem 是 flutter_picker_plus 中的数据单元,用于表示 Picker 中的每一个选项。它既可以是单级选项,也可以是多级(层级/子选项)结构的节点。
class PickerItem<T> {
final Widget? text;
final T? value;
final List<PickerItem<T>>? children;
PickerItem({this.text, this.value, this.children});
}
参数说明
text- 类型:
Widget? - 功能:在 Picker 中显示的内容。可以是
Text、Icon、或者自定义 Widget。 - 可选,但建议提供,如果没有提供
value,至少要有text。
- 类型:
value- 类型:
T?(泛型) - 功能:和这个 Picker 选项对应的真实数据。通过
picker.getSelectedValues()可以拿到这个值。 - 可选,如果没有提供
text,至少要有value。 - 泛型
T决定value类型,与PickerDataAdapter<T>保持一致。
- 类型:
children- 类型:
List<PickerItem<T>>? - 功能:子节点,用于多级联动 Picker(省-市-区等场景)。
- 如果没有子节点,可以为
null。
- 类型:
PickerDataAdapter的泛型
PickerDataAdapter<T> 的泛型 T 决定了 选中值的类型,也就是你通过 picker.getSelectedValues() 或 PickerItem.value 拿到的数据类型。
泛型 T 的作用
T表示每个选项的 value 类型。- 当你定义
PickerItem<T>时,其value就是T类型。 PickerDataAdapter<T>会保证返回的选中值类型与T匹配,避免类型转换错误。
泛型用 String,因为数据是字符串。
Picker(
adapter: PickerDataAdapter<String>(
pickerData: ["苹果", "香蕉", "橙子"], // 值是 String
),
onConfirm: (picker, value) {
// 这里 getSelectedValues() 返回 List<String>
List<String> selected = picker.getSelectedValues();
print(selected); // ["苹果"]
},
).showModal(context);
泛型用 int,因为数据是数字。
Picker(
adapter: PickerDataAdapter<int>(
pickerData: [1, 2, 3, 4, 5], // 值是 int
),
onConfirm: (picker, value) {
List<int> selected = picker.getSelectedValues(); // List<int>
print(selected); // [3]
},
).showModal(context);
泛型用 Fruit,这样选中的值就是你自定义对象。
class Fruit {
final String name;
final double price;
Fruit(this.name, this.price);
}
Picker(
adapter: PickerDataAdapter<Fruit>(
data: [
PickerItem(text: "苹果", value: Fruit("苹果", 3.5)),
PickerItem(text: "香蕉", value: Fruit("香蕉", 2.0)),
],
),
onConfirm: (picker, value) {
List<Fruit> selected = picker.getSelectedValues(); // List<Fruit>
print(selected[0].name); // 苹果
},
).showModal(context);
案例
import 'package:flutter/material.dart';
import 'package:flutter_picker_plus/flutter_picker_plus.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const CityPickerDemo(),
);
}
}
class CityPickerDemo extends StatefulWidget {
const CityPickerDemo({super.key});
@override
State<CityPickerDemo> createState() => _CityPickerDemoState();
}
class _CityPickerDemoState extends State<CityPickerDemo> {
String _selectedCity = "未选择";
void _showCityPicker() {
Picker(
adapter: PickerDataAdapter<String>(
data: [
PickerItem(
value: "北京",
text: const Text("北京"),
children: [
PickerItem(value: "东城", text: const Text("东城")),
PickerItem(value: "西城", text: const Text("西城")),
PickerItem(value: "朝阳", text: const Text("朝阳")),
],
),
PickerItem(
value: "上海",
text: const Text("上海"),
children: [
PickerItem(value: "黄浦", text: const Text("黄浦")),
PickerItem(value: "徐汇", text: const Text("徐汇")),
PickerItem(value: "浦东", text: const Text("浦东")),
],
),
PickerItem(
value: "广东",
text: const Text("广东"),
children: [
PickerItem(value: "广州", text: const Text("广州")),
PickerItem(value: "深圳", text: const Text("深圳")),
PickerItem(value: "珠海", text: const Text("珠海")),
],
),
],
),
title: const Text("请选择省市区"),
onConfirm: (Picker picker, List<int> value) {
setState(() {
_selectedCity = picker.getSelectedValues().join(" - ");
});
},
).showModal(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("PickerItem 多级选择器 Demo")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("选择结果:$_selectedCity", style: const TextStyle(fontSize: 18)),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _showCityPicker,
child: const Text("选择省市区"),
),
],
),
),
);
}
}