該考慮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