概要
这个案例没有使用任何插件,所以写着有点麻烦,
整体架构流程
综合使用flutter中的各种widget
这个页面是通过pageView来实现的,左右滑动切换主页概括,使用堆叠布局给图片加一点遮罩效果,然后当切换的时候获取 索引值,然后切换, 数据是模拟动态数据
import 'package:flutter/material.dart';
import 'package:movie/data.dart';
import 'package:movie/widget/start.dart';
import 'details.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
var _movieData = MovieData().movieList;
int currentIndex = 0;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView.builder(
physics: BouncingScrollPhysics(),
itemCount: _movieData.length,
itemBuilder: (context,index){
return listPageView(index);
})
);
}
Widget listPageView(int index){
return Stack(
children: [
Container(
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("${_movieData[index].image}"),fit: BoxFit.cover
)
),
),
Container(
color: Colors.black.withOpacity(0.5),
),
Container(
height: double.infinity,
width: double.infinity,
child: Column(
children: [
SizedBox(height: 20),
Row(
children: [
Expanded(
flex: 1,
child: Container(
margin: EdgeInsets.only(left: 15),
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.white.withOpacity(0.5),width: 2)
),
child: Icon(Icons.clear_all,color: Colors.white,),
)
),
Expanded(
flex: 6,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 25),
height: 50,
decoration: BoxDecoration(
border: Border.all(color: Colors.white.withOpacity(0.7),width: 2),
borderRadius: BorderRadius.circular(8)
),
child: TextField(
decoration: InputDecoration(
hintText: "Search Movies",
contentPadding: EdgeInsets.only(left: 15,top: 15),
hintStyle: TextStyle(color: Colors.white),
suffixIcon: Icon(Icons.search,color: Colors.white,)
),
),
)
)
],
),
SizedBox(height: 70),
Image.asset("assets/image/togologo.png",height: 100,width: 150),
SizedBox(height: 10),
Text("Drama",style: TextStyle(color: Colors.white)),
Container(
height: 20,
width: 100,
child: StartWidget(rating: _movieData[index].rating),
),
SizedBox(height: 20),
GestureDetector(
onTap: (){
Navigator.push(context, MaterialPageRoute(
builder: (context){
return DetailsScreen(movie: _movieData[index],);
}
));
},
child: Container(
height: 35,
width: 80,
decoration:BoxDecoration(
color: Color(0xffb63831),
borderRadius: BorderRadius.circular(12)
),
alignment: Alignment.center,
child: Text("BUY TICKET",style: TextStyle(fontSize: 12,color: Colors.white,fontWeight: FontWeight.bold),),
),
),
SizedBox(height: 10),
Container(
height: 260,
width: 180,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("${_movieData[index].image}"),fit: BoxFit.cover
),
borderRadius: BorderRadius.circular(20)
),
),
SizedBox(height: 20),
Text("${_movieData[index].name}",
style: TextStyle(fontSize: 20,color: Colors.white,fontWeight: FontWeight.bold),)
],
),
)
],
);
}
}
当写完这个页面时,点击buy ticket按钮进入到详情页出现一个动画, 使用TweenAnimationBuilder,
一个将背景进行缩放动画,一个将详情列表进行从下到上的动画效果
import 'package:flutter/material.dart';
import 'package:movie/data.dart';
import 'package:movie/screens/ticket_screen.dart';
import 'package:movie/widget/start.dart';
class DetailsScreen extends StatefulWidget {
const DetailsScreen({Key? key,required this.movie}) : super(key: key);
final MovieModel movie;
@override
State<DetailsScreen> createState() => _DetailsScreenState();
}
class _DetailsScreenState extends State<DetailsScreen> {
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
child: Stack(
children: [
Positioned(
top: -48,
bottom: 0,
child: TweenAnimationBuilder(
duration: Duration(milliseconds: 700),
tween: Tween(begin: 0.25,end: 1),
builder: (_,value, child){
return Transform.scale(scale: double.parse(value.toString()),child: child,);
},
child: Stack(
children: [
Image.asset(
widget.movie.image,
width: size.width,
height: size.height,
fit: BoxFit.cover,
),
Container(
width: size.width,
height: size.height,
color: Colors.black.withOpacity(0.6),
)
],
),
)
),
Positioned(
top: 50,
left: 20,
child: GestureDetector(
onTap: (){
Navigator.pop(context);
},
child: Container(
height: 50,
width: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withOpacity(0.7))
),
alignment: Alignment.center,
child: Icon(Icons.arrow_back_ios,color: Colors.white,),
),
),
),
Positioned(
top: 75,
left: 110,
child:Image.asset(
widget.movie.imageLogo,
height: 130,width: 200,)),
Positioned(
bottom: 0,
child: TweenAnimationBuilder(
tween: Tween(begin: size.height/2, end: 0.0),
duration: Duration(milliseconds: 500),
builder: (_,value,child){
return Transform.translate(offset: Offset(0,double.parse(value.toString())),child: child,);
},
child: Container(
height: 470,
width: size.width,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50)
)
),
child: Column(
children: [
Container(
height: 470,
padding: EdgeInsets.symmetric(horizontal: 10),
child: ListView(
physics: BouncingScrollPhysics(),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
widget.movie.name,
style: TextStyle(fontSize: 20,fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
Text(
widget.movie.genre[0],
style: TextStyle(fontSize: 12),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(widget.movie.rating.toString(),
style: TextStyle(fontSize: 20,fontWeight: FontWeight.bold)),
SizedBox(width: 5),
StartWidget(rating: widget.movie.rating)
],
),
SizedBox(height: 10),
Text("Director: ${widget.movie.director}",style: TextStyle(fontSize: 16)),
SizedBox(height: 20),
Row(
children: const [
Text("Actors",style: TextStyle(fontSize: 20,fontWeight: FontWeight.bold))
],
),
Row(
children: List.generate(widget.movie.castList.length, (index){
return Container(
width: 80,
height: 105,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 60,
width: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: DecorationImage(
image: AssetImage(widget.movie.castList[index].photo),fit: BoxFit.cover
)
),
),
Container(
width: 70,
height: 40,
margin: EdgeInsets.only(top: 5),
child: Text(
widget.movie.castList[index].name,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12,color: Colors.grey),
),
)
],
),
);
}),
),
SizedBox(height: 10),
Row(
children: [
Text("Story Line",style: TextStyle(fontSize: 18,fontWeight: FontWeight.bold)),
],
),
SizedBox(height: 5),
Container(
width: 380,
height: 100,
child: Text(widget.movie.storyLine,
style: TextStyle(color: Colors.grey)),
),
GestureDetector(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context){
return TicketScreen();
}));
},
child: Container(
height: 50,
width: 390,
decoration: BoxDecoration(
color: Color(0xffb53831),
borderRadius: BorderRadius.circular(12)
),
alignment: Alignment.center,
child: Text("Buy Ticket",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 16),),
),
)
],
)
],
),
)
],
),
),
),
)
],
),
)
);
}
}
这里面的人物是模拟假数据,可以自己使用自己的数据模拟,
点击Buy Ticket进入到购票页面,购票页面使用了Tab切换用来选择日期,将tab的的元素设置为Container控制宽高,当点击选择的时候将索引给Container切换为选择的样式,选择时间的这个功能,使用的是ListView这个,也是假数据生成,给Container设置一个点击事件GestureDetector用来切换选择不同的时间样式,座位样式通过使用数组来计算的
import 'package:flutter/material.dart';
class TicketScreen extends StatefulWidget {
const TicketScreen({Key? key}) : super(key: key);
@override
State<TicketScreen> createState() => _TicketScreenState();
}
class _TicketScreenState extends State<TicketScreen> with SingleTickerProviderStateMixin{
DateTime currentDate = DateTime.now();
late TabController _tabController;
String _dayFormat(int index){
switch(index){
case 1:
return "MO";
case 2:
return "TU";
case 3:
return "WE";
case 4:
return "TH";
case 5:
return "FR";
case 6:
return "SA";
case 7:
return "SU";
default:
return "MO";
}
}
@override
void initState() {
_tabController = TabController(length: 7,vsync: this);
super.initState();
}
int select = 0;
var time = [
["01:30","5"],
["06:30","10"],
["10:30","15"],
["12:30","20"],
];
int selectTime = 0;
Widget BuildChairs(Color color,bool isBorder){
return Container(
height: 10,
width: 10,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(6),
border: Border.all(width: 2,color:isBorder ? Colors.white.withOpacity(0.6) : Colors.black)
),
);
}
Widget _charList(){
Size size = MediaQuery.of(context).size;
var _ChairStatus = [
[1,1,1,1,1,1,1],
[1,1,1,1,3,1,1],
[1,1,1,1,1,3,3],
[2,2,2,1,3,1,1],
[1,1,1,1,1,1,1],
[1,1,1,1,1,1,1],
];
return Container(
child: Column(
children: <Widget>[
for(int i = 0; i < 6; i++)
Container(
margin: EdgeInsets.only(top: i == 3 ? size.height * .02 : 0),
child: Row(
children: <Widget>[
for(int x = 0; x < 9; x++)
Expanded(
flex: x == 0 || x == 8 ? 2 : 1,
child: x == 0 ||
x == 8 ||
(i == 0 && x == 1) ||
(i == 0 && x == 7) ||
(x == 4) ? Container() :
Container(
height: size.width / 11 - 10,
margin: EdgeInsets.all(5),
child: _ChairStatus[i][x - 1] == 1 ?
BuildChairs(Colors.black,true) :
_ChairStatus[i][x - 1] == 2
? BuildChairs(Colors.orange,false) :
BuildChairs(Colors.white,false)
),
)
],
),
)
],
),
);
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
color: Colors.black,
child: Column(
children: [
SizedBox(height: 50),
Container(
height: 50,
width: double.infinity,
child: Stack(
alignment: Alignment.center,
children: [
Text("Togo",style: TextStyle(fontSize: 20,fontWeight: FontWeight.bold,color: Colors.white)),
Positioned(
left: 24,
child: GestureDetector(
onTap: (){
Navigator.of(context).pop();
},
child: Container(
height: 50,
width: 50,
decoration: BoxDecoration(
border: Border.all(color: Colors.white.withOpacity(0.6)),
borderRadius: BorderRadius.circular(12)
),
child: Icon(Icons.arrow_back_ios,color: Colors.white,),
),
),
),
],
),
),
SizedBox(height: 20),
Container(
height: 80,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Color(0xff1a1b1a),
),
margin: EdgeInsets.symmetric(horizontal: 20),
child: TabBar(
isScrollable: true,
controller: _tabController,
labelColor: Colors.black,
onTap: (index){
setState(() {
select = index;
});
},
indicatorColor: Colors.transparent,
unselectedLabelColor: Colors.white,
tabs: List.generate(7, (index){
var date = currentDate.add(Duration(days: index));
return Tab(
height: 60,
child: Container(
width: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color:select == index ? Color(0xffeeb442) : Colors.transparent,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(date.day.toString()),
SizedBox(height: 5),
Text(_dayFormat(date.weekday),style: TextStyle(color:
select == index ? Colors.black : Colors.grey))
],
),
),
);
}),
),
),
SizedBox(height: 10),
Container(
width: double.infinity,
height: 50,
margin: EdgeInsets.only(left: 30),
child: ListView(
scrollDirection: Axis.horizontal,
children: List.generate(time.length, (index){
return GestureDetector(
onTap: (){
setState(() {
selectTime = index;
});
},
child: Container(
width: 100,
margin: EdgeInsets.only(right: 20),
decoration: BoxDecoration(
border: Border.all(color:selectTime == index ? Colors.orange : Colors.white),
borderRadius: BorderRadius.circular(8)
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(time[index][0],style: TextStyle(fontSize: 16,color:selectTime == index ? Colors.orange : Colors.white)),
SizedBox(width: 5),
Text("Pm",style: TextStyle(color:selectTime == index ? Colors.orange : Colors.white)),
],
),
Text("from \$${time[index][1]}",style: TextStyle(color: Colors.grey,fontSize: 12))
],
),
),
);
}),
),
),
SizedBox(height: 10),
Row(
children: [
SizedBox(width: 20),
Icon(Icons.location_on,color: Colors.white),
SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Carnival Cinemas",style: TextStyle(color: Colors.white)),
Text("Kodungallur, Kerala, India",style: TextStyle(color: Colors.grey),)
],
)
],
),
SizedBox(height: 10),
Container(
height: 5,
width: 200,
color: Colors.white,
),
Container(
height: 40,
width: size.width * 0.65,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50)
),
gradient: LinearGradient(
colors: [Colors.white,Colors.transparent],
stops: [0,1],
begin: Alignment.topCenter,
end: Alignment.bottomCenter
)
),
),
Container(
height: 250,
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
bottom: size.height * .02,
child: Container(width: size.width, child: _charList())),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
Container(
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.black,
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(30)
),
),
SizedBox(width: 10),
Text("Avaliable",style: TextStyle(color: Colors.white),)
],
),
Row(
children: [
Container(
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(30)
),
),
SizedBox(width: 10),
Text("Resvered",style: TextStyle(color: Colors.white),)
],
),
Row(
children: [
Container(
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.black,
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(30)
),
),
SizedBox(width: 10),
Text("Selected",style: TextStyle(color: Colors.white),)
],
),
],
),
SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("\$20.00",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 25)),
Container(
height: 40,
width: 200,
decoration: BoxDecoration(
color: Color(0xffb63831),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30)
)
),
alignment: Alignment.center,
child: Text("Pay",style: TextStyle(fontSize: 30,fontWeight: FontWeight.bold,color: Colors.white),),
)
],
)
],
),
),
);
}
}
小结
本案例是综合于flutter中常用的一些widget,可以用来提升自己的布局,代码并没有写成组件化,所以不适合用来做项目参照,只是用来练手的, 谢谢