如何确保步进标题不会在onTap上消失?

时间:2019-05-31 09:13:21

标签: scroll flutter scroller stepper

我正在实现一个步进器,其中的内容显示选择列表。根据步骤在屏幕上的位置,滚动条会将标题移出视图,仅在点击时显示列表的一部分。从UX的角度来看,这是不合适的。

我已经尝试按照小部件注释中的建议将物理更改为ClampingScrollPhysics(),但这不能解决问题。下面是该程序的简化版本,可帮助其他人重新创建-只需粘贴到flutter演示项目中即可。

我还更新了flutter:-)

_MyHomePageState类扩展状态{

 static YourAge yourAge = YourAge();

static Widget _ageSelector = CustomSelectorFromList(
    selections: yourAge.valueList,
    initialSelection: yourAge.currentSelectionIndex,
    onSelectionChanged:
        (int choice) {
      yourAge.value = yourAge.valueList[choice];
    });


List<Step> _listSteps = [
  Step(title: Text("Step 1"),
   content: _ageSelector,),
  Step(title: Text("Step 2"),
   content: _ageSelector,),
  Step(title: Text("Step 3"),
   content: _ageSelector,),
 Step(title: Text("Step 4"),
   content: _ageSelector,),
 Step(title: Text("Step 5"),
   content: _ageSelector,),
 Step(title: Text("Step 6"),
   content: _ageSelector,),
 Step(title: Text("Step 7"),
   content: _ageSelector,),
];

static int _currentStep = 0;

@override
Widget build(BuildContext context) {


return Scaffold(
  appBar: AppBar(
    title: Text(widget.title),
  ),
  body: Column(
    children: <Widget>[
      Expanded(
        child: Container(
          child: Stepper(
          steps: _listSteps,
          physics: ClampingScrollPhysics(),
          currentStep: _currentStep,
          onStepTapped: (step){
            setState(() {
              _currentStep = step;
            });
          },),
        ),
      ),
    ],
  )

);

}

}

   abstract class DiscreteParameter{
    final String _yourValueKey;
      final List<String> _valueList;
      final String _defaultValue;


      DiscreteParameter(this._yourValueKey, this._valueList,this._defaultValue);


  String _value;

  String get value => _value;
  set value(String value) {
    _value = value;
  }

  int get currentSelectionIndex => _valueList.indexOf(_value);
  set currentSelectionIndex(int index) => (){
    _value = _valueList[index];
  };

  List<String> get valueList => _valueList;
}

class YourAge extends DiscreteParameter{
  static String _yourAgeKey = 'yourAge';
  YourAge():super(_yourAgeKey, _ageList, aged26to35);


  //String _yourAge = aged26to35;
  static const String agedUpto15 = "<15";
  static const String aged16to25 = "16-25";
  static const String aged26to35 = "26-35";
  static const String aged36to45 = "36-45";
  static const String aged46to55 = "46-55";
  static const String aged56to65 = "56-65";
  static const String aged66plus = "66+";
  static const List<String> _ageList = [agedUpto15, aged16to25, aged26to35, aged36to45, aged46to55, aged56to65, aged66plus];

}

class CustomSelectorFromList extends StatefulWidget {
  final Function(int) onSelectionChanged;
  final int initialSelection;
  final List<String> selections;

  @override
  _CustomSelectorFromListState createState() =>     _CustomSelectorFromListState(
      onSelectionChanged: onSelectionChanged,
      initialSelection: initialSelection,
      selections: selections);

  //include a callback function to parent to react to state change
  CustomSelectorFromList({Key key, @required this.selections, @required     this.onSelectionChanged, @required this.initialSelection});
}

class _CustomSelectorFromListState extends State<CustomSelectorFromList>     {
  Function(int) onSelectionChanged;
  final int initialSelection;
  final List<String> selections;
  int _listLength;
  int _value = 0;

  _CustomSelectorFromListState({Key key, @required this.selections, @required this.onSelectionChanged, @required this.initialSelection}){
    //state is preserved so can be set from user "shared preferences"
    _value = initialSelection;
    _listLength = selections.length;
  }


  @override
  Widget build(BuildContext context) {
    //debugPrint("list length: ${selections.length.toString()}");
    return Wrap(
      direction: Axis.vertical,
      children: List<Widget>.generate(
        _listLength, (int index) {
        return ChoiceChip(
          label: Text("${selections[index]}",
            style: TextStyle(
                color: _value==index ? Colors.white70 : Colors.blueGrey
            ),),
          selectedColor: Colors.blueGrey,
          disabledColor: Colors.white70,
          //labelPadding: EdgeInsets.symmetric(),
          padding: const EdgeInsets.all(10),
          selected: _value == index,
          onSelected: (bool selected) {
            setState(() {
              _value = selected ? index : null;
            });
            //callback to parent widget - index of selected item
            onSelectionChanged(index);
          },
        );
      },
      ).toList(),
    );
  }
}

我希望步骤的onTap滚动以显示下面的步骤标题和内容。但是,如果我显示第1步,请不要滚动并单击第2步,则标题会跳出视图,仅显示底部的几个选择筹码,迫使用户将列表向下拖动以查看所有选项。

1 个答案:

答案 0 :(得分:0)

给定的行为是使用以下组合的结果:Column => Expanded => StepperColumn小部件本身不可滚动,因此,如果子级超过高度(例如屏幕高度),则将导致像素溢出。因此,Column => Stepper的组合将无法获得预期的结果。之所以有效,是因为您用Stepper小部件包裹了Expanded,该小部件可用于实现RenderFlex的小部件(Stepper会做什么)并将使用所有可用的小部件渲染它的空间,并在必要时使其可滚动。因此,基本上,您可以只将Stepper用作Scaffold的主体,就可以达到相同的目的-只是想指出一点,因为这可能会对您有所帮助!

查看Stepper的{​​{1}}回调的实现,它将调用onStepTapped函数以确保其中一个Scrollable.ensureVisible(...)的内容在显示后可见点击。因此,颤振会在这一点上避免出现给定的行为。似乎在这种情况下将要设置的偏移量是由于Step => Column => Expanded的组合计算错误的。

为了获得更好的结果,我尝试了另一种方法:Stepper => ListView并将Stepper的{​​{1}}设置为scrollPhysics,因此{{ 1}}不会消耗任何滚动输入,并且Stepper被用作主滚动处理程序:

NeverScrollableScrollPhysics