註意:無特殊說明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 基礎用法 PageView控制項可以實現一個“圖片輪播”的效果,PageView不僅可以水平滑動也可以垂直滑動,簡單用法如下: PageView滾動方向預設是水 ...
註意:無特殊說明,Flutter版本及Dart版本如下:
- Flutter版本: 1.12.13+hotfix.5
- Dart版本: 2.7.0
基礎用法
PageView控制項可以實現一個“圖片輪播”的效果,PageView不僅可以水平滑動也可以垂直滑動,簡單用法如下:
PageView(
children: <Widget>[
MyPage1(),
MyPage2(),
MyPage3(),
],
)
PageView滾動方向預設是水平,可以設置其為垂直方向:
PageView(
scrollDirection: Axis.vertical,
...
)
PageView配合PageController可以實現非常酷炫的效果,控制每一個Page不占滿,
PageView(
controller: PageController(
viewportFraction: 0.9,
),
...
)
PageController中屬性initialPage
表示當前載入第幾頁,預設第一頁。
onPageChanged
屬性是頁面發生變化時的回調,用法如下:
PageView(
onPageChanged: (int index){
},
...
)
無限滾動
PageView滾動到最後時希望滾動到第一個頁面,這樣看起來PageView是無限滾動的:
List<Widget> pageList = [PageView1(), PageView2(), PageView3()];
PageView.builder(
itemCount: 10000,
itemBuilder: (context, index) {
return pageList[index % (pageList.length)];
},
)
巧妙的利用取餘重覆構建頁面實現PageView無限滾動的效果:
實現指示器
指示器顯示總數和當前位置,通過onPageChanged
確定當前頁數並更新指示器。
List<String> pageList = ['PageView1', 'PageView2', 'PageView3'];
int _currentPageIndex = 0;
_buildPageView() {
return Center(
child: Container(
height: 230,
child: Stack(
children: <Widget>[
PageView.builder(
onPageChanged: (int index) {
setState(() {
_currentPageIndex = index % (pageList.length);
});
},
itemCount: 10000,
itemBuilder: (context, index) {
return _buildPageViewItem(pageList[index % (pageList.length)]);
},
),
Positioned(
bottom: 10,
left: 0,
right: 0,
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(pageList.length, (i) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 5),
width: 10,
height: 10,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _currentPageIndex == i
? Colors.blue
: Colors.grey),
);
}).toList(),
),
),
),
],
),
),
);
}
_buildPageViewItem(String txt, {Color color = Colors.red}) {
return Container(
color: color,
alignment: Alignment.center,
child: Text(
txt,
style: TextStyle(color: Colors.white, fontSize: 28),
),
);
}
效果如下:
切換動畫
如此常見的切換效果顯然不能體驗我們獨特的個性,我們需要更炫酷的方式,看下麵的效果:
在滑出的時候當前頁面逐漸縮小並居中,通過給PageController添加監聽獲取當前滑動的進度:
_pageController.addListener(() {
setState(() {
_currPageValue = _pageController.page;
});
});
通過當前的進度計算各個頁面的縮放繫數及平移繫數,通過 判斷當前構建的是哪個頁面
if (index == _currPageValue.floor()) {
//當前的item
var currScale = 1 - (_currPageValue - index) * (1 - _scaleFactor);
} else if (index == _currPageValue.floor() + 1) {
//右邊的item
} else if (index == _currPageValue.floor() - 1) {
//左邊
} else {
//其他,不在屏幕顯示的item
}
通過對這幾種類型的頁面的縮放和平移達到我們想要的效果。
完整代碼:
class ViewPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _ViewPageState();
}
class _ViewPageState extends State<ViewPage> {
var imgList = [
'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2877516247,37083492&fm=26&gp=0.jpg',
'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1582796218195&di=04ce93c4ac826e19067e71f916cec5d8&imgtype=0&src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F344fda8b47808261c946c81645bff489c008326f15140-koiNr3_fw658'
];
PageController _pageController;
var _currPageValue = 0.0;
//縮放繫數
double _scaleFactor = .8;
//view page height
double _height = 230.0;
@override
void initState() {
super.initState();
_pageController = PageController(viewportFraction: 0.9);
_pageController.addListener(() {
setState(() {
_currPageValue = _pageController.page;
});
});
}
@override
void dispose() {
super.dispose();
_pageController.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
height: _height,
child: PageView.builder(
itemBuilder: (context, index) => _buildPageItem(index),
itemCount: 10,
controller: _pageController,
));
}
_buildPageItem(int index) {
Matrix4 matrix4 = Matrix4.identity();
if (index == _currPageValue.floor()) {
//當前的item
var currScale = 1 - (_currPageValue - index) * (1 - _scaleFactor);
var currTrans = _height * (1 - currScale) / 2;
matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
..setTranslationRaw(0.0, currTrans, 0.0);
} else if (index == _currPageValue.floor() + 1) {
//右邊的item
var currScale =
_scaleFactor + (_currPageValue - index + 1) * (1 - _scaleFactor);
var currTrans = _height * (1 - currScale) / 2;
matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
..setTranslationRaw(0.0, currTrans, 0.0);
} else if (index == _currPageValue.floor() - 1) {
//左邊
var currScale = 1 - (_currPageValue - index) * (1 - _scaleFactor);
var currTrans = _height * (1 - currScale) / 2;
matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0)
..setTranslationRaw(0.0, currTrans, 0.0);
} else {
//其他,不在屏幕顯示的item
matrix4 = Matrix4.diagonal3Values(1.0, _scaleFactor, 1.0)
..setTranslationRaw(0.0, _height * (1 - _scaleFactor) / 2, 0.0);
}
return Transform(
transform: matrix4,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
image: DecorationImage(
image: NetworkImage(imgList[index % 2]), fit: BoxFit.fill),
),
),
),
);
}
}
推薦幾款Github上帶動畫效果的PageView
更多相關閱讀:
- Flutter系列文章總覽
- Flutter Widgets 之 Expanded和Flexible
- Flutter Widgets 之 AnimatedList
- Flutter Widgets 之 SliverAppBar