Home > Software design >  Is there a widget or a library that I can use to get this effect?
Is there a widget or a library that I can use to get this effect?

Time:08-26

I want to build a scrollable Widget with an int callback depending on where the user stopped scrolling. It is supposed to represent a scale for the user to select his weight. I'd really appreciate any widget or library suggestions.
This is what I want it to look like.

image

CodePudding user response:

the basic idea is use scroll metrix we get from notification listener, flutter already have list builder lazyly render neccessary view, we can use that. no package need.

here the result : enter image description here

the code :

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

scale indicator :

///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;
  }
}
  • Related