该考虑Provider
了
官方说好用,那么用就是了~
使用provider
是为了更好的进行状态管理,为什么要进行状态管理?当然你怕你组件多了自己瞎搞,乱成一锅粥~
没图啊,效果图再贴一遍吧,狗带
看似理性的分析
- 要时间滚动日记卡片的时候,下面的导航按钮随着改变颜色,导航栏就必须知道,当前滚到第几页(
double
)了,才能做出相应的改变 - 导航栏和日记卡片使用相同的色盘:
/// 色盘: 写两个意思一下,十二个月,应该有十二个。。 static const List<List<Color>> linerColor = [ [ Color.fromARGB(255, 87, 211, 255), Color.fromARGB(255, 86, 173, 254), ], [ Color.fromARGB(255, 86, 173, 254), Color.fromARGB(255, 82, 118, 254), ], ];
- 日记卡片在滚动的时候尽量减少其他组件的
build
- 底部导航要写渐变动画吗?不用,卡片的滚动会通知到导航栏,使其重新
build
,只要我build
的足够快,你的眼睛就跟不上我。。。动画也不是这么搞的嘛,笑摸我狗头~
provider
走起来
有了上面的分析,我们的provider
只要通知page
就够了,
/// 哇靠,怎么和官方的写法有差别!!
class HomeState with ChangeNotifier {
HomeState(this._ctrl) : assert(_ctrl != null) {
_ctrl.addListener(() {
_curPage = _ctrl.page.floor();
notifyListeners();
});
}
final PageController _ctrl;
int get curPage => _curPage;
double get value => _ctrl?.page ?? 0;
int _curPage = 0;
void setPage(int index) {
_curPage = index;
notifyListeners();
}
void buildChild() {
notifyListeners();
}
}
哇靠,怎么和官方的写法有差别!!其实没什么差别,只是我们有一个addListener
的操作,而我们的PageController
不是由HomeState
来管理的。(其实是可以放在HomeState
管理,当时有一个什么顾虑,我现在想不起来了,可怕。。继续)
这里我可以看到,只要滚动卡片就会buildChild
。
拆分组件
刚开始的时候,我们的页面都堆在一个页面里,看起来及其凶残,现在我们来拆分一下,
_FloatBtnWidget
悬浮的添加按钮
class _FloatBtnWidget extends StatelessWidget {
_FloatBtnWidget(this._homeProvider);
final HomeState _homeProvider;
@override
Widget build(BuildContext context) {
double cil = _homeProvider.value - _homeProvider.value.floor();
double lerp = cil == 0 ? 1 : cil;
return Container(
width: 56,
height: 56,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color.lerp(
StaticStyle.linerColor[_homeProvider.curPage][0],
StaticStyle.linerColor[_homeProvider.value.ceil()][0],
lerp,
),
Color.lerp(
StaticStyle.linerColor[_homeProvider.curPage][1],
StaticStyle.linerColor[_homeProvider.value.ceil()][1],
lerp,
),
],
),
boxShadow: [
BoxShadow(
color: Color.fromARGB(100, 87, 211, 255),
blurRadius: 8,
)
],
),
child: Icon(Icons.add, color: Colors.white),
);
}
}
_BottomNavWidget
底部导航栏
class _BottomNavWidget extends StatelessWidget {
_BottomNavWidget(this._homeProvider, this.tabState);
final TabState tabState;
final HomeState _homeProvider;
@override
Widget build(BuildContext context) {
double cil = _homeProvider.value - _homeProvider.value.floor();
double lerp = cil == 0 ? 1 : cil;
final Gradient gradient = LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.black54,
Colors.black,
],
);
final Gradient selectedGradient = LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color.lerp(
StaticStyle.linerColor[_homeProvider.curPage][0],
StaticStyle.linerColor[_homeProvider.value.ceil()][0],
lerp,
),
Color.lerp(
StaticStyle.linerColor[_homeProvider.curPage][1],
StaticStyle.linerColor[_homeProvider.value.ceil()][1],
lerp,
),
],
);
return DecoratedBox(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Color.fromARGB(100, 200, 200, 200),
blurRadius: 8,
)
]),
child: ClipRRect(
borderRadius: BorderRadius.only(topRight: Radius.circular(20), topLeft: Radius.circular(20)),
child: BottomAppBar(
elevation: 0,
notchMargin: 6,
shape: CircularNotchedRectangle(),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
GradientIconBtn(
Icons.note_add,
key: ValueKey('page-index-0'),
iconSize: 26,
gradient: gradient,
selectedGradient: selectedGradient,
selected: tabState.tabIndex == 0,
onPress: () {
tabState.setTab(0);
},
),
Text(''),
GradientIconBtn(
Icons.person,
key: ValueKey('page-index-1'),
iconSize: 26,
gradient: gradient,
selectedGradient: selectedGradient,
selected: tabState.tabIndex == 1,
onPress: () {
tabState.setTab(1);
},
),
],
),
),
),
);
}
}
导航栏的菜单也进行的封装GradientIconBtn
:
class GradientIconBtn extends StatelessWidget {
GradientIconBtn(
this.icon, {
Key key,
@required this.onPress,
this.iconSize,
this.gradient,
this.selectedGradient,
this.selected = false,
}) : super(key: key);
final VoidCallback onPress;
final IconData icon;
final double iconSize;
final Gradient gradient;
final Gradient selectedGradient;
final bool selected;
@override
Widget build(BuildContext context) {
return IconButton(
onPressed: onPress,
icon: gradient == null
? Icon(icon, size: iconSize)
: GradientText(
iconData: icon,
iconSize: iconSize,
gradient: selected ? selectedGradient : gradient,
),
);
}
}
最终组装ChangeNotifierProvider
:
ChangeNotifierProvider<HomeState>(
builder: (_) => HomeState(_pageController),
child: Consumer<HomeState>(
child: IndexedStack(
index: widget.tabState.tabIndex,
children: <Widget>[
NoteYearViewPage(_pageController),
MinePage(),
],
),
builder: (_, HomeState homeProvider, Widget child) => Scaffold(
body: child,
floatingActionButton: _FloatBtnWidget(homeProvider),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: _BottomNavWidget(homeProvider, widget.tabState),
),
),
)
NoteYearViewPage
是卡片页面,比较简单,写死的渐变。MinePage
是我的页面,空荡荡。。
可以看到,我们使用了ChangeNotifierProvider
,当收到buildChild
事件后,就会build
一个Scaffold
而我们的child
则会原封不动的放进Scaffold
中,避免了重新build
。
搞定收工
好像完成了,呵呵呵~贴代码真是爽,整个过程只要28分钟。。
中间有一些小细节可能没有说明,想了解的小伙伴可以查看源码: gayhub