|
|
@@ -0,0 +1,241 @@ |
|
|
import 'package:flutter_web/material.dart'; |
|
|
import 'package:flutter_web_test/flutter_web_test.dart'; |
|
|
import 'package:flutter_web_ui/ui.dart' as ui; |
|
|
|
|
|
Future main() async { |
|
|
await ui.webOnlyInitializePlatform(); |
|
|
runApp(MaterialApp( |
|
|
debugShowCheckedModeBanner: false, |
|
|
home: AnimatedAlignDemo(), |
|
|
theme: ThemeData.dark(), |
|
|
)); |
|
|
} |
|
|
|
|
|
class AnimatedAlignDemo extends StatelessWidget { |
|
|
Widget build(BuildContext context) { |
|
|
return Scaffold( |
|
|
body: Center( |
|
|
child: AspectRatio( |
|
|
aspectRatio: 1, |
|
|
child: Padding( |
|
|
padding: const EdgeInsets.all(16.0), |
|
|
child: SlidePuzzle( |
|
|
boardSize: 5, |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
class SlidePuzzle extends StatefulWidget { |
|
|
final Duration duration = Duration(milliseconds: 200); |
|
|
final int boardSize; |
|
|
|
|
|
SlidePuzzle({this.boardSize = 5}); |
|
|
|
|
|
_SlidePuzzleState createState() => _SlidePuzzleState(); |
|
|
} |
|
|
|
|
|
class _SlidePuzzleState extends State<SlidePuzzle> { |
|
|
int row; |
|
|
int col; |
|
|
|
|
|
void initState() { |
|
|
row = widget.boardSize ~/ 2; |
|
|
col = widget.boardSize ~/ 2; |
|
|
super.initState(); |
|
|
} |
|
|
|
|
|
List<AnimatedTile> get tiles => [ |
|
|
AnimatedTile( |
|
|
boardSize: widget.boardSize, |
|
|
row: row, |
|
|
col: col, |
|
|
child: Tile(), |
|
|
duration: widget.duration, |
|
|
), |
|
|
]; |
|
|
|
|
|
void _moveTileLeft() => row = (row - 1) % widget.boardSize; |
|
|
|
|
|
void _moveTileUp() => col = (col - 1) % widget.boardSize; |
|
|
|
|
|
void _moveTileRight() => row = (row + 1) % widget.boardSize; |
|
|
|
|
|
void _moveTileDown() => col = (col + 1) % widget.boardSize; |
|
|
|
|
|
Widget build(BuildContext context) { |
|
|
return SwipeDetector( |
|
|
onSwipeUp: () => setState(() => _moveTileUp()), |
|
|
onSwipeDown: () => setState(() => _moveTileDown()), |
|
|
onSwipeLeft: () => setState(() => _moveTileLeft()), |
|
|
onSwipeRight: () => setState(() => _moveTileRight()), |
|
|
child: Container( |
|
|
child: Stack( |
|
|
children: tiles, |
|
|
), |
|
|
decoration: BoxDecoration( |
|
|
borderRadius: BorderRadius.circular(4.0), |
|
|
color: Colors.grey.shade700, |
|
|
), |
|
|
), |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
class Tile extends StatelessWidget { |
|
|
const Tile({ |
|
|
Key key, |
|
|
}) : super(key: key); |
|
|
|
|
|
Widget build(BuildContext context) { |
|
|
return Padding( |
|
|
padding: const EdgeInsets.all(4.0), |
|
|
child: DecoratedBox( |
|
|
decoration: BoxDecoration( |
|
|
borderRadius: BorderRadius.circular(4.0), |
|
|
color: Colors.deepOrange, |
|
|
), |
|
|
), |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
class AnimatedTile extends StatelessWidget { |
|
|
final int boardSize; |
|
|
final int row; |
|
|
final int col; |
|
|
final Widget child; |
|
|
final Duration duration; |
|
|
|
|
|
AnimatedTile({ |
|
|
this.boardSize, |
|
|
this.row, |
|
|
this.col, |
|
|
this.child, |
|
|
this.duration, |
|
|
}); |
|
|
|
|
|
AlignmentGeometry get alignment { |
|
|
var maxTileIndex = (boardSize - 1).toDouble(); |
|
|
var rowPosition = ui.lerpDouble(-1.0, 1.0, row.toDouble() / maxTileIndex); |
|
|
var colPosition = ui.lerpDouble(-1.0, 1.0, col.toDouble() / maxTileIndex); |
|
|
return Alignment(rowPosition, colPosition); |
|
|
} |
|
|
|
|
|
Widget build(BuildContext context) { |
|
|
return AnimatedAlign( |
|
|
alignment: alignment, |
|
|
duration: duration, |
|
|
curve: Curves.ease, |
|
|
child: FractionallySizedBox( |
|
|
widthFactor: 1.0 / boardSize, |
|
|
heightFactor: 1.0 / boardSize, |
|
|
child: child, |
|
|
), |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
class SwipeDetector extends StatefulWidget { |
|
|
final Widget child; |
|
|
final VoidCallback onSwipeLeft; |
|
|
final VoidCallback onSwipeUp; |
|
|
final VoidCallback onSwipeRight; |
|
|
final VoidCallback onSwipeDown; |
|
|
|
|
|
SwipeDetector({ |
|
|
this.child, |
|
|
this.onSwipeLeft, |
|
|
this.onSwipeUp, |
|
|
this.onSwipeRight, |
|
|
this.onSwipeDown, |
|
|
}); |
|
|
|
|
|
_SwipeDetectorState createState() => _SwipeDetectorState(); |
|
|
} |
|
|
|
|
|
class _SwipeDetectorState extends State<SwipeDetector> { |
|
|
double _verticalStartY = 0.0; |
|
|
double _verticalEndY = 0.0; |
|
|
double _verticalDrag = 0.0; |
|
|
double _horizontalStartX = 0.0; |
|
|
double _horizontalEndX = 0.0; |
|
|
double _horizontalDrag = 0.0; |
|
|
|
|
|
_horizontalStart(DragStartDetails details) { |
|
|
if (details == null || details.globalPosition == null) return; |
|
|
_horizontalStartX = details.globalPosition.dx; |
|
|
} |
|
|
|
|
|
_horizontalUpdate(DragUpdateDetails details) { |
|
|
if (details == null || details.globalPosition == null) { |
|
|
return; |
|
|
} |
|
|
|
|
|
_horizontalEndX = details.globalPosition.dx; |
|
|
|
|
|
var distance = _horizontalEndX - _horizontalStartX; |
|
|
if (distance != null && distance < 0 && _horizontalDrag < 0 || |
|
|
distance > 0 && _horizontalDrag > 0) { |
|
|
return; |
|
|
} |
|
|
|
|
|
if (distance < 0) { |
|
|
widget.onSwipeLeft(); |
|
|
} else if (distance > 0) { |
|
|
widget.onSwipeRight(); |
|
|
} |
|
|
|
|
|
_horizontalDrag = distance; |
|
|
} |
|
|
|
|
|
_horizontalEnd(DragEndDetails details) { |
|
|
_horizontalDrag = 0.0; |
|
|
} |
|
|
|
|
|
_verticalStart(DragStartDetails details) { |
|
|
if (details == null || details.globalPosition == null) return; |
|
|
_verticalStartY = details.globalPosition.dy; |
|
|
} |
|
|
|
|
|
_verticalUpdate(DragUpdateDetails details) { |
|
|
if (details == null || details.globalPosition == null) { |
|
|
return; |
|
|
} |
|
|
|
|
|
_verticalEndY = details.globalPosition.dy; |
|
|
|
|
|
var distance = _verticalEndY - _verticalStartY; |
|
|
if (distance != null && distance < 0 && _verticalDrag < 0 || |
|
|
distance > 0 && _verticalDrag > 0) { |
|
|
return; |
|
|
} |
|
|
|
|
|
if (distance < 0) { |
|
|
widget.onSwipeUp(); |
|
|
} else if (distance > 0) { |
|
|
widget.onSwipeDown(); |
|
|
} |
|
|
|
|
|
_verticalDrag = distance; |
|
|
} |
|
|
|
|
|
_verticalEnd(DragEndDetails details) { |
|
|
_verticalDrag = 0.0; |
|
|
} |
|
|
|
|
|
Widget build(BuildContext context) { |
|
|
return GestureDetector( |
|
|
onHorizontalDragStart: _horizontalStart, |
|
|
onHorizontalDragUpdate: _horizontalUpdate, |
|
|
onHorizontalDragEnd: _horizontalEnd, |
|
|
onVerticalDragStart: _verticalStart, |
|
|
onVerticalDragUpdate: _verticalUpdate, |
|
|
onVerticalDragEnd: _verticalEnd, |
|
|
child: widget.child, |
|
|
); |
|
|
} |
|
|
} |