首页 ui是这样
技术点分析
可以看到首页需要这种可以滚动分页切换的 采用
tabbar+TabBarView
问题:但是tabbar
下面的indicator
(指示器)默认不是圆角的 而且不能自定义 宽度 系统默认只提供了2种
//根据文字宽度
TabBarIndicatorSize.label
//根据tab的宽度
TabBarIndicatorSize.tab
1. flutter 自定义 indicator(指示器)
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
// 默认高度从46改为40
const double _kTabHeight = 40.0;
const double _kTextAndIconTabHeight = 72.0;
class RoundUnderlineTabIndicator extends Decoration {
/// Create an underline style selected tab indicator.
///
/// The [borderSide] and [insets] arguments must not be null.
const RoundUnderlineTabIndicator({
this.borderSide = const BorderSide(width: 2.0, color: Colors.white),
this.insets = EdgeInsets.zero,
}) : assert(borderSide != null),
assert(insets != null);
/// The color and weight of the horizontal line drawn below the selected tab.
final BorderSide borderSide;
/// Locates the selected tab's underline relative to the tab's boundary.
///
/// The [TabBar.indicatorSize] property can be used to define the
/// tab indicator's bounds in terms of its (centered) tab widget with
/// [TabIndicatorSize.label], or the entire tab with [TabIndicatorSize.tab].
final EdgeInsetsGeometry insets;
@override
Decoration lerpFrom(Decoration a, double t) {
if (a is UnderlineTabIndicator) {
return UnderlineTabIndicator(
borderSide: BorderSide.lerp(a.borderSide, borderSide, t),
insets: EdgeInsetsGeometry.lerp(a.insets, insets, t),
);
}
return super.lerpFrom(a, t);
}
@override
Decoration lerpTo(Decoration b, double t) {
if (b is UnderlineTabIndicator) {
return UnderlineTabIndicator(
borderSide: BorderSide.lerp(borderSide, b.borderSide, t),
insets: EdgeInsetsGeometry.lerp(insets, b.insets, t),
);
}
return super.lerpTo(b, t);
}
@override
_UnderlinePainter createBoxPainter([ VoidCallback onChanged ]) {
return _UnderlinePainter(this, onChanged);
}
}
class _UnderlinePainter extends BoxPainter {
_UnderlinePainter(this.decoration, VoidCallback onChanged)
: assert(decoration != null),
super(onChanged);
final RoundUnderlineTabIndicator decoration;
BorderSide get borderSide => decoration.borderSide;
EdgeInsetsGeometry get insets => decoration.insets;
Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
assert(rect != null);
assert(textDirection != null);
final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
// return Rect.fromLTWH(
// indicator.left,
// indicator.bottom - borderSide.width,
// indicator.width,
// borderSide.width,
// );
//希望的宽度
double wantWidth = 25;
//取中间坐标
double cw = (indicator.left + indicator.right) / 2;
return Rect.fromLTWH(cw - wantWidth / 2,
indicator.bottom - borderSide.width, wantWidth, borderSide.width);
}
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
assert(configuration != null);
assert(configuration.size != null);
final Rect rect = offset & configuration.size;
final TextDirection textDirection = configuration.textDirection;
final Rect indicator = _indicatorRectFor(rect, textDirection).deflate(borderSide.width / 2.0);
// final Paint paint = borderSide.toPaint()..strokeCap = StrokeCap.square;
// 改为圆角
final Paint paint = borderSide.toPaint()..strokeCap = StrokeCap.round;
canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, paint);
}
}
Copy一份系统的 从新命名一个 RoundUnderlineTabIndicator 类 修改 修改参数即可
使用方式
TabBar(
..............
indicator: RoundUnderlineTabIndicator(
borderSide: BorderSide(
width: 3.5,
color: ColorsUtils.main_color,
)),
......
)
问题2
上面区域有阴影,可以自己写,我这是用的ui给的一张背景图,这时候就涉及到 图片拉伸的问题
需要指定区域拉伸
, 就和iOS原生的Show Slicing
,android 的.9处理 是一样的效果,当然你可以选择用card组件去实现
使用image的centerSlice 拉伸指定区域 这里需要 Rect.fromLTRB 或者 Rect.fromLTWH,这里需要注意的是
1.组件不能比你图片大
2.fromLTRB 和 fromLTWH 指定位置的问题
return Container(
width: 300,
height: 195,
decoration: BoxDecoration(
image: DecorationImage(
centerSlice: Rect.fromLTRB(60, 60, 71, 71),
image: AssetImage("assets/images/beijing.png"),
fit: BoxFit.fill),
color: Colors.red),
child: Text("哈哈"));
上图 Rect.fromLTRB(60, 60, 71, 71) 我以为 会是这样的
但实际上却不是这样的 实际上是这样的
为啥会是这样呢Rect.fromLTRB ltrb都是距离左上角的距离
fromLTWH 这个就更好理解一点了
问题3
这里的搭配
pageView+Indicator
可以看出指示器需要自定义 而且带动画的
flutter 自定义指示器(Indicator)
/// An indicator showing the currently selected page of a PageController
class CSTIndicator extends AnimatedWidget {
CSTIndicator({
this.controller,
this.itemCount,
this.onPageSelected,
this.color:ColorsUtils.yelllow_color ,
}) : super(listenable: controller);
/// The PageController that this DotsIndicator is representing.
final PageController controller;
/// The number of items managed by the PageController
final int itemCount;
/// Called when a dot is tapped
final ValueChanged onPageSelected;
/// The color of the dots.
///
/// Defaults to `Colors.white`.
final Color color;
// The base size of the dots
static const double _kDotSize = 8.0;
// The increase in the size of the selected dot
static const double _kMaxZoom = 2.5;
// The distance between the center of each dot
static const double _kDotSpacing = 25.0;
Widget _buildDot(int index) {
double selectedness = Curves.easeOut.transform(
max(
0.0,
1.0 - ((controller.page ?? controller.initialPage) - index).abs(),
),
);
double zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness;
return new Container(
width: _kDotSpacing,
child: new Center(
child: new Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_kDotSize/2),
color: color,
),
width: _kDotSize * zoom,
height: _kDotSize ,
child: new InkWell(
onTap: () => onPageSelected(index),
),
),
),
);
}
Widget build(BuildContext context) {
return new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: new List.generate(itemCount, _buildDot),
);
}
}
使用方式
CSTIndicator(
controller: pageCtr, //注意这个controller是 PageController
itemCount: 3,
/*
如需要点击 指示器切换pageView 需要实现此事件,我上面用不上,所以不实现
onPageSelected: (int page) {
pageCtr.animateToPage(
page,
duration:time,
curve:curve,
);
*/
)
以上理解如有误请大佬能够指出,3q
ok,以梦为马,不负韶华