• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • Flutter Widgets 之 PageView

    標簽: Flutter  Android  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

    更多相關閱讀:

    如果這篇文章有幫助到您,希望您來個“贊”并關注我的公眾號,非常謝謝。

    版權聲明:本文為mengks1987原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/mengks1987/article/details/104570500

    智能推薦

    Flutter 基礎Widgets Text()之TextStyle詳解

    Text概述 即一個單一樣式的文本 Text Widget就是顯示單一樣式的文本字符串。字符串可能會跨越多行,也可能全部顯示在同一行上,具體取決于布局約束。 style參數可選。如果省略了,文本將使用最近的DefaultTextStyle的樣式。如果給定樣式的TextStyle.inherit屬性為true(默認值),則給定樣式將與最近的DefaultTextStyle合并。例如,比如可以在使用默...

    Flutter 基礎布局Widgets之Stack詳解

    概述 Stack 組件是一種層疊式布局,即組件覆蓋另一個組件,覆蓋的順序取決于在children中放置的順序,使用場景比如在圖片上加上一些文字描述,即將文本Widget覆蓋在圖片組件,詳見下面的小例。 構造函數 alignment 子Widgets們的對齊方式 textDirection 文本的方向,默認當然是 left-to-right fit 子Widgets的放置方式,默認loose 子Wi...

    Flutter 基礎布局Widgets之Row詳解

    概述 Row即創建一個水平的組件列表,是非常常用的組件,而Column即創建一個垂直的組件列表,用法和Row一模一樣,因為Row以及Column都是Flex的子類,它們的具體實現也都是由Flex完成,只是參數不同。 構造函數 mainAxisAlignment 在flex布局中,子元素應該如何沿著主軸放置,即各個組件的放置的方式, 比如MainAxisAlignment.start 組件都盡量靠近...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    猜你喜歡

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    精品国产乱码久久久久久蜜桃不卡