Horizontal Stepper in Flutter
Asked Answered
S

7

7

I want to create a horizontal stepper, which is easy I know, but this time, the count of steps should large.

Just to give an example, this is what I am doing for the vertical,

import 'package:flutter/material.dart';


void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new HomePage(),
    );
  }
}

class HomePage extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body:  Container(
          margin: EdgeInsets.symmetric(vertical: 20.0),
          child: new ListView(
        children: <Widget>[
          new Text("Helllo "),
          new Text( " Welcome"),
          new Text (" Yaaa0"),
          new SimpleWidget(),
        ],
      ), ),
    );
  }
}





class SimpleWidget extends StatefulWidget {
  @override
  SimpleWidgetState createState() => new SimpleWidgetState();
}

class SimpleWidgetState extends State<SimpleWidget> {
  int stepCounter = 0;


  List<Step> steps = [];

   @override
  void initState() {
    prepareState();
    super.initState();
  }
  void prepareState(){
    for (var i= 0; i<100; i++){
      var stepVal = new Step(
      title:new Text("Step $i"),
      content: new Text("This is the child of $i step"),
      isActive: true,
    );
      steps.add(stepVal);

    }
  }
  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new Stepper(
        type: StepperType.vertical,
        physics : ClampingScrollPhysics(),
        currentStep: this.stepCounter,
        steps: steps,
        onStepTapped: (step) {
          setState(() {
            stepCounter = step;
          });
        },
        onStepCancel: () {
          setState(() {
            stepCounter > 0 ? stepCounter -= 1 : stepCounter = 0;
          });
        },
        onStepContinue: () {
          setState(() {
            stepCounter < steps.length - 1 ? stepCounter += 1 : stepCounter = 0;
          });
        },
      ),
    );
  }
}

As soon as I try to recreate this in the horizontal mode, it shows nothing. I have tried to make the listView horizontal, I have tried to make the stepper horizontal, both individually and also together. None works. You can try that in the dartpad.

My question : 1. How to make a Stepper in horizontal that is scrollable in the horizontal mode. 2. The content of the Stepper is scrollable , I can see that. Can it be switched off?

Skewback answered 26/12, 2019 at 6:22 Comment(0)
A
8

enter image description here

use this class

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';

class StepProgressView extends StatelessWidget {
  final double _width;

  final List<String> _titles;
  final int _curStep;
  final Color _activeColor;
  final Color _inactiveColor = HexColor("#E6EEF3");
  final double lineWidth = 3.0;

  StepProgressView(
      {Key key,
      @required int curStep,
      List<String> titles,
      @required double width,
      @required Color color})
      : _titles = titles,
        _curStep = curStep,
        _width = width,
        _activeColor = color,
        assert(width > 0),
        super(key: key);

  Widget build(BuildContext context) {
    return Container(
        width: this._width,
        child: Column(
          children: <Widget>[
            Row(
              children: _iconViews(),
            ),
            SizedBox(
              height: 8,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: _titleViews(),
            ),
          ],
        ));
  }

  List<Widget> _iconViews() {
    var list = <Widget>[];
    _titles.asMap().forEach((i, icon) {
      var circleColor = (i == 0 || _curStep > i + 1) ? _activeColor : _inactiveColor;
      var lineColor = _curStep > i + 1 ? _activeColor : _inactiveColor;
      var iconColor = (i == 0 || _curStep > i + 1) ? _activeColor : _inactiveColor;

      list.add(
        Container(
          width: 20.0,
          height: 20.0,
          padding: EdgeInsets.all(0),
          decoration: new BoxDecoration(
            /* color: circleColor,*/
            borderRadius: new BorderRadius.all(new Radius.circular(22.0)),
            border: new Border.all(
              color: circleColor,
              width: 2.0,
            ),
          ),
          child: Icon(
            Icons.circle,
            color: iconColor,
            size: 12.0,
          ),
        ),
      );

      //line between icons
      if (i != _titles.length - 1) {
        list.add(Expanded(
            child: Container(
          height: lineWidth,
          color: lineColor,
        )));
      }
    });

    return list;
  }

  List<Widget> _titleViews() {
    var list = <Widget>[];
    _titles.asMap().forEach((i, text) {
      list.add(Text(text, style: TextStyle(color: HexColor("#000000"))));
    });
    return list;
  }
}

declare list and int variable inside class you want to use

final List<String> titles = [TextConstant.CART, TextConstant.ADDRESS, TextConstant.PAYMENT];
  int _curStep = 1;

finally use above class

StepProgressView(width: MediaQuery.of(context).size.width,
curStep: _curStep,
color: Color(0xff50AC02),
titles: titles),
Alamein answered 29/3, 2022 at 5:41 Comment(0)
V
3

