用 Flutter 水一个可丑的渐变首页(二)

1,664 阅读3分钟

该考虑Provider

官方说好用,那么用就是了~

使用provider是为了更好的进行状态管理,为什么要进行状态管理?当然你怕你组件多了自己瞎搞,乱成一锅粥~

没图啊,效果图再贴一遍吧,狗带

效果图

看似理性的分析

  1. 要时间滚动日记卡片的时候,下面的导航按钮随着改变颜色,导航栏就必须知道,当前滚到第几页(double)了,才能做出相应的改变
  2. 导航栏和日记卡片使用相同的色盘:
    /// 色盘: 写两个意思一下,十二个月,应该有十二个。。
     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),
         ],
     ];
    
  3. 日记卡片在滚动的时候尽量减少其他组件的build
  4. 底部导航要写渐变动画吗?不用,卡片的滚动会通知到导航栏,使其重新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