使用flutter 写一个简单的购买电影票app

概要

这个案例没有使用任何插件,所以写着有点麻烦, 

整体架构流程

综合使用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,可以用来提升自己的布局,代码并没有写成组件化,所以不适合用来做项目参照,只是用来练手的, 谢谢

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值