import 'dart:async';

import 'package:basketball/constants.dart';
import 'package:flutter/material.dart';

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

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

class BouncingBallAppState extends State<BouncingBallApp> {
  double floor = 1.0;
  double ceiling = 0;
  double ballPositionX = 50;
  double ballPositionY = 50;
  double ballVelocityX = 0;
  double ballVelocityY = 0;
  double gravity = 9.8;
  double restitution = 0.8;
  double friction = 0.98;
  bool isDragging = false;

  @override
  void initState() {
    super.initState();
    Timer.periodic(const Duration(milliseconds: 16), (timer) {
      _updateBall(16);
    });
  }

  void _onPanStart(DragStartDetails details) {
    setState(() {
      isDragging = true;
    });
  }

  void _onPanUpdate(DragUpdateDetails details) {
    // if (ballVelocityX.abs() < 3 && ballVelocityY.abs() < 3) {
    setState(() {
      ballVelocityX += details.delta.dx;
      ballVelocityY += -details.delta.dy;
    });
    // }
  }

  void _onPanEnd(DragEndDetails details) {
    setState(() {
      isDragging = false;
      ballVelocityX *= friction;
      ballVelocityY *= friction;
    });
  }

  void _updateBall(double delta) {
    if (!isDragging) {
      setState(() {
        ballPositionX += ballVelocityX * delta / 100;
        ballPositionY += ballVelocityY * delta / 100;

        if (ballPositionY + AppConstants.ballSize / 2 >= floor - 10) {
// Stop the ball from bouncing if it’s too close to the ground
          ballPositionY = floor - AppConstants.ballSize / 2 - 10;
          ballVelocityX *= friction;
          if ((ballVelocityY * ballVelocityY) < 4) {
            ballVelocityY = 0;
          } else {
            ballVelocityY *= -restitution;
          }
        } else if ((ballVelocityY * ballVelocityY) + (ballVelocityX * ballVelocityX) < 5) {
// Stop the ball from bouncing if it’s moving too slowly
          ballVelocityY = 0;
          ballVelocityX = 0;
        } else {
          ballVelocityY += gravity * delta / 100;
        }

        if (ballPositionX < AppConstants.ballSize / 2) {
          ballPositionX = AppConstants.ballSize / 2;
          ballVelocityX *= -restitution;
          ballVelocityY *= restitution;
        } else if (ballPositionX > MediaQuery.of(context).size.width - AppConstants.ballSize / 2) {
          ballPositionX = MediaQuery.of(context).size.width - AppConstants.ballSize / 2;
          ballVelocityX *= -restitution;
          ballVelocityY *= restitution;
        }
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    floor = MediaQuery.of(context).size.height;
    return GestureDetector(
      onPanStart: _onPanStart,
      onPanUpdate: _onPanUpdate,
      onPanEnd: _onPanEnd,
      child: Scaffold(
        body: Center(
          child: Stack(
            children: <Widget>[
              Positioned.fill(
                child: Container(
                  color: Colors.grey[300],
                  child: const SizedBox.expand(),
                ),
              ),
              Positioned(
                  left: ballPositionX - AppConstants.ballSize / 2,
                  top: ballPositionY - AppConstants.ballSize / 2,
                  child: const Image(
                    width: AppConstants.ballSize,
                    height: AppConstants.ballSize,
                    image: AssetImage('assets/ball.png'),
                  )),
            ],
          ),
        ),
      ),
    );
  }
}

void main() => runApp(MaterialApp(home: BouncingBallApp(key: UniqueKey())));
Hi, I have this Flutter app to simulate bouncing ball behaviour. How to actually remove extra pixels from the bottom, they are so annoying:(