如何根据用户停止滚动的位置构建带有 int 回调的可滚动小部件?

Mat*_*elo 2 dart flutter flutter-dependencies

我想构建一个带有 int 回调的可滚动小部件,具体取决于用户停止滚动的位置。它应该代表用户选择体重的秤。

这就是我想要的样子。

图像

Say*_*d J 5

基本思想是使用我们从通知监听器获得的滚动矩阵,flutter 已经有列表构建器延迟渲染必要的视图,我们可以使用它。无需包

结果如下: 在此输入图像描述

代码 :

home.dart :

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

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  double? _userWeight;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Text(
              _userWeight == null
                  ? 'User Weight : no data'
                  : 'User Weight : $_userWeight',
              style: const TextStyle(fontSize: 24.0),
            ),
            ScaleIndicator(
              onScrollChanged: (double result) {},
              onSelected: (double result) {
                setState(() {
                  _userWeight = result;
                });
              },
            ),
          ],
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

刻度指标:

///callback when scrolling changed, since you ask for int
/// i prefer double instead : 20.5 kg or 100.34 kg
typedef OnScrollChanged = void Function(double scale);

///calback when selected
typedef OnSelected = void Function(double scale);

class ScaleIndicator extends StatefulWidget {
  /// default value show in widget when first open
  final double? initialValue;

  ///what the distance for performance, min max kg so the we can pass max length to listview builder
  final int? range;
  final Color? indicatorColor;

  ///callback when scrolling changed
  final OnScrollChanged? onScrollChanged;

  ///calback when selected return double
  final OnSelected onSelected;

  const ScaleIndicator(
      {Key? key,
      this.initialValue,
      this.range,
      this.indicatorColor,
      this.onScrollChanged,
      required this.onSelected})
      : super(key: key);

  @override
  State<ScaleIndicator> createState() => _ScaleIndicatorState();
}

class _ScaleIndicatorState extends State<ScaleIndicator> {
  // default value show in widget when first open
  late double _initialValue;

  // what the distance for performance, min max kg so the we can pass max length to listview builder
  late int _range;

  late Color _indicatorColor;

  late double _valueSelected;

  static const double _indicatorWidth = 10.0;

  @override
  void initState() {
    super.initState();
    // set your default value here
    _initialValue = 0;
    _range = 200;
    _indicatorColor = Colors.blue;
    _valueSelected = 0;
  }

  @override
  Widget build(BuildContext context) {
    return NotificationListener<ScrollNotification>(
      onNotification: (ScrollNotification scroll) {
        double pixels = scroll.metrics.pixels;
        double result = pixels / (_indicatorWidth * 10.0);
        setState(() {
          _valueSelected = result;
          widget.onScrollChanged!(
              double.tryParse(_valueSelected.toStringAsFixed(2)) ?? 0.0);
        });
        return true;
      },
      child: SizedBox(
        width: 200,
        height: 120,
        child: Column(
          children: [
            Flexible(
                child: FractionalTranslation(
                    translation: const Offset(0.175, 0.0),
                    child: Text(
                      "${_valueSelected.toStringAsFixed(2)} kg",
                      style: const TextStyle(
                          fontSize: 24.0, fontWeight: FontWeight.bold),
                    ))),
            Expanded(
              child: ListView.builder(
                itemCount: _range,
                scrollDirection: Axis.horizontal,
                itemBuilder: (context, index) {
                  return SizedBox(
                    width: _indicatorWidth,
                    child: Row(
                      crossAxisAlignment: CrossAxisAlignment.end,
                      children: [
                        Container(
                          width: 3,
                          height: _heightFromIndex(index),
                          decoration: BoxDecoration(
                              color: _indicatorColor,
                              borderRadius: const BorderRadius.only(
                                  topLeft: Radius.circular(5.0),
                                  topRight: Radius.circular(5.0))),
                        ),
                        const Expanded(child: SizedBox())
                      ],
                    ),
                  );
                },
              ),
            ),
            //Button for ex you wanna show it on ShowDialog or else
            Padding(
              padding: const EdgeInsets.all(10.0),
              child: ElevatedButton(
                  onPressed: () {
                    widget.onSelected(
                        double.tryParse(_valueSelected.toStringAsFixed(2)) ??
                            0.0);
                  },
                  child: const Text("Done")),
            )
          ],
        ),
      ),
    );
  }

  double _heightFromIndex(int index) {
    if (index % 10 == 0) {
      return 40.0;
    } else if (index % 5 == 0) {
      return 25.0;
    }
    return 10.0;
  }
}
Run Code Online (Sandbox Code Playgroud)