如何使用Flutter实现一个走马灯布局-创新互联

这篇文章给大家分享的是有关如何使用Flutter实现一个走马灯布局的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

10年积累的成都网站建设、成都网站制作经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站制作后付款的网站建设流程,更有凌云免费网站建设让你可以放心的选择与我们合作。

走马灯是一种常见的效果,本文讲一下如何用PageViewFlutter 里实现一个走马灯, 效果如下,当前页面的高度比其它页面高,切换页面的时候有一个高度变化的动画。实现这样的效果主要用到的是PageView.builder 部件。

开发

创建首页

首先创建一个IndexPage 部件,这个部件用来放PageView ,因为需要使用setState 方法更新 UI,所以它是 stateful 的。

import 'package:flutter/material.dart';class IndexPage extends StatefulWidget { @override _IndexPageState createState() => _IndexPageState();}class _IndexPageState extends State { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0.0, backgroundColor: Colors.white, ), body: Column( children: [], ), ); }}

然后在部件内申明一个_pageIndex 变量用来保存当前显示的页面的 index,在initState 生命周期里面初始化一个PageController 用来配置PageView 部件。

bodyColumn 里面创建一个PageView.builder ,使用一个SizedBox 部件指定PageView 的高度,将controller 设置为_pageController ,在onPageChanged 事件里将当前显示页面的index 值赋值给_pageIndex 变量。

int _pageIndex = 0;PageController _pageController;@overridevoid initState() { super.initState(); _pageController = PageController( initialPage: 0, viewportFraction: 0.8, );}body: Column( children: [ SizedBox( height: 580.0, child: PageView.builder( itemCount: 3, pageSnapping: true, controller: _pageController, onPageChanged: (int index) {  setState(() {  _pageIndex = index;  }); }, itemBuilder: (BuildContext ctx, int index) {  return _buildItem(_pageIndex, index); }, ), ), ],),

关键点: 设置PageControllerviewportFraction 参数小于 1,这个值是用来设置每个页面在屏幕上显示的比例,小于 1 的话,就可以在当前页面同时显示其它页面的内容了。

/// The fraction of the viewport that each page should occupy./// Defaults to 1.0, which means each page fills the viewport in the scrolling direction.final double viewportFraction;

实现 _buildItem

接着实现_buildItem 方法,这个方法就是返回PageView.builder 里每一个页面渲染的内容,第一个参数activeIndex 是当前显示在屏幕上页面的index ,第二个参数index 是每一项自己的index

使用一个Center 部件让内容居中显示,然后用一个AnimatedContainer 添加页面切换时的高度变化的动画效果,切换页面的时候使用了setState 方法改变了_pageIndexFlutter 重新绘制每一项。关键点在于判断当前页面是否为正在显示的页面,是的话它的高度就是 500 不是的话就是 450。

_buildItem(activeIndex, index) { return Center( child: AnimatedContainer( curve: Curves.easeInOut, duration: Duration(milliseconds: 300), height: activeIndex == index ? 500.0 : 450.0, margin: EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0), decoration: BoxDecoration( color: heroes[index].color, borderRadius: BorderRadius.all(Radius.circular(12.0)), ), child: Stack(), ), );}

添加内容

然后给AnimatedContainer 添加每一项的内容

child: Stack( fit: StackFit.expand, children: [ ClipRRect( borderRadius: BorderRadius.all( Radius.circular(12.0), ), child: Image.network( heroes[index].image, fit: BoxFit.cover, ), ), Align( alignment: Alignment.bottomCenter, child: Row( children: [  Expanded(  child: Container(  padding: EdgeInsets.all(12.0),  decoration: BoxDecoration(  color: Colors.black26,  borderRadius: BorderRadius.only(   bottomRight: Radius.circular(12.0),   bottomLeft: Radius.circular(12.0),  ),  ),  child: Text(  heroes[index].title,  textAlign: TextAlign.center,  style: TextStyle(   fontSize: 20.0,   fontWeight: FontWeight.bold,   color: Colors.white,  ),  ),  ),  ) ], ), ), ],),

实现指示器

然后实现页面的指示器,创建一个PageIndicator 部件,需要传入pageCount 表示总页数,以及currentIndex 表示当前显示的页数索引。把所有指示器放在一个Row 部件里,判断当前指示器的index 是否为正在显示页面的index ,是的话显示较深的颜色。

class PageIndicator extends StatelessWidget { final int pageCount; final int currentIndex; const PageIndicator(this.currentIndex, this.pageCount); Widget _indicator(bool isActive) { return Container( width: 6.0, height: 6.0, margin: EdgeInsets.symmetric(horizontal: 3.0), decoration: BoxDecoration( color: isActive ? Color(0xff666a84) : Color(0xffb9bcca), shape: BoxShape.circle, boxShadow: [  BoxShadow(  color: Colors.black12,  offset: Offset(0.0, 3.0),  blurRadius: 3.0,  ), ], ), ); } List _buildIndicators() { List indicators = []; for (int i = 0; i < pageCount; i++) { indicators.add(i == currentIndex ? _indicator(true) : _indicator(false)); } return indicators; } @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: _buildIndicators(), ); }}

添加PageIndicatorSizedBox 下面

封装 Carousel

最后的最后优化一下代码,把部件封装一下,让它成为一个单独的部件,创建一个Carousel 部件,对外暴露itemsheight 两个属性,分别配置数据和高度。

class Carousel extends StatefulWidget { final List items; final double height; const Carousel({ @required this.items, @required this.height, }); @override _CarouselState createState() => _CarouselState();}class _CarouselState extends State { int _pageIndex = 0; PageController _pageController; Widget _buildItem(activeIndex, index) { final items = widget.items; return Center( child: AnimatedContainer( curve: Curves.easeInOut, duration: Duration(milliseconds: 300), height: activeIndex == index ? 500.0 : 450.0, margin: EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0), decoration: BoxDecoration(  color: items[index].color,  borderRadius: BorderRadius.all(Radius.circular(12.0)), ), child: Stack(  fit: StackFit.expand,  children: [  ClipRRect(  borderRadius: BorderRadius.all(  Radius.circular(12.0),  ),  child: Image.network(  items[index].image,  fit: BoxFit.cover,  ),  ),  Align(  alignment: Alignment.bottomCenter,  child: Row(  children: [   Expanded(   child: Container(   padding: EdgeInsets.all(12.0),   decoration: BoxDecoration(   color: Colors.black26,   borderRadius: BorderRadius.only(    bottomRight: Radius.circular(12.0),    bottomLeft: Radius.circular(12.0),   ),   ),   child: Text(   items[index].title,   textAlign: TextAlign.center,   style: TextStyle(    fontSize: 20.0,    fontWeight: FontWeight.bold,    color: Colors.white,   ),   ),   ),   )  ],  ),  ),  ], ), ), ); } @override void initState() { super.initState(); _pageController = PageController( initialPage: 0, viewportFraction: 0.8, ); } @override Widget build(BuildContext context) { return Column( children: [ Container(  height: widget.height,  child: PageView.builder(  pageSnapping: true,  itemCount: heroes.length,  controller: _pageController,  onPageChanged: (int index) {  setState(() {  _pageIndex = index;  });  },  itemBuilder: (BuildContext ctx, int index) {  return _buildItem(_pageIndex, index);  },  ), ), PageIndicator(_pageIndex, widget.items.length), ], ); }}

之后在IndexPage 部件里就只用实例化一个Carousel 了,同时由于IndexPage 不用管理部件状态了,可以将它变成StatelessWidget

完整代码

import 'package:flutter/material.dart';class Hero { final Color color; final String image; final String title; Hero({ @required this.color, @required this.image, @required this.title, });}List heroes = [ Hero( color: Color(0xFF86F3FB), image: "/tupian/20230522/404.html title: '寒冰射手-艾希', ), Hero( color: Color(0xFF7D6588), image: "/tupian/20230522/404.html title: '刀锋舞者-艾瑞莉娅', ), Hero( color: Color(0xFF4C314D), image: "/tupian/20230522/404.html title: '九尾妖狐-阿狸', ),];class Carousel extends StatefulWidget { final List items; final double height; const Carousel({ @required this.items, @required this.height, }); @override _CarouselState createState() => _CarouselState();}class _CarouselState extends State { int _pageIndex = 0; PageController _pageController; Widget _buildItem(activeIndex, index) { final items = widget.items; return Center( child: AnimatedContainer( curve: Curves.easeInOut, duration: Duration(milliseconds: 300), height: activeIndex == index ? 500.0 : 450.0, margin: EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0), decoration: BoxDecoration(  color: items[index].color,  borderRadius: BorderRadius.all(Radius.circular(12.0)), ), child: Stack(  fit: StackFit.expand,  children: [  ClipRRect(  borderRadius: BorderRadius.all(  Radius.circular(12.0),  ),  child: Image.network(  items[index].image,  fit: BoxFit.cover,  ),  ),  Align(  alignment: Alignment.bottomCenter,  child: Row(  children: [   Expanded(   child: Container(   padding: EdgeInsets.all(12.0),   decoration: BoxDecoration(   color: Colors.black26,   borderRadius: BorderRadius.only(    bottomRight: Radius.circular(12.0),    bottomLeft: Radius.circular(12.0),   ),   ),   child: Text(   items[index].title,   textAlign: TextAlign.center,   style: TextStyle(    fontSize: 20.0,    fontWeight: FontWeight.bold,    color: Colors.white,   ),   ),   ),   )  ],  ),  ),  ], ), ), ); } @override void initState() { super.initState(); _pageController = PageController( initialPage: 0, viewportFraction: 0.8, ); } @override Widget build(BuildContext context) { return Column( children: [ Container(  height: widget.height,  child: PageView.builder(  pageSnapping: true,  itemCount: heroes.length,  controller: _pageController,  onPageChanged: (int index) {  setState(() {  _pageIndex = index;  });  },  itemBuilder: (BuildContext ctx, int index) {  return _buildItem(_pageIndex, index);  },  ), ), PageIndicator(_pageIndex, widget.items.length), ], ); }}class PageIndicator extends StatelessWidget { final int currentIndex; final int pageCount; const PageIndicator(this.currentIndex, this.pageCount); Widget _indicator(bool isActive) { return Container( width: 6.0, height: 6.0, margin: EdgeInsets.symmetric(horizontal: 3.0), decoration: BoxDecoration( color: isActive ? Color(0xff666a84) : Color(0xffb9bcca), shape: BoxShape.circle, boxShadow: [  BoxShadow(  color: Colors.black12,  offset: Offset(0.0, 3.0),  blurRadius: 3.0,  ), ], ), ); } List _buildIndicators() { List indicators = []; for (int i = 0; i < pageCount; i++) { indicators.add(i == currentIndex ? _indicator(true) : _indicator(false)); } return indicators; } @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: _buildIndicators(), ); }}class IndexPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0.0, backgroundColor: Colors.white, ), body: Carousel( height: 540, items: heroes, ), backgroundColor: Colors.white, ); }}

感谢各位的阅读!关于“如何使用Flutter实现一个走马灯布局”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!


网站名称:如何使用Flutter实现一个走马灯布局-创新互联
当前链接:http://myzitong.com/article/edehi.html