1

我正在实现一个步进器,其中的内容显示一个选项列表。根据步骤在屏幕上的位置,滚动条将标题推出视图,并且仅在点击时显示部分列表。从用户体验的角度来看,这感觉不对。

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

我也更新了颤振:-)

class _MyHomePageState extends State<MyHomePage> {
  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 步,那么标题会向上射出视野,只显示底部的几个选择芯片,迫使用户向下拖动列表以查看所有选项。

4

1 回答 1

1

给定的行为是使用以下组合的结果:Column=> Expanded=> Stepper。小Column部件本身是不可滚动的,因此如果孩子超过高度(例如屏幕高度),它将导致像素溢出。所以Column=>的组合Stepper不会达到预期的结果。它适用于您的情况,因为您使用小部件包装了您Stepper的小部件,该Expanded小部件可用于实现RenderFlex(做什么Stepper)的小部件,并将使用所有可用空间来呈现它并在必要时使其可滚动。所以基本上你可以只使用你Stepper的身体,Scaffold你会达到同样的效果——只是想指出这一点,因为这可能会对你有所帮助!

查看Stepper'sonStepTapped回调的实现,它将调用该Scrollable.ensureVisible(...)函数以确保其中的内容Step在被点击后可见。因此,flutter 在这一点上试图避免你给定的行为。似乎在这种情况下将设置的偏移量被Column=> Expanded=>的组合错误计算了Stepper

为了产生更好的结果,我尝试了一种不同的方法:ListView=>Stepper并将 of 设置scrollPhysicsStepperNeverScrollableScrollPhysics因此Stepper不会消耗任何滚动输入,并且ListView用作主滚动处理程序:

return Scaffold(
  appBar: AppBar(
    title: Text(widget.title),
  ),
  body: ListView(
    children: <Widget>[
      Stepper(
        steps: _listSteps,
        physics: NeverScrollableScrollPhysics(),
        currentStep: _currentStep,
        onStepTapped: (step) {
          setState(() {
            _currentStep = step;
          });
        },
      ),
    ],
  ),
);
于 2019-06-03T16:13:38.663 回答