BraveEvidence
BraveEvidence

Reputation: 85

How to achieve Flutter Checkbox Stepper?

I am trying to achieve the following layout

enter image description here

I tried using Stepper but it didn't really solved my issue so I tried using vanilla dart code itself

Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              Checkbox(value: true, onChanged: (value) {}),
              Text("Complaint Received"),
            ],
          ),
          Padding(
            padding: const EdgeInsets.only(
              left: 20,
            ),
            child: Container(
              padding: const EdgeInsets.all(0),
              width: 2,
              height: 80,
              color: Colors.black,
            ),
          ),
          Row(
            children: <Widget>[
              Checkbox(value: true, onChanged: (value) {}),
              Text("Complaint Received"),
            ],
          ),
          Container(
            width: 2,
            height: 10,
            color: Colors.black,
          ),
          Row(
            children: <Widget>[
              Checkbox(value: true, onChanged: (value) {}),
              Text("Complaint Received"),
            ],
          ),
          Container(
            width: 2,
            height: 10,
            color: Colors.black,
          ),
          Row(
            children: <Widget>[
              Checkbox(value: true, onChanged: (value) {}),
              Text("Complaint Received"),
            ],
          ),
          Container(
            width: 2,
            height: 10,
            color: Colors.black,
          ),
        ],
      ),

Following is the ui which I achieved

enter image description here

I don't understand why there is padding between the first two row and the container

Upvotes: 2

Views: 1644

Answers (1)

Egor
Egor

Reputation: 10334

In Flutter, Checkboxes are not pure widgets like the StatelessWidget that we are used to.

Checkbox draws its bounds, a box with rounded edges, and the 'check' mark itself through RenderObject's paint method, sort of like HTML5 Canvas (if you are familiar).

This fact makes it hard to customize Flutter checkboxes, even changing such trivial stuff like the padding around it.


My solution does not solve your problem in the way you tried to, but it does provide a great view just like the one you wanted to achieve.

final double barsHeight = 40.0;
final double barsWidth = 3.0;
final Color inactiveBarColor = Colors.grey;
final Color activeBarColor = Colors.blue;

Map<String, bool> steps = {
  "Complaint Received": true,
  "Engineer Assigned": true,
  "Engineer On the way": false,
  "Complaint Done": false,
};

@override
Widget build(BuildContext context) {
  double rowPadding = (barsHeight - kRadialReactionRadius) / 2;
  double _barHeight = barsHeight;
  if (rowPadding < 0) {
    rowPadding = 0;
    _barHeight = kRadialReactionRadius;
  }
  return Padding(
    padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
    child: Stack(fit: StackFit.loose, children: <Widget>[

      Positioned(
        left: kRadialReactionRadius - barsWidth / 2,
        top: kRadialReactionRadius + rowPadding,
        bottom: kRadialReactionRadius + rowPadding,
        width: barsWidth,
        child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: List.generate(steps.length - 1, (i) =>
          Container(
            margin: EdgeInsets.symmetric(vertical: kRadialReactionRadius / 2 - 2),
            height: _barHeight + 4,
            color: steps.values.elementAt(i) && steps.values.elementAt(i + 1) ? activeBarColor : inactiveBarColor,
          )
        ))
      ),
      Theme(
        data: Theme.of(context).copyWith(disabledColor: inactiveBarColor, unselectedWidgetColor: inactiveBarColor),
        child: Column(mainAxisSize: MainAxisSize.min, children: steps.keys.map((key) =>
          Padding(
            padding: EdgeInsets.symmetric(vertical: rowPadding),
            child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[
              Checkbox(
                value: steps[key],
                onChanged: steps[key] ? (_) {} : null,
                activeColor: activeBarColor,
                materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
              ),
              Text(key),
            ])
          )
        ).toList())
      )

    ]),
  );
}

How it works, in short:

  • Checkbox uses kRadialReactionRadius constant for its size and padding. That's how we know its exact size and now we can draw our bars in-between each checkbox row.
  • Vertical bars are drawn underneath the Column with Checkboxes using Stack.
  • Checkboxes default border color is overridden by Theme widget.

To change checkbox state - simply modify values in steps variable. e.g.

setState(() {
  steps[steps.keys.elementAt(/*checkbox index*/)] = true;
});

Final result:

final result

Let me know if this helped.

Upvotes: 4

Related Questions