大多数时候,填写有很多字段的表格会很麻烦,这可能会使用户不愿意完成这个过程。这就是多步骤表格发挥作用的地方。
多步骤表单正如其名称所暗示的那样:多步骤表单是将一个长的表单分解成短的部分。它们为您的应用程序的用户提供了一个不太令人生畏的体验。
Flutter预装了一个Stepper部件,使我们有能力将我们的表单分解成一个个步骤。在这篇文章中,我们将探讨什么是Stepper部件,以及如何在Flutter中应用它来构建多步骤表单以提高用户体验。您还将学习如何定制Stepper小组件以满足您的移动应用程序的规格。
步进器小部件的属性
这些是Stepper小组件的一些基本属性。我们也会通过演示来展示它们,但在开始之前,你可以在这里回顾一下它们:
- 类型(
StepperType.horizontal或StepperType.vertical)--这决定了方向和每个步骤将如何放置,相对于彼此而言 - 步骤 - 步进器的步骤,其标题、字幕和图标总是可见的。下面是一个我们将用于演示的步骤的例子。

currentStep- 步骤的索引值(0、1、2等)。定义了表格中的活动步骤onStepContinue()- 一个回调--当--继续的按钮,进入下一个步骤onStepCancel()- 一个回调-取消按钮,移动到上一个步骤。onStepTapped(int index) - 当用户点击步骤时的回调,以移动到正在进行的选定的步骤。该回调也为我们提供了用户点击的步骤的索引。List<Step>- 一个步长的步骤,其标题和内容会在各自的步骤中显示出来。Active
为了进一步深入了解,一个步骤本身的属性是:
title- 使用此属性来命名该步骤。这个属性是一个必需的属性,接受一个小部件作为值,通常是一个文本小部件subtitle- 使用这个属性为步骤添加一个副标题。它接受一个小部件作为值,通常是一个文本小部件。content- 我们将使用此属性来为该步骤提供内容。它是一个必需的属性,接受任何小组件作为一个值state- 使用这个属性来设置步骤的状态,如 , , , , 或 。根据这个状态,步骤的图标会发生变化。completeddisablededitingindexederrorisActive- 使用此属性来指示步骤是活动还是不活动。它接受一个布尔值作为值
在Flutter中创建一个多步骤表单
现在,让我们创建一个新的Flutter项目,展示如何应用Stepper widget。
我们的表单的初始状态看起来就像我们下面的图片。然而,我们将使用步进器部件将其分成多个步骤,这样用户就不会被他们必须填写的字段数量所淹没。

我们有可重复使用的自定义部件,我们已经创建了这些部件。
我们有我们的main.dart file ,它有以下内容:
import 'package:flutter/material.dart';
import 'package:stepper_widget/form_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const FormPage(),
);
}
}
我们的FormPage widget容纳了这些内容:
import 'package:flutter/material.dart';
import 'package:stepper_widget/widgets/custom_button.dart';
import 'package:stepper_widget/widgets/custom_input.dart';
class FormPage extends StatefulWidget {
const FormPage({Key? key}) : super(key: key);
@override
_FormPageState createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text(
"Stepper Widget ",
),
centerTitle: true,
),
body: Container(
padding: const EdgeInsets.all(20),
child: ListView(
children: [
const CustomInput(
hint: "First Name",
inputBorder: OutlineInputBorder(),
),
const CustomInput(
hint: "Last Name",
inputBorder: OutlineInputBorder(),
),
const CustomInput(
hint: "Address",
inputBorder: OutlineInputBorder(),
),
const CustomInput(
hint: "City and State",
inputBorder: OutlineInputBorder(),
),
const CustomInput(
hint: "Bio",
inputBorder: OutlineInputBorder(),
),
const CustomInput(
hint: "Bio",
inputBorder: OutlineInputBorder(),
),
CustomBtn(
title: const Text(
"Save",
style: TextStyle(color: Colors.white),
),
callback: () {},
)
],
),
),
),
);
}
}
我们的CustomInput :
import 'package:flutter/material.dart';
class CustomInput extends StatelessWidget {
final ValueChanged<String>? onChanged;
final String? hint;
final InputBorder? inputBorder;
const CustomInput({Key? key, this.onChanged, this.hint, this.inputBorder})
: super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 10),
child: TextField(
onChanged: (v) => onChanged!(v),
decoration: InputDecoration(hintText: hint!, border: inputBorder),
),
);
}
}
最后是我们的自定义按钮,CustomBtn :
import 'package:flutter/material.dart';
class CustomBtn extends StatelessWidget {
final Function? callback;
final Widget? title;
CustomBtn({Key? key, this.title, this.callback}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 10),
child: SizedBox(
width: double.infinity,
child: Container(
color: Colors.blue,
child: TextButton(
onPressed: () => callback!(),
child: title!,
),
),
),
);
}
}
为我们的表格创建步骤
为了使用我们的Stepper小组件,我们将从创建一个步骤列表开始。
因为我们已经定义了步进部件的一些重要功能,所以我们在这里就不多说了。我们可以直接跳入。
List<Step> getSteps() {
return <Step>[
Step(
state: currentStep > 0 ? StepState.complete : StepState.indexed,
isActive: currentStep >= 0,
title: const Text("Account Info"),
content: Column(
children: const [
CustomInput(
hint: "First Name",
inputBorder: OutlineInputBorder(),
),
CustomInput(
hint: "Last Name",
inputBorder: OutlineInputBorder(),
),
],
),
),
Step(
state: currentStep > 1 ? StepState.complete : StepState.indexed,
isActive: currentStep >= 1,
title: const Text("Address"),
content: Column(
children: const [
CustomInput(
hint: "City and State",
inputBorder: OutlineInputBorder(),
),
CustomInput(
hint: "Postal Code",
inputBorder: OutlineInputBorder(),
),
],
),
),
Step(
state: currentStep > 2 ? StepState.complete : StepState.indexed,
isActive: currentStep >= 2,
title: const Text("Misc"),
content: Column(
children: const [
CustomInput(
hint: "Bio",
inputBorder: OutlineInputBorder(),
),
],
),
),
];
}
在创建了我们的步进小部件所需的步骤后,我们现在可以在我们的formpage.dart 中替换ListView 。但在此之前,让我们看看我们的单步的每个字段代表什么。
我们对单步的第一个属性是state :你可能记得,这定义了步进器上的领先图标,如下图所示。当用户完成了对某一步骤的字段的编辑并移动到下一步骤时,前一步骤在state 属性中被标记为已完成,而当前步骤被标记为indexed ,这仅仅意味着用户正在积极编辑这一步骤。

