1
0
mirror of https://github.com/flutter/samples.git synced 2026-10-10 20:04:47 +00:00
Files
samples/animations/lib/src/misc/physics_card_drag.dart
Eric Windmill 2999d738b8 Dart 3.9 / Flutter 3.35 [first LLM release] (#2714)
I got carried away with Gemini and basically rewrote CI and the release
process for the new LLM reality. This work was largely completed by
Gemini.

- Bump all SDK versions to the current beta (3.9.0-0)
- Run `flutter channel beta`
- Wrote `ci_script.dart` to replace the bash scripts
- Converted repository to pub workspace #2499 
- Added llm.md and release.md
- Added redirect for deprecated Samples Index

## Pre-launch Checklist

- [x] I read the [Flutter Style Guide] _recently_, and have followed its
advice.
- [x] I signed the [CLA].
- [x] I read the [Contributors Guide].
- [x] I have added sample code updates to the [changelog].
- [x] I updated/added relevant documentation (doc comments with `///`).
2025-08-14 12:26:24 -07:00

112 lines
3.1 KiB
Dart

// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
class PhysicsCardDragDemo extends StatelessWidget {
const PhysicsCardDragDemo({super.key});
static const String routeName = 'misc/physics_card';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Spring Physics')),
body: const DraggableCard(child: FlutterLogo(size: 128)),
);
}
}
/// A draggable card that moves back to [Alignment.center] when it's
/// released.
class DraggableCard extends StatefulWidget {
const DraggableCard({required this.child, super.key});
final Widget child;
@override
State<DraggableCard> createState() => _DraggableCardState();
}
class _DraggableCardState extends State<DraggableCard>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
/// The alignment of the card as it is dragged or being animated.
///
/// While the card is being dragged, this value is set to the values computed
/// in the GestureDetector onPanUpdate callback. If the animation is running,
/// this value is set to the value of the [_animation].
var _dragAlignment = Alignment.center;
late Animation<Alignment> _animation;
final _spring = const SpringDescription(
mass: 10,
stiffness: 1000,
damping: 0.7,
);
/// Calculate the velocity relative to the unit interval, [0,1],
/// used by the animation controller.
double _normalizeVelocity(Offset velocity, Size size) {
final normalizedVelocity = Offset(
velocity.dx / size.width,
velocity.dy / size.height,
);
// Returning negative implies dragging away from center
return -normalizedVelocity.distance;
}
/// Calculates and runs a [SpringSimulation]
void _runAnimation(Offset velocity, Size size) {
_animation = _controller.drive(
AlignmentTween(begin: _dragAlignment, end: Alignment.center),
);
final simulation = SpringSimulation(
_spring,
0,
1,
_normalizeVelocity(velocity, size),
);
_controller.animateWith(simulation);
}
@override
void initState() {
super.initState();
_controller = AnimationController.unbounded(vsync: this)
..addListener(
() => setState(() => _dragAlignment = _animation.value),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return GestureDetector(
onPanStart: (details) => _controller.stop(canceled: true),
onPanUpdate: (details) => setState(
() => _dragAlignment += Alignment(
details.delta.dx / (size.width / 2),
details.delta.dy / (size.height / 2),
),
),
onPanEnd: (details) =>
_runAnimation(details.velocity.pixelsPerSecond, size),
child: Align(
alignment: _dragAlignment,
child: Card(child: widget.child),
),
);
}
}