flutter開發(fā)技巧自定頁面指示器PageIndicator詳解
一、來源
項目中遇到多個需要自定義輪播圖指示器的需求,封裝成基礎(chǔ)組件方便復(fù)用;
原理是通過 ValueListenableBuilder 實時監(jiān)聽輪播圖的當(dāng)前索引,然后更新指示器組件,達到最終效果;
二、效果

三、源碼實現(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);
/// 當(dāng)前頁面索引
ValueNotifier<int> currentPage;
EdgeInsetsGeometry? margin;
/// item數(shù)量
int itemCount;
/// 每個item尺寸(最好用固定寬度除以個數(shù),避免總寬度溢出)
Size itemSize;
/// 自定義每個item
PageIndicatorItemWidgetBuilder? itemBuilder;
/// 默認(rèn)顏色
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-02
Android中使用GridView進行應(yīng)用程序UI布局的教程
GridView即平常我們見到的類似九宮格的矩陣型布局,只不過默認(rèn)不帶分割線,這里我們就從基礎(chǔ)開始來看一下Android中使用GridView進行應(yīng)用程序UI布局的教程2016-06-06
解決VSCode調(diào)試react-native android項目錯誤問題
這篇文章主要介紹了VSCode調(diào)試react-native android項目錯誤解決辦法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12
Android帶進度條的下載圖片示例(AsyncTask異步任務(wù))
本文主要介紹Android帶進度條的下載圖片示例(AsyncTask異步任務(wù))的方法解析。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04

