CupertinoSlidingSegmentedControl
Flutter中cupertino库提供的一个iOS风格的滑动分段选择器
CupertinoSlidingSegmentedControl是Flutter中cupertino库提供的一个iOS风格的滑动分段选择器。它模拟了iOS原生应用中常见的选择器,具有平滑的滑动动画效果,用户可以通过点击或滑动来切换不同的选项。

主要用途和逻辑
CupertinoSlidingSegmentedControl主要用于提供一组互斥的选择项,用户只能选择其中一个。它通常用于:
- 模式切换: 例如在“列表视图”和“网格视图”之间切换。
- 数据筛选: 选择不同的数据筛选条件(如“全部”、“已完成”、“待处理”)。
- 时间范围选择: “今天”、“本周”、“本月”等。
- 设置选项: 提供多选一的设置选项。
它的核心逻辑是管理当前选中的值(groupValue),并通过onValueChanged回调函数通知父组件选中的值发生了变化。
使用场景
CupertinoSlidingSegmentedControl适用于任何需要iOS风格多选一界面的场景,特别常见于:
- 工具栏或导航栏中: 作为筛选或视图切换工具。
- 设置页面: 提供明确的选项组。
- 数据展示界面: 切换不同维度的数据视图。
示例
基础使用
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class BasicSegmentedControlDemo extends StatefulWidget {
const BasicSegmentedControlDemo({super.key});
State<BasicSegmentedControlDemo> createState() => _BasicSegmentedControlDemoState();
}
class _BasicSegmentedControlDemoState extends State<BasicSegmentedControlDemo> {
String? _selectedSegment = 'Daily';
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('基础分段选择器'),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CupertinoSlidingSegmentedControl<String>(
groupValue: _selectedSegment,
onValueChanged: (String? newValue) {
setState(() {
_selectedSegment = newValue;
});
},
children: const <String, Widget>{
'Daily': Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Text('日'),
),
'Weekly': Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Text('周'),
),
'Monthly': Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Text('月'),
),
},
),
const SizedBox(height: 20),
Text(
'当前选择: $_selectedSegment',
style: const TextStyle(fontSize: 18),
),
],
),
),
);
}
}
使用图标和文本混合作为选项
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class IconTextSegmentedControlDemo extends StatefulWidget {
const IconTextSegmentedControlDemo({super.key});
State<IconTextSegmentedControlDemo> createState() => _IconTextSegmentedControlDemoState();
}
class _IconTextSegmentedControlDemoState extends State<IconTextSegmentedControlDemo> {
int? _selectedMode = 0; // 0 for list, 1 for grid
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('图标文本分段选择器'),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CupertinoSlidingSegmentedControl<int>(
groupValue: _selectedMode,
onValueChanged: (int? newValue) {
setState(() {
_selectedMode = newValue;
});
},
children: const <int, Widget>{
0: Padding(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(CupertinoIcons.list_bullet),
SizedBox(width: 5),
Text('列表'),
],
),
),
1: Padding(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(CupertinoIcons.square_grid_2x2),
SizedBox(width: 5),
Text('网格'),
],
),
),
},
),
const SizedBox(height: 20),
Text(
'当前模式: ${_selectedMode == 0 ? '列表' : '网格'}',
style: const TextStyle(fontSize: 18),
),
],
),
),
);
}
}
自定义颜色和背景
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class CustomStyledSegmentedControlDemo extends StatefulWidget {
const CustomStyledSegmentedControlDemo({super.key});
State<CustomStyledSegmentedControlDemo> createState() => _CustomStyledSegmentedControlDemoState();
}
class _CustomStyledSegmentedControlDemoState extends State<CustomStyledSegmentedControlDemo> {
Color? _selectedColor = Colors.red; // 红色
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('自定义样式分段选择器'),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CupertinoSlidingSegmentedControl<Color>(
groupValue: _selectedColor,
onValueChanged: (Color? newValue) {
setState(() {
_selectedColor = newValue;
});
},
thumbColor: CupertinoColors.activeBlue, // 滑块颜色
backgroundColor: CupertinoColors.systemGrey5, // 背景颜色
children: <Color, Widget>{
Colors.red: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
child: Text(
'红色',
style: TextStyle(color: _selectedColor == Colors.red ? Colors.white : Colors.red),
),
),
Colors.green: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
child: Text(
'绿色',
style: TextStyle(color: _selectedColor == Colors.green ? Colors.white : Colors.green),
),
),
Colors.blue: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
child: Text(
'蓝色',
style: TextStyle(color: _selectedColor == Colors.blue ? Colors.white : Colors.blue),
),
),
},
),
const SizedBox(height: 20),
Container(
width: 100,
height: 100,
color: _selectedColor,
child: Center(
child: Text(
'选择的颜色',
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Colors.white),
),
),
),
],
),
),
);
}
}
注意点
- 状态管理:
CupertinoSlidingSegmentedControl是一个无状态的组件,它本身不保存选中状态。你需要通过groupValue属性传入当前选中的值,并通过onValueChanged回调函数来更新父组件的状态,从而实现UI的更新。 groupValue类型:groupValue和children的键(key)必须是相同类型。这意味着如果你在children中使用了String作为键,那么groupValue也必须是String类型。children的键值对:children属性是一个Map<T, Widget>,其中T是groupValue的类型,Widget是每个分段的UI内容。每个键必须是唯一的。onValueChanged不可为null: 如果onValueChanged为null,则控件将变为禁用状态,用户无法进行选择。这在需要展示但禁止用户交互时非常有用。- 内容宽度:
CupertinoSlidingSegmentedControl会尝试将其子组件(children中的Widget)的宽度均分。如果子组件内容的宽度差异较大,可能会导致视觉上的不对称。建议各个子组件的内容长度相似或使用Padding进行调整。 thumbColor与backgroundColor: 可以通过thumbColor自定义滑块的颜色,通过backgroundColor自定义选择器整体的背景颜色。这些颜色应与你的应用主题保持一致。- 文本颜色处理: 在示例三中可以看到,当使用自定义颜色时,需要根据当前选中状态手动调整子组件中文本的颜色,以确保选中项的文本颜色与滑块颜色形成对比,非选中项的文本颜色也清晰可见。
构造函数
const CupertinoSlidingSegmentedControl({
super.key,
required Map<T, Widget> children, // 必需,定义每个分段的内容
required ValueChanged<T?>? onValueChanged, // 必需,当选择值改变时触发
T? groupValue, // 当前选中的值
Color? thumbColor, // 滑块的颜色
Color? backgroundColor, // 分段控制的背景颜色
EdgeInsetsGeometry? padding, // 内边距
});
属性
| 属性名 | 属性类型 | 说明 |
|---|---|---|
children | Map<T, Widget> | 一个映射表,定义了分段选择器中的所有选项。键 T 必须与 groupValue 的类型相同,值 Widget 是每个分段的实际显示内容。 |
onValueChanged | ValueChanged<T?>? | 当用户选择不同的分段时触发的回调函数。参数 T? 代表新的选中值。如果为 null,控件将被禁用。 |
groupValue | T? | 当前分段选择器中被选中的值。这个值必须是 children 中某个键。如果为 null,则没有分段被选中。 |
thumbColor | Color? | 滑块的颜色。默认为 CupertinoColors.white。 |
backgroundColor | Color? | 分段选择器整体的背景颜色。默认为 CupertinoColors.systemFill(在暗模式下为 CupertinoColors.darkBackgroundGray)。 |
padding | EdgeInsetsGeometry? | 分段选择器内部的填充。默认为 EdgeInsets.all(2.0)。 |
关键属性解释
children: 这是定义分段选择器内容的核心属性。它是一个Map,其中每个key代表一个选项的标识符(可以是任何类型,但通常是String或int或enum),对应的value是一个Widget,用于显示该选项的UI。确保所有的key都是唯一的。onValueChanged: 这个回调函数是实现分段选择器交互的关键。当用户点击或滑动到某个分段时,onValueChanged会被调用,并传入新选中的分段的键。你需要在这个回调中调用setState来更新组件的状态,从而改变groupValue并触发UI的重建。如果此属性为 null,CupertinoSlidingSegmentedControl将处于禁用状态。groupValue: 它指明了当前哪个分段是“选中”状态。它的值必须与childrenMap中的某个键匹配。当groupValue发生变化时,CupertinoSlidingSegmentedControl会平滑地移动滑块到新的选中项。如果groupValue为null,则表示没有分段被选中,滑块将不可见(或停留在默认位置,具体取决于实现)。