flutter開發(fā)技巧自定頁面指示器PageIndicator詳解
一、來源
項目中遇到多個需要自定義輪播圖指示器的需求,封裝成基礎(chǔ)組件方便復用;
原理是通過 ValueListenableBuilder 實時監(jiān)聽輪播圖的當前索引,然后更新指示器組件,達到最終效果;
二、效果
三、源碼實現(xiàn)
1、flutter_swiper_null_safety 使用示例:
import 'package:flutter/material.dart'; import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; import 'package:flutter_templet_project/R.dart'; import 'package:flutter_templet_project/basicWidget/page_indicator_widget.dart'; import 'package:flutter_templet_project/extension/buildContext_extension.dart'; class FlutterSwiperIndicatorDemo extends StatefulWidget { FlutterSwiperIndicatorDemo({ Key? key, this.title}) : super(key: key); final String? title; @override _FlutterSwiperIndicatorDemoState createState() => _FlutterSwiperIndicatorDemoState(); } class _FlutterSwiperIndicatorDemoState extends State<FlutterSwiperIndicatorDemo> { ValueNotifier<int> currentIndex = ValueNotifier(0); BorderRadius borderRadius = BorderRadius.all(Radius.circular(8)); final items = R.imgUrls;//圖片鏈接數(shù)組 @override Widget build(BuildContext context) { dynamic arguments = ModalRoute.of(context)!.settings.arguments; return Scaffold( appBar: AppBar( title: Text(widget.title ?? "$widget"), ), body: _buildSwiper() ); } ///創(chuàng)建子項 _buildItem(context, index) { final imgUrl = items[index]; return InkWell( onTap: () => print(index), child: Container( padding: EdgeInsets.only(bottom: 0), //為了顯示陰影 child: ClipRRect( borderRadius: this.borderRadius, child: Stack( alignment: Alignment.center, fit: StackFit.expand, children: [ FadeInImage( placeholder: AssetImage('images/img_placeholder.png',), image: NetworkImage(imgUrl), fit: BoxFit.cover, ), ], ), ), ), ); } _buildSwiper() { return Container( height: 200, child: ClipRRect( borderRadius: this.borderRadius, child: Stack( children: [ Swiper( itemBuilder: (context, index) => _buildItem(context, index), // indicatorLayout: PageIndicatorLayout.COLOR, autoplay: this.items.length > 1, loop: this.items.length > 1, itemCount: this.items.length, // pagination: this.items.length <= 1 ? null : SwiperPagination(), // control: SwiperControl(), // itemWidth: 200, // viewportFraction: 0.6, onIndexChanged: (index){ currentIndex.value = index; } ), // if (this.items.length > 1) buildPageIndicator(), if (this.items.length > 1) PageIndicatorWidget( currentIndex: currentIndex, itemCount: this.items.length, itemSize: Size(context.screenSize.width/ 4 / this.items.length, 2), // itemBuilder: (isSelected, itemSize) { // return Container( // width: itemSize.width, // height: itemSize.height, // color: isSelected ? Colors.red : Colors.green, // ); // }, ) ], ) ), ); }
2、PageIndicatorWidget 指示器源碼:
import 'package:flutter/material.dart'; typedef PageIndicatorItemWidgetBuilder = Widget Function(bool isSelected, Size itemSize); /// 輪播圖指示器 class PageIndicatorWidget extends StatelessWidget { PageIndicatorWidget({ Key? key, this.margin = const EdgeInsets.only(bottom: 10), required this.currentPage, required this.itemCount, this.normalColor = const Color(0x25ffffff), this.selectedColor = Colors.white, this.itemSize = const Size(8, 2), this.itemBuilder, this.hidesForSinglePage = true }) : super(key: key); /// 當前頁面索引 ValueNotifier<int> currentPage; EdgeInsetsGeometry? margin; /// item數(shù)量 int itemCount; /// 每個item尺寸(最好用固定寬度除以個數(shù),避免總寬度溢出) Size itemSize; /// 自定義每個item PageIndicatorItemWidgetBuilder? itemBuilder; /// 默認顏色 Color? normalColor; /// 選中顏色 Color? selectedColor; /// 單頁隱藏 bool hidesForSinglePage; @override Widget build(BuildContext context) { if (this.hidesForSinglePage && this.itemCount == 1) { return SizedBox(); } return buildPageIndicator(); } Widget buildPageIndicator() { return Container( margin: this.margin, child: Align( alignment: Alignment.bottomCenter, child: ValueListenableBuilder( valueListenable: this.currentPage, builder: (BuildContext context, dynamic value, Widget? child) { return Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: buildPageIndicatorItem( currentIndex: this.currentPage.value, normalColor: this.normalColor, selectedColor: this.selectedColor, ), ); }, ), ), ); } List<Widget> buildPageIndicatorItem({ currentIndex: 0, normalColor: const Color(0x25ffffff), selectedColor: Colors.white, }) { List<Widget> list = List.generate(this.itemCount, (index) { return this.itemBuilder != null ? this.itemBuilder!(currentIndex == index, this.itemSize) : ClipRRect( borderRadius: BorderRadius.all(Radius.circular(1)), child: Container( width: this.itemSize.width, height: this.itemSize.height, color: currentIndex == index ? selectedColor : normalColor, ), ); }); return list; } }
三、總結(jié)
此組件封裝也秉承最簡實現(xiàn),不超過百行代碼。開發(fā)工作中,如果我們發(fā)現(xiàn)最佳實踐,即使它很小,也需要記錄下來,常年累月下來機會自然而然擁有一套屬于自己的高效的組件庫??梢蕴岣呶覀兊拈_發(fā)效率,節(jié)約時間,讓工作變得輕松。
以上就是flutter開發(fā)技巧自定頁面指示器PageIndicator詳解的詳細內(nèi)容,更多關(guān)于flutter PageIndicator的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
超簡單實現(xiàn)Android自定義Toast示例(附源碼)
本篇文章主要介紹了超簡單實現(xiàn)Android自定義Toast示例(附源碼),具有一定的參考價值,有興趣的可以了解一下。2017-02-02Android中使用GridView進行應(yīng)用程序UI布局的教程
GridView即平常我們見到的類似九宮格的矩陣型布局,只不過默認不帶分割線,這里我們就從基礎(chǔ)開始來看一下Android中使用GridView進行應(yīng)用程序UI布局的教程2016-06-06解決VSCode調(diào)試react-native android項目錯誤問題
這篇文章主要介紹了VSCode調(diào)試react-native android項目錯誤解決辦法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12Android帶進度條的下載圖片示例(AsyncTask異步任務(wù))
本文主要介紹Android帶進度條的下載圖片示例(AsyncTask異步任務(wù))的方法解析。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04