所需组件
1、PageView
官方介绍:https://api.flutter-io.cn/flutter/widgets/PageView-class.html
2、AnimationController
官方介绍:https://api.flutter-io.cn/flutter/animation/AnimationController-class.html
个人需求与解决方法
将个人需求导入谷歌的GPT:使用flutter做一个页面,做一个类似列表的东西,其中元素左右滑动时,滑动到中间的元素呈现放大效果与以0度开始的一直旋转的动画
得到:
代码:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'List Animation',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
AnimationController _animationController;
PageController _pageController;
int _currentPage = 0;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
)..repeat(); // 设置旋转动画时长和循环
_pageController = PageController(initialPage: _currentPage);
}
@override
void dispose() {
_animationController.dispose(); // 释放动画资源
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('List Animation'),
),
body: PageView.builder(
controller: _pageController,
itemCount: 5,
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
itemBuilder: (context, index) {
double scale = 0.8;
double rotation = 0.0;
if (index == _currentPage) {
scale = 1.0;
rotation = _animationController.value * 360.0; // 设置旋转角度
} else if (index == _currentPage - 1 || index == _currentPage + 1) {
scale = 0.9;
rotation = 0.0;
}
return Transform.scale(
scale: scale,
child: Transform.rotate(
angle: rotation,
child: Container(
padding: EdgeInsets.all(16),
child: Text(
'Item $index',
style: TextStyle(fontSize: 24),
),
),
),
);
},
),
);
}
}
解释:
在这个示例中,我们使用PageView.builder构建一个PageView组件,并使用PageController来控制页面的滑动。每个页面都使用Transform.scale和Transform.rotate来实现放大和旋转效果,根据当前页面的索引来设置缩放比例和旋转角度。
在默认情况下,中间页面展示1.0倍的缩放和以0度开始的旋转动画,左右相邻的页面展示0.9倍的缩放和0度的旋转。你可以根据需要修改缩放比例、旋转角度和列表元素来满足你的需求。
运行这个示例代码,你将看到一个类似列表的页面,元素左右滑动时,滑动到中间的元素会出现放大效果和以0度开始的旋转动画。滑动页面时,当前页面会放大,左右相邻的页面会缩小。
得到的效果:
个人评价:
不能说不是很好,实际是一点都不好
修改一下
玩笑归玩笑,AI写的问题主要体现在对动画的理解不足,它误认为是角度放置了,使用了Transform.rotate,关于Transform可以看这个:Layout组件之Transform:https://www.jianshu.com/p/760ecb56b78a,根据我的理解,这个就是静态的,需要动态就用动画组件Animation。
OK,问题已定位,如果不太会写的(像我这样)或者懒得自己写的,就再问AI嘛:
问:
使用flutter与dart写一个提供按钮控制的动画:一张宽为200,高为300像素的图经过3秒放大1倍
答:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Animation Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 3),
vsync: this,
);
_animation = Tween<double>(begin: 0.25, end: 0.5).animate(_controller);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Animation Demo'),
),
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (BuildContext context, Widget child) {
return Container(
width: MediaQuery.of(context).size.width * _animation.value,
height: MediaQuery.of(context).size.height * _animation.value,
color: Colors.blue,
);
},
),
),
);
}
}
解释:
在这个示例中,我们使用了AnimationController和Tween来创建一个动画。AnimationController控制动画的时间和状态,Tween定义了动画的起始值和结束值。我们通过AnimatedBuilder来构建动画的UI,根据动画的值来更新图像的宽度和高度。
当运行这个示例时,你将看到一张初始宽高为屏幕1/4的蓝色图像,然后在3秒内逐渐放大到屏幕的一半大小。
再通过一番修改,改成自己想要的:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'List Animation',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with TickerProviderStateMixin {
late AnimationController _acRotate; //旋转控制
late AnimationController _acMagnify; //放大控制
late Animation<double> _aManify; //放大动画
int _currentPage = 0;
final PageController _pageController = PageController(
viewportFraction: 0.32,
initialPage: 0,
);
List<String> imgUrl=['static/img/lake.png','static/img/lake1.png','static/img/lake2.png','static/img/lake3.png'];
@override
void initState() {
super.initState();
_pageController.addListener(() {
setState(() {
_currentPage = _pageController.page!.round();
});
});
_acRotate = AnimationController(
duration: const Duration(seconds: 6),
vsync: this,
)..repeat();
_acMagnify = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_aManify = Tween<double>(begin: 1.0, end: 1.4).animate(_acMagnify);
_acMagnify.forward();
}
@override
void dispose() {
_acRotate.dispose();
_acRotate.dispose(); // 释放动画资源
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Good Animation'),
),
body: PageView.builder(
controller: _pageController,
itemCount: imgUrl.length,
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
itemBuilder: (context, index) {
return index == _currentPage
? AnimatedBuilder(
animation: _acRotate,
builder: (context, child) {
return Transform.rotate(
angle: _acRotate.value * 2 * 3.1415926,
// 设置旋转角度
child: AnimatedBuilder(
animation: _acRotate,
builder: (context, child) {
return Image.asset(
imgUrl[index],
width: 200 * _aManify.value,
height: 200 * _aManify.value,
);
}),
);
})
: Stack(
alignment: Alignment.center,
children: [
Image.asset(
imgUrl[index],
width: 200,
height: 200,
),
GestureDetector(
onTap: () async {
if (index == _currentPage - 1) {
//向右滑动一格
_pageController.previousPage(
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
} else if (index == _currentPage + 1) {
//向左滑动一格
_pageController.nextPage(
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
} else {
print('应该出错了');
}
},
)
],
);
},
),
);
}
}
效果: