1. 程式人生 > 程式設計 >使用Flutter實現一個走馬燈佈局的示例程式碼

使用Flutter實現一個走馬燈佈局的示例程式碼

走馬燈是一種常見的效果,本文講一下如何用 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<IndexPage> {
 @override
 Widget build(BuildContext context) {
 return Scaffold(
 appBar: AppBar(
 elevation: 0.0,backgroundColor: Colors.white,),body: Column(
 children: <Widget>[],);
 }
}

然後在部件內申明一個 _pageIndex 變數用來儲存當前顯示的頁面的 index,在 initState 生命週期裡面初始化一個 PageController 用來配置 PageView 部件。

bodyColumn 裡面建立一個 PageView.builder ,使用一個 SizedBox 部件指定 PageView 的高度,將 controller 設定為 _pageController ,在 onPageChanged 事件裡將當前顯示頁面的 index 值賦值給 _pageIndex 變數。

int _pageIndex = 0;
PageController _pageController;

@override
void initState() {
 super.initState();
 _pageController = PageController(
 initialPage: 0,viewportFraction: 0.8,);
}

body: Column(
 children: <Widget>[
 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: <Widget>[
 ClipRRect(
 borderRadius: BorderRadius.all(
 Radius.circular(12.0),child: Image.network(
 heroes[index].image,fit: BoxFit.cover,Align(
 alignment: Alignment.bottomCenter,child: Row(
 children: <Widget>[
  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<Widget> _buildIndicators() {
 List<Widget> 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<Carousel> {
 int _pageIndex = 0;
 PageController _pageController;

 Widget _buildItem(activeIndex,index) {
 final items = widget.items;

 return Center(
 child: AnimatedContainer(
 curve: Curves.easeInOut,decoration: BoxDecoration(
  color: items[index].color,child: Stack(
  fit: StackFit.expand,children: <Widget>[
  ClipRRect(
  borderRadius: BorderRadius.all(
  Radius.circular(12.0),child: Image.network(
  items[index].image,Align(
  alignment: Alignment.bottomCenter,child: Row(
  children: <Widget>[
   Expanded(
   child: Container(
   padding: EdgeInsets.all(12.0),decoration: BoxDecoration(
   color: Colors.black26,borderRadius: BorderRadius.only(
    bottomRight: Radius.circular(12.0),child: Text(
   items[index].title,style: TextStyle(
    fontSize: 20.0,)
  ],);
 }

 @override
 void initState() {
 super.initState();
 _pageController = PageController(
 initialPage: 0,);
 }

 @override
 Widget build(BuildContext context) {
 return Column(
 children: <Widget>[
 Container(
  height: widget.height,child: PageView.builder(
  pageSnapping: true,itemCount: heroes.length,onPageChanged: (int index) {
  setState(() {
  _pageIndex = index;
  });
  },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: "https://game.gtimg.cn/images/lol/act/img/skin/big22009.jpg",title: '寒冰射手-艾希',Hero(
 color: Color(0xFF7D6588),image: "https://game.gtimg.cn/images/lol/act/img/skin/big39006.jpg",title: '刀鋒舞者-艾瑞莉婭',Hero(
 color: Color(0xFF4C314D),image: "https://game.gtimg.cn/images/lol/act/img/skin/big103015.jpg",title: '九尾妖狐-阿狸',];

class Carousel extends StatefulWidget {
 final List items;
 final double height;

 const Carousel({
 @required this.items,);
 }
}

class PageIndicator extends StatelessWidget {
 final int currentIndex;
 final int pageCount;

 const PageIndicator(this.currentIndex,);
 }
}

class IndexPage extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
 return Scaffold(
 appBar: AppBar(
 elevation: 0.0,body: Carousel(
 height: 540,items: heroes,);
 }
}

至此,整個佈局就完成了! :sunglasses:

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。