Flutter 之 Stepper

Stepper 是 flutter 提供的步骤选择器

1. Stepper

1.1 Stepper 介绍

Stepper 定义

  const Stepper({
    Key? key,
    required this.steps,
    this.physics,
    this.type = StepperType.vertical,
    this.currentStep = 0,
    this.onStepTapped,
    this.onStepContinue,
    this.onStepCancel,
    this.controlsBuilder,
    this.elevation,
    this.margin,
  }) 

Stepper 属性

Stepper 属性介绍
steps@required List<Step>
physics滑动的物理效果
typeStepper 类型,分为横向与纵向两种,默认为 StepperType.vertical
currentStep当前 step,默认为 0
onStepTappedstep 点击回调函数
onStepContinueNext 按钮点击回调函数
onStepCancelCancel 按钮点击回调函数
controlsBuilder内容下方按钮构建函数
elevation阴影 仅在Stepper的type为横向时有效
margin外边距 仅在Stepper的type为垂直时有效

1.2 Step 介绍

Step 定义

  const Step({
    required this.title,
    this.subtitle,
    required this.content,
    this.state = StepState.indexed,
    this.isActive = false,
  })

Step 属性

Step 属性介绍
title@required 标题控件
subtitle副标题控件
content@required 内容控件
state当前 step 的状态,StepState 会改变每一个 step 的图标,默认为 StepState.indexed
isActive是否激活状态,默认为 false,isActive == true 时会变成蓝色

2. Stepper 示例

2.1 垂直方向Stepper


class MSStepperDemo extends StatefulWidget {
  const MSStepperDemo({Key? key}) : super(key: key);

  @override
  State<MSStepperDemo> createState() => _MSStepperDemoState();
}

class _MSStepperDemoState extends State<MSStepperDemo> {
  List<MSStepModel> _datas = [];
  int _currentStep = 0;
  @override
  void initState() {
    _datas.add(MSStepModel(0, "第一步", "我们先进行第一步"));
    _datas.add(MSStepModel(1, "第二步", "我们先进行第二步"));
    _datas.add(MSStepModel(2, "第三步", "我们先进行第三步"));
    _datas.add(MSStepModel(3, "第四步", "我们先进行第四步"));
    _datas.add(MSStepModel(4, "第五步", "我们先进行第五步"));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("StepperDemo")),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Stepper(
          currentStep: _currentStep, // 当前步骤
          type: StepperType
              .vertical, // Stepper 类型,分为横向与纵向两种 默认StepperType.vertical
          steps: _getSteps(), // 步骤
          //margin: EdgeInsets.all(10), // 自定义外边距 仅在垂直方向 有效
          // 继续按钮点击回调函数
          onStepContinue: () {
            if (_currentStep < _datas.length - 1) {
              _currentStep++;
              setState(() {});
            }
          },
          // step 点击回调函数
          onStepTapped: (index) {
            _currentStep = index;
            setState(() {});
          },
          onStepCancel: () {
            if (_currentStep > 0) {
              _currentStep--;
              setState(() {});
            }
          },
        ),
      ),
    );
  }

  List<Step> _getSteps() {
    List<Step> steps = [];
    _datas.forEach((element) {
      // 我们根据当前 step 进行区分,
      // 当前 step 之前的认为 StepState.complete,
      // 当前认为 StepState.editing,
      // 之后认为 StepState.indexed
      if (element.index < _currentStep) {
        element.state = StepState.complete;
      } else if (element.index == _currentStep) {
        element.state = StepState.editing;
      } else {
        element.state = StepState.indexed;
      }
      steps.add(
        Step(
          // Step Title
          title: Row(
            children: [
              Icon(Icons.title),
              Text(element.title),
            ],
          ),
          // Step Content
          content: Text(element.content),
          // 是否激活状态,true 时会变成蓝色
          isActive: (_currentStep == _datas.indexOf(element)),
          state: element.state,
        ),
      );
    });
    return steps;
  }
}

class MSStepModel {
  MSStepModel(this.index, this.title, this.content);
  int index;
  String title;
  String content;
  StepState state = StepState.indexed;
}

2.2 水平方向Stepper


class MSStepperDemo2 extends StatefulWidget {
  const MSStepperDemo2({Key? key}) : super(key: key);

  @override
  State<MSStepperDemo2> createState() => _MSStepperDemo2State();
}