You can create Horizontal Stepper in Flutter without any external package also like by following This will work fine and use StatefulWidget to put this code inside it (StatefulWidget).

    int _currentStep = 0;
    Widget build(BuildContext context) {
    return Container(
          child: Column(
            children: [
              Expanded(
                child: Stepper(
                  type: StepperType.horizontal,
                  physics: ScrollPhysics(),
                  currentStep: _currentStep,
                  onStepTapped: (step) => tapped(step),
                  onStepContinue:  continued,
                  onStepCancel: cancel,
                  steps: <Step>[
                     Step(
                      title: new Text(''),
                      content: Column(
                        children: <Widget>[
                          TextFormField(
                            decoration: InputDecoration(labelText: 'Email Address'),
                          ),
                          TextFormField(
                            decoration: InputDecoration(labelText: 'Password'),
                          ),
                        ],
                      ),
                      isActive: _currentStep >= 0,
                      state: _currentStep >= 0 ?
                      StepState.complete : StepState.disabled,
                    ),
                     Step(
                      title: new Text(''),
                      content: Column(
                        children: <Widget>[
                          TextFormField(
                            decoration: InputDecoration(labelText: 'Home Address'),
                          ),
                          TextFormField(
                            decoration: InputDecoration(labelText: 'Postcode'),
                          ),
                        ],
                      ),
                      isActive: _currentStep >= 0,
                      state: _currentStep >= 1 ?
                      StepState.complete : StepState.disabled,
                    ),
                     Step(
                      title: new Text(''),
                      content: Column(
                        children: <Widget>[
                          TextFormField(
                            decoration: InputDecoration(labelText: 'Mobile Number'),
                          ),
                        ],
                      ),
                      isActive:_currentStep >= 0,
                      state: _currentStep >= 2 ?
                      StepState.complete : StepState.disabled,
                    ),
                    Step(
                      title: new Text(''),
                      content: Column(
                        children: <Widget>[
                          TextFormField(
                            decoration: InputDecoration(labelText: 'Mobile Number'),
                          ),
                        ],
                      ),
                      isActive:_currentStep >= 0,
                      state: _currentStep >= 3 ?
                      StepState.complete : StepState.disabled,
                    ),
                  ],
                ),
              ),
            ],
          ),
        );
        

  
  }
  

  tapped(int step){
    setState(() => _currentStep = step);
  }

  continued(){
    _currentStep < 3 ?
        setState(() => _currentStep += 1): null;
  }
  cancel(){
    _currentStep > 0 ?
        setState(() => _currentStep -= 1) : null;
  }
Vaunting answered 27/4, 2021 at 4:35 Comment(0)
H
2

try this example, e.g: conf pubspec file: fa_stepper: ^0.0.2, then flutter packages get , after that: using FAStepper constructor, define something like this:

Widget w1(BuildContext context) {
    return Scaffold(      
      // Body
      body: Container(
          child: FAStepper(
        // physics: ClampingScrollPhysics(),
        // Using a variable here for handling the currentStep
        currentStep: this.currentStep,
        // List the steps you would like to have
        titleHeight: 120,
        steps: mySteps,
        // Define the type of Stepper style
        // StepperType.horizontal :  Horizontal Style
        // StepperType.vertical   :  Vertical Style
        type: FAStepperType.horizontal,
        titleIconArrange: FAStepperTitleIconArrange.column,
        stepNumberColor: Colors.pinkAccent,
        // Know the step that is tapped
        onStepTapped: (step) {
          // On hitting step itself, change the state and jump to that step
          setState(() {
            // update the variable handling the current step value
            // jump to the tapped step
            currentStep = step;
          });
          // Log function call
          print("onStepTapped : " + step.toString());
        },
        onStepCancel: () {
          // On hitting cancel button, change the state
          setState(() {
            // update the variable handling the current step value
            // going back one step i.e subtracting 1, until its 0
            if (currentStep > 0) {
              currentStep = currentStep - 1;
            } else {
              currentStep = 0;
            }
          });
          // Log function call
          print("onStepCancel : " + currentStep.toString());
        },
        // On hitting continue button, change the state
        onStepContinue: () {
          setState(() {
            // update the variable handling the current step value
            // going back one step i.e adding 1, until its the length of the step
            if (currentStep < mySteps.length - 1) {
              currentStep = currentStep + 1;
            } else {
              currentStep = 0;
            }
          });
          // Log function call
          print("onStepContinue : " + currentStep.toString());
        },
      )),
    );
  }
Hiedihiemal answered 20/1, 2020 at 16:0 Comment(0)
P
1

