Debanjan Chakraborty
Debanjan Chakraborty

Reputation: 584

Horizontal Stepper in Flutter

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?

Upvotes: 7

Views: 27087

Answers (7)

adarsh
adarsh

Reputation: 531

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),

Upvotes: 8

Anandh Krishnan
Anandh Krishnan

Reputation: 5984

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,
                        ),
                      ],
                    ),
                  ),

Upvotes: 0

Prathamesh Doshi
Prathamesh Doshi

Reputation: 153

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;
  }

Upvotes: 3

Tilak Dewangan
Tilak Dewangan

Reputation: 361

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

Upvotes: 0

dipnv
dipnv

Reputation: 81

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());
        },
      )),
    );
  }

Upvotes: 2

Guru Prasad mohapatra
Guru Prasad mohapatra

Reputation: 1969

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,
            ),
        );

Upvotes: 1

Red Bayoub
Red Bayoub

Reputation: 11

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,
          )),
        ],
      ),
    );
  }

}

Upvotes: 0

Related Questions