isActive 属性只是用来显示用户目前正在查看哪一个步骤。title 收录了一个小部件,并将其显示在每个步骤的顶部,而每个步骤的内容是我们希望用户与之互动的实际表单小部件。
在用Stepper 替换了我们之前的Listview 小部件后,我们的代码看起来是这样的。
import 'package:flutter/material.dart';
import 'package:stepper_widget/widgets/custom_input.dart';
class FormPage extends StatefulWidget {
const FormPage({Key? key}) : super(key: key);
@override
_FormPageState createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
int currentStep = 0;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text(
"Stepper Widget ",
),
centerTitle: true,
),
body: Container(
padding: const EdgeInsets.all(20),
child: Stepper(
type: StepperType.horizontal,
currentStep: currentStep,
onStepCancel: () => currentStep == 0
? null
: setState(() {
currentStep -= 1;
}),
onStepContinue: () {
bool isLastStep = (currentStep == getSteps().length - 1);
if (isLastStep) {
//Do something with this information
} else {
setState(() {
currentStep += 1;
});
}
},
onStepTapped: (step) => setState(() {
currentStep = step;
}),
steps: getSteps(),
)),
),
);
}
List<Step> getSteps() {
return <Step>[
Step(
state: currentStep > 0 ? StepState.complete : StepState.indexed,
isActive: currentStep >= 0,
title: const Text("Account Info"),
content: Column(
children: const [
CustomInput(
hint: "First Name",
inputBorder: OutlineInputBorder(),
),
CustomInput(
hint: "Last Name",
inputBorder: OutlineInputBorder(),
),
],
),
),
Step(
state: currentStep > 1 ? StepState.complete : StepState.indexed,
isActive: currentStep >= 1,
title: const Text("Address"),
content: Column(
children: const [
CustomInput(
hint: "City and State",
inputBorder: OutlineInputBorder(),
),
CustomInput(
hint: "Postal Code",
inputBorder: OutlineInputBorder(),
),
],
),
),
Step(
state: currentStep > 2 ? StepState.complete : StepState.indexed,
isActive: currentStep >= 2,
title: const Text("Misc"),
content: Column(
children: const [
CustomInput(
hint: "Bio",
inputBorder: OutlineInputBorder(),
),
],
),
),
];
}
}
了解阶梯小部件
现在让我们来看看我们在Stepper小组件中定义的每个属性。
从type 开始,我们定义了我们的步进小部件的内容应该如何在步进器内布局。对于垂直和水平的步进类型,我们的步进小部件会是这样的。


CurrentStep 简单地接收用户当前可见的步骤的索引值。
OnStepCancel 是当我们的表单的用户点击后退按钮时实施的回调,我们目前正在做一个检查,以防止按钮在第一步就被激活。
onStepContinue 是我们的继续按钮的回调。在这里我们也有一个检查,以了解用户在最后一步的情况,在这里我们可以使用提供给我们的信息进行必要的操作。
OnStepTapped 返回用户点击的步骤,我们可以通过将其设置为当前步骤的值来使其激活。
其他可以为我们的Stepper部件提供更多的自定义功能,包括添加一个自定义主题或实现我们自己的自定义控制按钮,也就是我们目前的Next和Continue按钮。要做到这一点,只需使用我们的Stepper widget的theme 属性和controlsBuilder 属性。
最后的想法
使用多步骤表单可以大大改善用户体验,增加我们设计的视觉吸引力。
Flutter中的Stepper小组件在需要的情况下,为我们提供了很多有用的自定义功能,在不依赖第三方库的情况下获得我们想要的结果。