1

I am trying to recreate RobinHood slide number animation, I am treating every digit as an Item in n animated list, When I am adding an digit to the start of the list the last Item in the list receives a jump and I cant quite figure out how to fix it.

enter image description here

this is the code for every digit

class NumberColView extends StatefulWidget {
  final int animateTo;

  final bool comma;
  final TextStyle textStyle;
  final Duration duration;
  final Curve curve;

  NumberColView(
      {@required this.animateTo,
      @required this.textStyle,
      @required this.duration,
      this.comma = false,
      @required this.curve})
      : assert(animateTo != null && animateTo >= 0 && animateTo < 10);

  @override
  _NumberColState createState() => _NumberColState();
}

class _NumberColState extends State<NumberColView>
    with SingleTickerProviderStateMixin {
  ScrollController _scrollController;

  double _elementSize = 0.0;

  @override
  void initState() {
    super.initState();
    print(_elementSize);
    _scrollController = new ScrollController();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _elementSize = _scrollController.position.maxScrollExtent / 10;
      setState(() {});

    });
  }

  @override
  void didUpdateWidget(NumberColView oldWidget) {
    if (oldWidget.animateTo != widget.animateTo) {
      _scrollController.animateTo(_elementSize * widget.animateTo,
          duration: widget.duration, curve: widget.curve);
    }

    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    // print(widget.animateTo);
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        IgnorePointer(
          child: ConstrainedBox(
            constraints: BoxConstraints(maxHeight: _elementSize),
            child: SingleChildScrollView(
              controller: _scrollController,
              child: Column(
                children: List.generate(10, (position) {
                  return Text(position.toString(), style: widget.textStyle);
                }),
              ),
            ),
          ),
        ),
        widget.comma
            ? Container(
                child: Text(', ',
                    style:
                        TextStyle(fontSize: 16, fontWeight: FontWeight.bold)))
            : Container(),
      ],
    );
  }
}

This the code to build the whole number

class _NumberSlideAnimationState extends State<NumberSlideAnimation> {
  @override
  void didUpdateWidget(oldWidget) {
    if (oldWidget.number.length != widget.number.length) {
      int newLen = widget.number.length - oldWidget.number.length;
      if(newLen > 0) {
        widget.listKey.currentState.insertItem(0,
                            duration: const Duration(milliseconds: 200));
        
      }
      // setState(() {
      //   animateTo = widget.animateTo;
      // });
    }

    super.didUpdateWidget(oldWidget);
  }

  Widget digitSlide(BuildContext context, int position, animation) {
    int item = int.parse(widget.number[position]);
    return SlideTransition(
      position: Tween<Offset>(
        begin: const Offset(0, 1),
        end: Offset(0, 0),
      ).animate(animation),
      child:
          // TestCol(animateTo: item, style: widget.textStyle)

          NumberColView(
        textStyle: widget.textStyle,
        duration: widget.duration,
        curve: widget.curve,
        comma: (widget.number.length - 1) != position &&
                (widget.number.length - position) % 3 == 1 ??
            true,
        animateTo: item,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 40,
      child: AnimatedList(
        shrinkWrap: true,
        scrollDirection: Axis.horizontal,
        key: widget.listKey,
        initialItemCount: widget.number.length,
        itemBuilder: (context, position, animation) {
          return digitSlide(context, position, animation);
        },
      ),
    );
  }
}
bihire boris
  • 1,530
  • 3
  • 19
  • 44

1 Answers1

0

I don't know the right solution for your problem, however I know another way and I am just sharing it.

enter image description here

Here the full code If you want to use this method:

update: now 0 will come form bottom as what it should be and the uncharged values will be in there palaces.

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

  @override
  _CountState createState() => _CountState();
}

class _CountState extends State<Count> {
  int count = 10;
  int count2 = 10;
  int count3 = 10;
  Alignment alignment = Alignment.topCenter;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ClipRRect(
            child: Align(
              alignment: Alignment.center,
              heightFactor: 0.2,
              child: SizedBox(
                height: 100,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    ...List.generate(count.toString().length, (index) {
                      final num2IndexInRange =
                          count2.toString().length - 1 >= index;
                      final num3IndexInRange =
                          count3.toString().length - 1 >= index;

                      final num1 = int.parse(count.toString()[index]),
                          num2 = num2IndexInRange
                              ? int.parse(count2.toString()[index])
                              : 9,
                          num3 = num3IndexInRange
                              ? int.parse(count3.toString()[index])
                              : 1;

                      return AnimatedAlign(
                        key: Key("${num2} ${index}"),
                        duration: Duration(milliseconds: 500),
                        alignment: (num1 != num2 || num1 != num3)
                            ? alignment
                            : Alignment.center,
                        child: Text(
                          num3.toString(),
                          style: TextStyle(
                            fontSize: 20,
                          ),
                        ),
                      );
                    }),
                  ],
                ),
              ),
            ),
          ),
          SizedBox(height: 200),
          RaisedButton(
            onPressed: () async {
              setState(() {
                alignment = Alignment.topCenter;
                count += 10;
              });
              await Future.delayed(Duration(milliseconds: 250));
              setState(() {
                alignment = Alignment.bottomCenter;
                count2 = count;
              });
              await Future.delayed(Duration(milliseconds: 250));
              setState(() {
                count3 = count;
              });
            },
          ),
        ],
      ),
    );
  }
}

if you don't like how the spacing between the numbers changed you can warp the text widget with a sized box and give it a fixed width.

Mohammed Alfateh
  • 3,186
  • 1
  • 10
  • 24
  • its not quite what I wanted but it might give me some insights, could you try to keep the `0`s that didn't receive any change in the same place(last one) and only animate the `0` that changed from 9 -> 0 though – bihire boris Feb 18 '21 at 12:23
  • sorry for being late, the answer is updated but the screen shot is the old one – Mohammed Alfateh Feb 23 '21 at 21:21