Вы можете поменять местами два виджета, используя Stack и Positioned, в конечном итоге добавив эффект анимации. Я привожу два примера: первый код просто меняет два виджета. второй пример кода добавляет эффект анимации.
Замена двух виджетов (без анимации)
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
@override
createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final double address1Top = 20;
final double address2Top = 110;
bool swapped = false;
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 150,
color: Colors.blue,
child: Stack(
children: <Widget> [
// Top address
Positioned(
top: swapped ? address2Top : address1Top,
left: 20,
child: Text("This is the first address"),
),
// Bottom address
Positioned(
top: swapped ? address1Top : address2Top,
left: 20,
child: Text("This is another address"),
),
// Swap button
Positioned(
top: 50,
right: 20,
child: FlatButton(
onPressed: () => setState(() {
swapped = !swapped;
}),
child: Text("swap"),
),
),
],
),
);
}
}
Замена двух виджетов с анимацией
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
@override
createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with TickerProviderStateMixin {
final double address1Top = 20;
final double address2Top = 110;
bool swapped = false;
Animation<double> addressAnimation;
AnimationController controller;
animationListener() => setState(() { });
@override
void didChangeDependencies() async {
super.didChangeDependencies();
// Initialize animations
controller = AnimationController(duration: const Duration(milliseconds: 300), vsync: this);
addressAnimation = Tween(begin: 0.0, end: address2Top - address1Top).animate(CurvedAnimation(parent: controller, curve: const Interval(0.0, 1.0, curve: Curves.easeInOut)))..addListener(animationListener);
}
@override
dispose() {
// Dispose of animation controller
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
var tweenValue = addressAnimation?.value ?? 0.0;
return Container(
width: 300,
height: 150,
color: Colors.blue,
child: Stack(
children: <Widget> [
// Top address
Positioned(
top: address1Top + tweenValue,
left: 20,
child: Text("This is the first address"),
),
// Bottom address
Positioned(
top: address2Top - tweenValue,
left: 20,
child: Text("This is another address"),
),
// Swap button
Positioned(
top: 50,
right: 20,
child: FlatButton(
onPressed: () => setState(() {
swapped ? controller.reverse() : controller.forward();
swapped = !swapped;
}),
child: Text("swap"),
),
),
],
),
);
}
}
Надеюсь, это поможет:)