Wrap the stepper with a ConstrainedBox and set its height to a constant and make the StepperType as horizontal. You can check it in dartpad .

        return ConstrainedBox(
          constraints: BoxConstraints.tightFor(height: 500.0),
          child: Stepper(
              type: StepperType.horizontal,
            ),
        );
Philps answered 26/12, 2019 at 7:35 Comment(1)
I did that and it only it gave the same error. The following assertion was thrown during layout: A RenderFlex overflowed by 7688 pixels on the right. Widget creation tracking is currently disabled. Enabling it enables improved error messages.Skewback
A
0

There is an issue about this on github https://github.com/flutter/flutter/issues/40601

BUT

This is what i m using right now

output image

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme:ThemeData(
      primarySwatch:Colors.amber
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}




class CustomStep {
  final String title;
  final Widget page;
  CustomStep(
      {@required this.title, @required this.page});
}


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

  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  ScrollController _scrollController = new ScrollController();
  static const double STEP_WIDTH = 90;
  PageController pageController = PageController();
  List<CustomStep> stepsList;
  int currentPage=0;
 @override
  void initState() {
    super.initState();
    stepsList = [
      CustomStep(
        title: 'ddddd',
        page: Placeholder(
          color: Colors.pink,
        ),
      ),
      CustomStep(
        title: 'zzzzzzzz',
        page: Placeholder(
          color: Colors.deepPurple,
        ),
      ),
    ];
  }

  SizedBox buildStepDivider(int index) {
    return SizedBox(
      height: 90,
      child: Container(
        alignment: Alignment.topCenter,
        child: Transform.translate(
          offset: Offset(0, 16),
          child: Container(
            color: index < currentPage
                ? Theme.of(context).primaryColor
                : Colors.grey,
            width: 30,
            height: 3,
            padding: EdgeInsets.symmetric(horizontal: 10),
          ),
        ),
      ),
    );
  }


  buildStep(int index) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 5),
      child: SizedBox(
        height: 90,
        width: STEP_WIDTH,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Container(
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                color: index <= currentPage
                    ? Theme.of(context).primaryColor
                    : Colors.grey[300],
              ),
              padding: EdgeInsets.all(10),
              child: Text((index + 1).toString()),
            ),
            Expanded(
                child: Text(
              stepsList[index].title,
              textAlign: TextAlign.center,
            ))
          ],
        ),
      ),
    );
  }

  _buildStepper(int currentStep) {
    Future.delayed(
        Duration(milliseconds: 100),
        () => _scrollController.animateTo((STEP_WIDTH * currentStep).toDouble(),
            duration: const Duration(milliseconds: 300),
            curve: Curves.easeOut));
    return Center(
      child: SizedBox(
        height: 110,
        child: ListView.builder(
            controller: _scrollController,
            shrinkWrap: true,
            scrollDirection: Axis.horizontal,
            itemCount: stepsList.length,
            itemBuilder: (ctx, index) => index < stepsList.length - 1
                ? Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      buildStep(index),
                      buildStepDivider(index)
                    ],
                  )
                :Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      buildStep(index)]) ),
      ),
    );
  }



  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('hello'), centerTitle: true),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          _buildStepper(currentPage),
          Expanded(
              child: PageView.builder(
            controller: pageController,
            physics: NeverScrollableScrollPhysics(),
            onPageChanged: (index) {
              setState(() {
                currentPage = index;
              });
            },
            itemCount: stepsList.length,
            itemBuilder: (ctx, index) => 
                     stepsList[index].page,
          )),
        ],
      ),
    );
  }

}
Allysonalma answered 23/5, 2020 at 5:19 Comment(0)
V
0

I am sure you have got the answer, but maybe this is for someone who is looking for a package instead of creating a custom one. Here is something that I found good, please do check out and see if it fits in your use-case.

https://pub.dev/packages/im_stepper

Vowell answered 25/2, 2021 at 17:7 Comment(0)
S
0

A very easy step to create a number stepper is

 Container(
                    margin: const EdgeInsets.only(top: 4, right: 6),
                    padding: const EdgeInsets.all(3.0),
                    decoration: BoxDecoration(
                      border: Border.all(color: Colors.red, width: 2),
                      borderRadius: BorderRadius.circular(2),
                    ),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        InkWell(
                          child: Icon(Icons.remove, color: Colors.red),
                          onTap: _dicrement,
                        ),
                        Container(
                          margin: EdgeInsets.only(right: 8, left: 8),
                          child: Text(
                            _currentCount.toString(),
                            style: TextStyle(fontWeight: FontWeight.bold),
                          ),
                        ),
                        InkWell(
                          child: Icon(Icons.add, color: Colors.red),
                          onTap: _increment,
                        ),
                      ],
                    ),
                  ),
Stilliform answered 29/11, 2021 at 7:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.