我想将一个小部件从一个flex布局(在本例中是一个Wrap)移动到另一个flex布局(又是一个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),
),
],
),
);
}
}
结果是:一个移动的小部件动画(gif)。
所以,我的问题是:有没有更好的方法在框架内或与库一起做这件事?我不知道你说的"更好的方法"是什么意思&;但是有一个包(不是库)称为local_hero
,这是相同的动画,你会得到一个Hero
小部件之间的两个页面,但在同一个页面,也许你可以尝试一下,看看它是否是你想要的:
https://pub.dev/packages/local_hero
以防你不知道,https://pub.dev是官方发布flutter/dart包的地方。