Flutter 动画:将一个小部件从一个 flex 布局移动到另一个 flex 布局
Flutter Animation: Move a widget from a flex layout to another flex layout
我想将小部件从弹性布局(在本例中为 Wrap)移动到另一个弹性布局(再次为 Wrap)。这有点像编程拖放。
我已经编写了一个有效的概念验证程序:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
// To get the position of a widget
extension GlobalKeyExtension on GlobalKey {
Rect? get globalPaintBounds {
final renderObject = currentContext?.findRenderObject();
final matrix = renderObject?.getTransformTo(null);
if (matrix != null && renderObject?.paintBounds != null) {
final rect = MatrixUtils.transformRect(matrix, renderObject!.paintBounds);
return rect;
} else {
return null;
}
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(home: MyHomePage());
}
}
// A simple square
class Square extends StatelessWidget {
final Color color;
const Square({this.color = Colors.blue, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: color,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final parentKey = GlobalKey();
final sourceKey = GlobalKey();
final targetKey = GlobalKey();
Rect? sourceRect;
Rect? targetRect;
bool moved = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Move test')),
body: Stack(
key: parentKey,
children: [
Padding(
padding: const EdgeInsets.all(32),
child: Column(
children: [
// The top Wrap is the target of the movement
Wrap(
spacing: 16,
runSpacing: 16,
children: [
const Square(),
const Square(),
// Display the target
if (moved)
const Square(color: Colors.yellow)
else if (sourceRect != null && targetRect != null)
const Opacity(opacity: 0, child: Square())
else
Square(key: targetKey),
const Square(),
const Square(),
],
),
const Spacer(),
Wrap(
spacing: 16,
runSpacing: 16,
children: [
const Square(color: Colors.red),
const Square(color: Colors.red),
// Display the source
if (sourceRect == null && targetRect == null && !moved)
InkWell(
onTap: () {
print('Tapped');
setState(() {
final parentRect = parentKey.globalPaintBounds;
if (parentRect != null) {
sourceRect = sourceKey.globalPaintBounds;
sourceRect = sourceRect?.translate(
-parentRect.left,
-parentRect.top,
);
targetRect = targetKey.globalPaintBounds;
targetRect = targetRect?.translate(
-parentRect.left,
-parentRect.top,
);
}
});
WidgetsBinding.instance
.addPostFrameCallback((timeStamp) {
setState(() {
sourceRect = targetRect;
print('START animation!');
});
});
},
child: Square(key: sourceKey, color: Colors.red),
),
const Square(color: Colors.red),
const Square(color: Colors.red),
],
),
],
),
),
// This part is within Stack and moves the square
if (sourceRect != null && targetRect != null)
AnimatedPositioned(
left: sourceRect!.left,
top: sourceRect!.top,
duration: const Duration(seconds: 1),
onEnd: () {
print('STOP animation!');
setState(() {
sourceRect = null;
targetRect = null;
moved = true;
});
},
child: const Square(color: Colors.yellow),
),
],
),
);
}
}
结果是这样的:a moving widget animation (gif)。
所以,我的问题是:是否有更好的方法在框架内或库中执行此操作?
我不确定你所说的“更好的方法”是什么意思,但是有一个名为 package(不是库)的 package 10=] 这与您在两个页面之间但在同一页面内使用 Hero
小部件获得的动画相同,也许您可以尝试一下,看看它是否是您想要的:
https://pub.dev/packages/local_hero
以防万一你不知道,https://pub.dev 是 flutter/dart 包正式发布的地方。
我想将小部件从弹性布局(在本例中为 Wrap)移动到另一个弹性布局(再次为 Wrap)。这有点像编程拖放。
我已经编写了一个有效的概念验证程序:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
// To get the position of a widget
extension GlobalKeyExtension on GlobalKey {
Rect? get globalPaintBounds {
final renderObject = currentContext?.findRenderObject();
final matrix = renderObject?.getTransformTo(null);
if (matrix != null && renderObject?.paintBounds != null) {
final rect = MatrixUtils.transformRect(matrix, renderObject!.paintBounds);
return rect;
} else {
return null;
}
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(home: MyHomePage());
}
}
// A simple square
class Square extends StatelessWidget {
final Color color;
const Square({this.color = Colors.blue, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: color,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final parentKey = GlobalKey();
final sourceKey = GlobalKey();
final targetKey = GlobalKey();
Rect? sourceRect;
Rect? targetRect;
bool moved = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Move test')),
body: Stack(
key: parentKey,
children: [
Padding(
padding: const EdgeInsets.all(32),
child: Column(
children: [
// The top Wrap is the target of the movement
Wrap(
spacing: 16,
runSpacing: 16,
children: [
const Square(),
const Square(),
// Display the target
if (moved)
const Square(color: Colors.yellow)
else if (sourceRect != null && targetRect != null)
const Opacity(opacity: 0, child: Square())
else
Square(key: targetKey),
const Square(),
const Square(),
],
),
const Spacer(),
Wrap(
spacing: 16,
runSpacing: 16,
children: [
const Square(color: Colors.red),
const Square(color: Colors.red),
// Display the source
if (sourceRect == null && targetRect == null && !moved)
InkWell(
onTap: () {
print('Tapped');
setState(() {
final parentRect = parentKey.globalPaintBounds;
if (parentRect != null) {
sourceRect = sourceKey.globalPaintBounds;
sourceRect = sourceRect?.translate(
-parentRect.left,
-parentRect.top,
);
targetRect = targetKey.globalPaintBounds;
targetRect = targetRect?.translate(
-parentRect.left,
-parentRect.top,
);
}
});
WidgetsBinding.instance
.addPostFrameCallback((timeStamp) {
setState(() {
sourceRect = targetRect;
print('START animation!');
});
});
},
child: Square(key: sourceKey, color: Colors.red),
),
const Square(color: Colors.red),
const Square(color: Colors.red),
],
),
],
),
),
// This part is within Stack and moves the square
if (sourceRect != null && targetRect != null)
AnimatedPositioned(
left: sourceRect!.left,
top: sourceRect!.top,
duration: const Duration(seconds: 1),
onEnd: () {
print('STOP animation!');
setState(() {
sourceRect = null;
targetRect = null;
moved = true;
});
},
child: const Square(color: Colors.yellow),
),
],
),
);
}
}
结果是这样的:a moving widget animation (gif)。
所以,我的问题是:是否有更好的方法在框架内或库中执行此操作?
我不确定你所说的“更好的方法”是什么意思,但是有一个名为 package(不是库)的 package 10=] 这与您在两个页面之间但在同一页面内使用 Hero
小部件获得的动画相同,也许您可以尝试一下,看看它是否是您想要的:
https://pub.dev/packages/local_hero
以防万一你不知道,https://pub.dev 是 flutter/dart 包正式发布的地方。