class _MSStepperDemo2State extends State<MSStepperDemo2> {
  List<MSStepModel> _datas = [];
  int _currentStep = 0;
  @override
  void initState() {
    _datas.add(MSStepModel(0, "第一步", "我们先进行第一步"));
    _datas.add(MSStepModel(1, "第二步", "我们先进行第二步"));
    _datas.add(MSStepModel(2, "第三步", "我们先进行第三步"));
    _datas.add(MSStepModel(3, "第四步", "我们先进行第四步"));
    _datas.add(MSStepModel(4, "第五步", "我们先进行第五步"));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("SteperDemo2")),
      body: Padding(
        padding: EdgeInsets.all(8),
        child: SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Container(
            width: 600,
            child: Stepper(
              currentStep: _currentStep, // 当前 Step
              steps: _getSteps(), // Steps
              type: StepperType.horizontal,
              onStepTapped: (index) {
                _currentStep = index;
                setState(() {});
              },
              onStepContinue: () {
                if (_currentStep < _datas.length - 1) {
                  _currentStep++;
                  setState(() {});
                }
              },
              onStepCancel: () {
                if (_currentStep > 0) {
                  _currentStep--;
                  setState(() {});
                }
              },
            ),
          ),
        ),
      ),
    );
  }

  List<Step> _getSteps() {
    List<Step> steps = [];
    _datas.forEach((element) {
      if (element.index < _currentStep) {
        element.state = StepState.complete;
      } else if (element.index == _currentStep) {
        element.state = StepState.editing;
      } else {
        element.state = StepState.indexed;
      }
      steps.add(
        Step(
          title: Row(
            children: [
              Icon(Icons.title),
              Text(element.title),
            ],
          ),
          content: Text(element.content),
          isActive:
              _currentStep == _datas.indexOf(element), // 是否激活状态,true 时会变成蓝色
          state: element.state, // Step Status
        ),
      );
    });
    return steps;
  }
}

class MSStepModel {
  MSStepModel(this.index, this.title, this.content);
  int index;
  String title;
  String content;
  StepState state = StepState.indexed;
}

 

注意:横向的时候如果内容过多会溢出,这个是跟纵向有区别的,这时候要么减少内容,要么使用滑动组件嵌套,但还还需要嵌套一个已知宽度的父空间

2.3 controlsBuilder


class MSStepperDemo3 extends StatefulWidget {
  const MSStepperDemo3({Key? key}) : super(key: key);

  @override
  State<MSStepperDemo3> createState() => _MSStepperDemo3State();
}

class _MSStepperDemo3State extends State<MSStepperDemo3> {
  List<MSStepModel> _datas = [];
  int _currentStep = 0;
  @override
  void initState() {
    _datas.add(MSStepModel(0, "第一步", "我们先进行第一步"));
    _datas.add(MSStepModel(1, "第二步", "我们先进行第二步"));
    _datas.add(MSStepModel(2, "第三步", "我们先进行第三步"));
    _datas.add(MSStepModel(3, "第四步", "我们先进行第四步"));
    _datas.add(MSStepModel(4, "第五步", "我们先进行第五步"));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("SteperDemo3")),
      body: Padding(
        padding: EdgeInsets.all(8),
        child: Stepper(
          currentStep: _currentStep, // 当前 Step
          steps: _getSteps(), // Steps
          type: StepperType.vertical,
          controlsBuilder: (ctx, details) {
            return Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                    onPressed: details.onStepContinue, child: Text("下一步")),
                SizedBox(width: 20),
                ElevatedButton(
                    onPressed: details.onStepCancel, child: Text("上一步")),
              ],
            );
          },
          onStepTapped: (index) {
            _currentStep = index;
            setState(() {});
          },
          onStepContinue: () {
            if (_currentStep < _datas.length - 1) {
              _currentStep++;
              setState(() {});
            }
          },
          onStepCancel: () {
            if (_currentStep > 0) {
              _currentStep--;
              setState(() {});
            }
          },
        ),
      ),
    );
  }

  List<Step> _getSteps() {
    List<Step> steps = [];
    _datas.forEach((element) {
      if (element.index < _currentStep) {
        element.state = StepState.complete;
      } else if (element.index == _currentStep) {
        element.state = StepState.editing;
      } else {
        element.state = StepState.indexed;
      }
      steps.add(
        Step(
          title: Row(
            children: [
              Icon(Icons.title),
              Text(element.title),
            ],
          ),
          content: Text(element.content),
          isActive:
              _currentStep == _datas.indexOf(element), // 是否激活状态,true 时会变成蓝色
          state: element.state, // Step Status
        ),
      );
    });
    return steps;
  }
}

class MSStepModel {
  MSStepModel(this.index, this.title, this.content);
  int index;
  String title;
  String content;
  StepState state = StepState.indexed;
}

 

3. 技术小结

  • 第一步,我们既然要做一个 Stepper,我们要准备一套数据模型,并且模型包含了状态,标题,内容,下标,等各个元素,然后将这些数据构造出来当做数据源。
  • 第二步,我们创建 Stepper,并对一些属性进行设置。
  • 第三步,我们创建 steps,这里很重要,要根据我们第一步中的数据源,将每一个 model 构造成对应的 Step 控件。
  • 第四步,我们在创建 steps 时,对 model 的状态进行管理,设置编辑中,已完成,未完成等不同状态。
  • 第五步,我们对 Stepper 的交互事件进行处理,点击时将 currentStep 设置到对应 index,cancel 时我们执行 currentStep -1 操作,Next 时我们执行 currentStep +1 操作,此处注意防止越界。



作者:maskerII
链接:https://www.jianshu.com/p/d9e46a6f3e4c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值