Flutter学习-13
2025-11-09 06:16:32
Flutter组件封装
3279
一. StarRating
1.1功能
rating:必传参数,告诉Widget当前的评分。
maxRating:可选参数,最高评分,根据它来计算一个比例,默认值为10;
size:星星的大小,决定每一个star的大小;
unselectedColor:未选中星星的颜色(该属性是使用默认的star才有效);
selectedColor:选中星星的颜色(该属性也是使用默认的star才有效);
unselectedImage:定制未选中的star;
selectedImage:定义选中时的star;
count:展示星星的个数;
1.2. 实现思路分析
利用重叠功能
child: Stack( children: <Widget>[ Row(children: getUnSelectImage(), mainAxisSize: MainAxisSize.min,), Row(children: getSelectImage(), mainAxisSize: MainAxisSize.min,), ], ),
使用ClipRect定制CustomClipper进行裁剪
class MyRectClipper extends CustomClipper<Rect>{
finaldouble width;
MyRectClipper({
this.width
});
@override
Rect getClip(Size size) {
return Rect.fromLTRB(0, 0, width, size.height);
}
@override
bool shouldReclip(MyRectClipper oldClipper) {
return width != oldClipper.width;
}
}
使用MyRectClipper进行裁剪:
Widget leftStar = ClipRect( clipper: MyRectClipper(width: leftRatio * widget.size), child: widget.selectedImage, );
代码:
import 'package:flutter/material.dart';
class MKStarRating extends StatefulWidget {
final double rating;
final double maxRating;
final int count;
final double size;
final Color unselectedColor;
final Color selectedColor;
final Widget unselectedImage;
final Widget selectedImage;
MKStarRating({
@required this.rating,
this.maxRating = 10,
this.count = 5,
this.size = 30,
this.unselectedColor = const Color(0xffbbbbbb),
this.selectedColor = const Color(0xffff0000),
Widget unselectedImage,
Widget selectedImage,
}):unselectedImage=unselectedImage??Icon(Icons.star_border,
color: unselectedColor, size: size),
selectedImage=selectedImage?? Icon(Icons.star, color: selectedColor, size: size) ;
@override
_MKStarRatingState createState() => _MKStarRatingState();
}
class _MKStarRatingState extends State<MKStarRating> {
@override
Widget build(BuildContext context) {
return Stack(children: [
Row(mainAxisSize: MainAxisSize.min, children: buildUnselectedStar()),
Row(mainAxisSize: MainAxisSize.min, children: buildSelectedStar()),
]);
}
List<Widget> buildUnselectedStar() {
return List.generate(widget.count, (index) {
return widget.unselectedImage;
});
}
List<Widget> buildSelectedStar() {
List<Widget> starts = [];
final star = widget.selectedImage;
double oneValue = widget.maxRating / widget.count;
int entireCount = (widget.rating / oneValue).floor();
for (var i = 0; i < entireCount; i++) {
starts.add(star);
}
double leftWidth= ((widget.rating/oneValue)-entireCount)*widget.size;
print(leftWidth);
final halfStar=ClipRect(
clipper:MKStarClipper(leftWidth) ,
child: star,);
starts.add(halfStar);
return starts;
}
}
class MKStarClipper extends CustomClipper<Rect>{
double width;
MKStarClipper(this.width);
@override
Rect getClip(Size size) {
return Rect.fromLTRB(0, 0, this.width, size.height);
}
@override
bool shouldReclip(CustomClipper<Rect> oldClipper){
return false;
}
}
主要难点是初始列表传值
二. DashedLine
2.1分割虚线功能
axis:确定虚线的方向;
dashedWidth:根据虚线的方向确定自己虚线的宽度;
dashedHeight:根据虚线的方向确定自己虚线的高度;
count:内部会根据设置的个数和宽高确定密度(虚线的空白间隔);
color:虚线的颜色,不多做解释;
2.2. 代码
import 'package:flutter/material.dart';
class MKDashedLine extends StatelessWidget{
final Axis axis;
final double dashedWidth;
final double dashedHeight;
final int count;
final Color color;
MKDashedLine({
this.axis=Axis.horizontal,
this.dashedWidth=1,
this.dashedHeight=1,
this.count=10,
this.color=Colors.red,
});
@override
Widget build(BuildContext context){
return Flex(direction: axis,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:List.generate(count, (_){
return SizedBox(
width:dashedWidth,
height:dashedHeight,
child:DecoratedBox(
decoration: BoxDecoration(color:color),
)
);
})
);
}
}
三. 底部TabBar
3.1. TabBar属性
currentIndex:当前选中哪一个item;
selectedFontSize:选中时的文本大小;
unselectedFontSize:未选中时的文本大小;
type:当item的数量超过2个时,需要设置为fixed;
items:放入多个BottomNavigationBarItem类型;
onTap:监听哪一个item被选中;
class BottomNavigationBar extends StatefulWidget {
BottomNavigationBar({
Key key,
@requiredthis.items,
this.onTap,
this.currentIndex = 0,
this.elevation = 8.0,
BottomNavigationBarType type,
Color fixedColor,
this.backgroundColor,
this.iconSize = 24.0,
Color selectedItemColor,
this.unselectedItemColor,
this.selectedIconTheme = const IconThemeData(),
this.unselectedIconTheme = const IconThemeData(),
this.selectedFontSize = 14.0,
this.unselectedFontSize = 12.0,
this.selectedLabelStyle,
this.unselectedLabelStyle,
this.showSelectedLabels = true,
bool showUnselectedLabels,
})
}
使用IndexedStack来管理多个页面的切换:
body: IndexedStack( index: _currentIndex, children: <Widget>[ Home(), Subject(), Group(), Mall(), Profile() ],
3.2. TabBar代码实现
import 'package:flutter/material.dart';
import '../home/home.dart';
import './widget.dart';
class MKMainPage extends StatefulWidget {
@override
_MKMainPageState createState() => _MKMainPageState();
}
class _MKMainPageState extends State<MKMainPage> {
int _currentIndex=0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: <Widget>[
MKHomePage(),
MKHomePage(),
MKHomePage(),
MKHomePage(),
MKHomePage(),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
items: [
MKBottomBarItem('home','首页'),
MKBottomBarItem('subject','书阴影'),
MKBottomBarItem('group','小组'),
MKBottomBarItem('mall','事集'),
MKBottomBarItem('profile','我的'),
],
onTap: (index){
print(index);
setState(() {
_currentIndex=index;
});
},
),
);
}
}
