02.29 Flutter Widgets 之 PageView

Flutter Widgets 之 PageView

注意:無特殊說明,Flutter版本及Dart版本如下:

- Flutter版本: 1.12.13+hotfix.5

- Dart版本: 2.7.0

基礎用法

PageView控件可以實現一個“圖片輪播”的效果,PageView不僅可以水平滑動也可以垂直滑動,簡單用法如下:

<code>PageView(    children: <widget>[        MyPage1(),            MyPage2(),         MyPage3(),        ],)/<widget>/<code>
Flutter Widgets 之 PageView

PageView滾動方向默認是水平,可以設置其為垂直方向:

<code>PageView(    scrollDirection: Axis.vertical,    ...)/<code>

PageView配合PageController可以實現非常酷炫的效果,控制每一個Page不佔滿,

<code>PageView(    controller: PageController(        viewportFraction: 0.9,    ),    ...)/<code>
Flutter Widgets 之 PageView

PageController中屬性initialPage表示當前加載第幾頁,默認第一頁。

onPageChanged屬性是頁面發生變化時的回調,用法如下:

<code>PageView(    onPageChanged: (int index){    },    ...)/<code>


無限滾動

PageView滾動到最後時希望滾動到第一個頁面,這樣看起來PageView是無限滾動的:

<code>List<widget> pageList = [PageView1(), PageView2(), PageView3()];PageView.builder(    itemCount: 10000,    itemBuilder: (context, index) {        return pageList[index % (pageList.length)];    },)/<widget>/<code>

巧妙的利用取餘重複構建頁面實現PageView無限滾動的效果:

Flutter Widgets 之 PageView

實現指示器

指示器顯示總數和當前位置,通過onPageChanged確定當前頁數並更新指示器。

<code>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),      ),    );  }/<widget>/<string>/<code>

效果如下:

Flutter Widgets 之 PageView


切換動畫

如此常見的切換效果顯然不能體驗我們獨特的個性,我們需要更炫酷的方式,看下面的效果:

Flutter Widgets 之 PageView

在滑出的時候當前頁面逐漸縮小並居中,通過給PageController添加監聽獲取當前滑動的進度:

<code>_pageController.addListener(() {      setState(() {        _currPageValue = _pageController.page;      });    });/<code>

通過當前的進度計算各個頁面的縮放係數及平移係數,通過 判斷當前構建的是哪個頁面

<code>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          }/<code>

通過對這幾種類型的頁面的縮放和平移達到我們想要的效果。

完整代碼:

<code>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&class="lazy" data-original=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),          ),        ),      ),    );  }}/<viewpage>/<statefulwidget>/<code>


推薦幾款Github上帶動畫效果的PageView

  • https://github.com/gskinnerTeam/flutter_vignettes/tree/master/vignettes/parallax_travel_cards_list
  • https://github.com/gskinnerTeam/flutter_vignettes/tree/master/vignettes/gooey_edge
  • https://github.com/roughike/page-transformer
  • https://github.com/best-flutter/transformer_page_view
  • https://github.com/Milad-Akarie/smooth_page_indicator


分享到:


相關文章: